Move garden-o-matic progress feedback from non-modal dialogs to a status console...
authorojan@chromium.org <ojan@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Mar 2012 00:36:24 +0000 (00:36 +0000)
committerojan@chromium.org <ojan@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Mar 2012 00:36:24 +0000 (00:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81983

Reviewed by Adam Barth.

The dialogs get in the way if you are doing multiple rebaselines. Also,
a number of people didn't realize that you could do multiple rebaselines
in parallel. A non-modal dialog is just confusing.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/base.js:
* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/controllers.js:
If we're only rebaselining 1 test, show the test name in the initial message. Otherwise,
show the number of tests being rebaselined.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/ui.js:
Turn MessageBox into StatusArea. StatusArea is a singleton and messages in the StatusArea are grouped
by ID. That way, the results of each UI action (e.g. clicking rebaseline) are grouped together
into a single area.

Once addFinalMessage has been called for all IDs, we show the close button. Clicking close
also serves to clear all teh content in the StatusArea.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/ui_unittests.js:
* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/styles/results.css:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111793 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/base.js
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/controllers.js
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/ui.js
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/ui_unittests.js
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/styles/onebar.css
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/styles/results.css
Tools/ChangeLog

index eae0315a5d9feaaa9e824b5357547aef6dfea5b4..ac28741213b19039f99f4e4cb5c5472a4fad2696 100644 (file)
@@ -115,6 +115,7 @@ base.flattenArray = function(arrayOfArrays)
 
 base.values = function(dictionary)
 {
+    // FIXME: Replace this with Object.values(dictionary)?
     var result = [];
 
     for (var key in dictionary) {
@@ -382,7 +383,9 @@ base.extends = function(base, prototype)
         var element = typeof base == 'string' ? document.createElement(base) : base.call(this);
         extended.prototype.__proto__ = element.__proto__;
         element.__proto__ = extended.prototype;
-        element.init && element.init.apply(element, arguments);
+        var singleton = element.init && element.init.apply(element, arguments);
+        if (singleton)
+            return singleton;
         return element;
     }
 
index 4ae5e1f6cab5192240ea99b4136dbb79e39b3444..bc497dadc8bf90ffa527dfd3a417932f98dbf7ca 100644 (file)
@@ -32,26 +32,36 @@ var kCheckoutUnavailableMessage = 'Failed! Garden-o-matic needs a local server t
 // FIXME: Where should this function go?
 function rebaselineWithStatusUpdates(failureInfoList)
 {
-    var statusView = new ui.MessageBox('Rebaseline', 'Performing rebaseline...');
+    var statusView = new ui.StatusArea('Rebaseline');
+    var id = statusView.newId();
+
+    var testNames = base.uniquifyArray(failureInfoList.map(function(failureInfo) { return failureInfo.testName; }));
+    var testName = testNames.length == 1 ? testNames[0] : testNames.length + ' tests';
+    statusView.addMessage(id, 'Performing rebaseline of ' + testName + '...');
 
     checkout.rebaseline(failureInfoList, function() {
-        statusView.addFinalMessage('Rebaseline done! Please land with "webkit-patch land-cowboy".');
+        statusView.addFinalMessage(id, 'Rebaseline done! Please land with "webkit-patch land-cowboy".');
     }, function(failureInfo) {
-        statusView.addMessage(failureInfo.testName + ' on ' + ui.displayNameForBuilder(failureInfo.builderName));
+        statusView.addMessage(id, failureInfo.testName + ' on ' + ui.displayNameForBuilder(failureInfo.builderName));
     }, function() {
-        statusView.addFinalMessage(kCheckoutUnavailableMessage);
+        statusView.addFinalMessage(id, kCheckoutUnavailableMessage);
     });
 }
 
 // FIXME: Where should this function go?
 function updateExpectationsWithStatusUpdates(failureInfoList)
 {
-    var statusView = new ui.MessageBox('Expectations Update', 'Updating expectations...');
+    var statusView = new ui.StatusArea('Expectations Update');
+    var id = statusView.newId();
+
+    var testNames = base.uniquifyArray(failureInfoList.map(function(failureInfo) { return failureInfo.testName; }));
+    var testName = testNames.length == 1 ? testNames[0] : testNames.length + ' tests';
+    statusView.addMessage(id, 'Updating expectations of ' + testName + '...');
 
     checkout.updateExpectations(failureInfoList, function() {
-        statusView.addFinalMessage('Expectations update done! Please land with "webkit-patch land-cowboy".');
+        statusView.addFinalMessage(id, 'Expectations update done! Please land with "webkit-patch land-cowboy".');
     }, function() {
-        statusView.addFinalMessage(kCheckoutUnavailableMessage);
+        statusView.addFinalMessage(id, kCheckoutUnavailableMessage);
     });
 }
 
index 77b6bbf50aeffc05fd0993d039f56866dc5ac432..bc63d1a06052b4e1ad09a34a7acefb3f8d9242a9 100644 (file)
@@ -143,38 +143,66 @@ ui.RelativeTime = base.extends('time', {
     }
 });
 
-ui.MessageBox = base.extends('div',  {
-    init: function(title, message)
+ui.StatusArea = base.extends('div',  {
+    init: function()
     {
-        this._content = document.createElement('div');
-        this.appendChild(this._content);
-        this.addMessage(message);
+        // This is a Singleton.
+        if (ui.StatusArea._instance)
+            return ui.StatusArea._instance;
+        ui.StatusArea._instance = this;
+
+        this.className = 'status';
         document.body.appendChild(this);
-        $(this).dialog({
-            resizable: false,
-            width: $(window).width() * 0.80,  // FIXME: We should have CSS do this work for us.
-        });
-        $('.ui-dialog-title', this.parentNode).text(title);
-        $(this).bind('dialogclose', function() {
-            $(this).detach();
-        }.bind(this));
+        this._currentId = 0;
+        this._unfinishedIds = {};
+
+        this.appendChild(new ui.actions.List([new ui.actions.Close()]));
+        $(this).bind('close', this.close.bind(this));
+
+        var processing = document.createElement('div');
+        processing.className = 'process-text';
+        processing.textContent = 'Processing...';
+        this.appendChild(processing);
     },
     close: function()
     {
-        $(this).dialog('close');
+        this.style.visibility = 'hidden';
+        Array.prototype.forEach.call(this.querySelectorAll('.status-content'), function(node) {
+            node.parentNode.removeChild(node);
+        });
     },
-    addMessage: function(message)
+    addMessage: function(id, message)
     {
+        this.style.visibility = 'visible';
+        $(this).addClass('processing');
+
         var element = document.createElement('div');
         $(element).addClass('message').text(message);
-        this._content.appendChild(element);
+
+        var content = this.querySelector('#' + id);
+        if (!content) {
+            content = document.createElement('div');
+            content.id = id;
+            content.className = 'status-content';
+            this.appendChild(content);
+        }
+
+        if (element.offsetTop < this.scrollTop || element.offsetTop + element.offsetHeight > this.scrollTop + this.offsetHeight)
+            this.scrollTop = element.offsetTop;
     },
     // FIXME: It's unclear whether this code could live here or in a controller.
-    addFinalMessage: function(message)
+    addFinalMessage: function(id, message)
     {
-        this.addMessage(message);
-        this.appendChild(new ui.actions.List([new ui.actions.Close()]));
-        $(this).bind('close', this.close.bind(this));
+        this.addMessage(id, message);
+
+        delete this._unfinishedIds[id];
+        if (!Object.keys(this._unfinishedIds).length)
+            $(this).removeClass('processing');
+    },
+    newId: function() {
+        var id = 'status-content-' + ++this._currentId;
+        this._unfinishedIds[id] = 1;
+        return id;
     }
 });
 
index c77610924b38342a1707fc33e5a97c1fa22c241b..0758f657f68cf37123a7332ff66b441435c87ea4 100644 (file)
@@ -119,17 +119,57 @@ test("time", 6, function() {
     equal(time.date().getTime(), tenMinutesAgo.getTime());
 });
 
-test("MessageBox", 1, function() {
-    var messageBox = new ui.MessageBox('The Title', 'First message');
-    messageBox.addMessage('Second Message');
-    equal(messageBox.outerHTML,
-        '<div class="ui-dialog-content ui-widget-content" style="width: auto; min-height: 130px; height: auto; " scrolltop="0" scrollleft="0">' +
-            '<div>' +
-                '<div class="message">First message</div>' +
+test("StatusArea", 3, function() {
+    var statusArea = new ui.StatusArea();
+    var id = statusArea.newId();
+    statusArea.addMessage(id, 'First Message');
+    statusArea.addMessage(id, 'Second Message');
+    equal(statusArea.outerHTML,
+        '<div class="status processing" style="visibility: visible; ">' +
+            '<ul class="actions"><li><button class="action">Close</button></li></ul>' +
+            '<div class="process-text">Processing...</div>' +
+            '<div id="status-content-1" class="status-content">' +
+                '<div class="message">First Message</div>' +
                 '<div class="message">Second Message</div>' +
             '</div>' +
         '</div>');
-    messageBox.close();
+
+    var secondStatusArea = new ui.StatusArea();
+    var secondId = secondStatusArea.newId();
+    secondStatusArea.addMessage(secondId, 'First Message second id');
+
+    equal(statusArea.outerHTML,
+        '<div class="status processing" style="visibility: visible; ">' +
+            '<ul class="actions"><li><button class="action">Close</button></li></ul>' +
+            '<div class="process-text">Processing...</div>' +
+            '<div id="status-content-1" class="status-content">' +
+                '<div class="message">First Message</div>' +
+                '<div class="message">Second Message</div>' +
+            '</div>' +
+            '<div id="status-content-2" class="status-content">' +
+                '<div class="message">First Message second id</div>' +
+            '</div>' +
+        '</div>');
+
+    statusArea.addFinalMessage(id, 'Final Message 1');
+    statusArea.addFinalMessage(secondId, 'Final Message 2');
+
+    equal(statusArea.outerHTML,
+        '<div class="status" style="visibility: visible; ">' +
+            '<ul class="actions"><li><button class="action">Close</button></li></ul>' +
+            '<div class="process-text">Processing...</div>' +
+            '<div id="status-content-1" class="status-content">' +
+                '<div class="message">First Message</div>' +
+                '<div class="message">Second Message</div>' +
+                '<div class="message">Final Message 1</div>' +
+            '</div>' +
+            '<div id="status-content-2" class="status-content">' +
+                '<div class="message">First Message second id</div>' +
+                '<div class="message">Final Message 2</div>' +
+            '</div>' +
+        '</div>');
+
+    statusArea.close();
 });
 
 })();
index 119c171c4a2f0d8d7be0dad729589ce04af6b7da..2b606eed188df56b468351c8918bc758911eee5c 100644 (file)
@@ -31,6 +31,7 @@
 #onebar {
     border: none;
     padding: 0px;
+    margin-bottom: 100px;
 }
 
 #onebar .ui-widget-header {
index b0b3502b5dc9947a62a4676bfb5d8b8e8f1b3296..c6b6d30709733bab7ea1a79cfd9575a7c4b472cd 100644 (file)
 .ui-state-active .non-link-title, .ui-state-active .non-link-title:link, .ui-state-active .non-link-title:visited {
     display: none;
 }
+
+/*** status console ***/
+.status {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 100px;
+    background-color: white;
+    border-top: 1px solid gray;
+    -webkit-box-shadow: 0px -2px 10px gray;
+    overflow: auto;
+    white-space: nowrap;
+    padding: 4px;
+}
+
+.status .actions, .status .process-text {
+    position: fixed;
+    bottom: 0;
+    right: 20px;
+    margin: 4px;
+}
+
+.processing .actions {
+    display: none;
+}
+
+.processing .process-text {
+    display: block;
+}
+
+.process-text {
+    display: none;
+}
+
+.status-content {
+    border-bottom: 1px dashed;
+    margin-bottom: 1em;
+}
+
+.status-content:last-child {
+    border-bottom: 0;
+    margin-bottom: 0;
+}
index 53288bfce69e9baa628210ebb55362ab901cfca6..56d62f27bc01d1f3b107e71ebcc48dddcb7da517 100644 (file)
@@ -1,3 +1,30 @@
+2012-03-22  Ojan Vafai  <ojan@chromium.org>
+
+        Move garden-o-matic progress feedback from non-modal dialogs to a status console anchored to the bottom of the page
+        https://bugs.webkit.org/show_bug.cgi?id=81983
+
+        Reviewed by Adam Barth.
+
+        The dialogs get in the way if you are doing multiple rebaselines. Also,
+        a number of people didn't realize that you could do multiple rebaselines
+        in parallel. A non-modal dialog is just confusing.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/base.js:
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/controllers.js:
+        If we're only rebaselining 1 test, show the test name in the initial message. Otherwise,
+        show the number of tests being rebaselined.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/ui.js:
+        Turn MessageBox into StatusArea. StatusArea is a singleton and messages in the StatusArea are grouped
+        by ID. That way, the results of each UI action (e.g. clicking rebaseline) are grouped together
+        into a single area.
+
+        Once addFinalMessage has been called for all IDs, we show the close button. Clicking close
+        also serves to clear all teh content in the StatusArea. 
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/ui_unittests.js:
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/styles/results.css:
+
 2012-03-22  Tony Chang  <tony@chromium.org>
 
         Unreviewed, fix chromium build after wtf move.