build.webkit.org/dashboard: Cannot click on green tester bubbles
[WebKit-https.git] / Tools / BuildSlaveSupport / build.webkit.org-config / public_html / dashboard / Scripts / BuildbotTesterQueueView.js
1 /*
2  * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 BuildbotTesterQueueView = function(debugQueues, releaseQueues)
27 {
28     BuildbotQueueView.call(this, debugQueues, releaseQueues);
29
30     this.update();
31 };
32
33 BaseObject.addConstructorFunctions(BuildbotTesterQueueView);
34
35 BuildbotTesterQueueView.prototype = {
36     constructor: BuildbotTesterQueueView,
37     __proto__: BuildbotQueueView.prototype,
38
39     update: function()
40     {
41         BuildbotQueueView.prototype.update.call(this);
42
43         this.element.removeChildren();
44
45         function appendBuilderQueueStatus(queue)
46         {
47             if (queue.buildbot.needsAuthentication && !queue.buildbot.isAuthenticated) {
48                 this._appendUnauthorizedLineView(queue);
49                 return;
50             }
51
52             this._appendPendingRevisionCount(queue);
53
54             var appendedStatus = false;
55
56             var limit = 2;
57             for (var i = 0; i < queue.iterations.length && limit > 0; ++i) {
58                 var iteration = queue.iterations[i];
59                 if (!iteration.loaded || !iteration.finished)
60                     continue;
61
62                 --limit;
63
64                 var willHaveAnotherStatusLine = i + 1 < queue.iterations.length && limit > 0 && !iteration.successful; // This is not 100% correct, as the remaining iterations may not be finished or loaded yet, but close enough.
65                 var messageElement = this.revisionContentForIteration(iteration, (iteration.productive && willHaveAnotherStatusLine) ? iteration.previousProductiveIteration : null);
66
67                 var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
68                 var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
69                 var apiTestResults = iteration.apiTestResults || {failureCount: 0};
70                 var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
71                 var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
72                 var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
73                 var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
74
75                 if (iteration.successful) {
76                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
77                     var status = new StatusLineView(messageElement, StatusLineView.Status.Good, "all tests passed", undefined, url);
78                     limit = 0;
79                 } else if (!iteration.productive) {
80                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
81                     var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
82                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
83                     // Something wrong happened, but it was not a test failure.
84                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
85                     var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
86                 } else if (layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
87                     var url = iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration);
88                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, layoutTestResults.failureCount === 1 ? "layout test failure" : "layout test failures", layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + "\uff0b" : layoutTestResults.failureCount, url);
89                     new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
90                 } else if (!layoutTestResults.failureCount && javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
91                     var url = iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration);
92                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, javascriptTestResults.failureCount === 1 ? "javascript test failure" : "javascript test failures", javascriptTestResults.failureCount, url);
93                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
94                     var url = iteration.queue.buildbot.apiTestResultsURLForIteration(iteration);
95                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, apiTestResults.failureCount === 1 ? "api test failure" : "api test failures", apiTestResults.failureCount, url);
96                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
97                     var url = iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration);
98                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, platformAPITestResults.failureCount === 1 ? "platform api test failure" : "api test failures", platformAPITestResults.failureCount, url);
99                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && pythonTestResults.failureCount && !perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
100                     var url = iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration);
101                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, pythonTestResults.failureCount === 1 ? "webkitpy test failure" : "webkitpy test failures", pythonTestResults.failureCount, url);
102                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && perlTestResults.errorOccurred && !bindingTestResults.errorOccurred) {
103                     var url = iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration);
104                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, "webkitperl test failed", undefined, url);
105                 } else if (!layoutTestResults.failureCount && !javascriptTestResults.failureCount && !apiTestResults.failureCount && !platformAPITestResults.failureCount && !pythonTestResults.failureCount && !perlTestResults.errorOccurred && bindingTestResults.errorOccurred) {
106                     var url = iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration);
107                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, "bindings tests failed", undefined, url);
108                 } else {
109                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
110                     var totalFailures = layoutTestResults.failureCount + javascriptTestResults.failureCount + apiTestResults.failureCount + platformAPITestResults.failureCount + pythonTestResults.failureCount + perlTestResults.errorOccurred + bindingTestResults.errorOccurred;
111                     var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, totalFailures === 1 ? "test failure" : "test failures", totalFailures, url);
112                     new PopoverTracker(status.statusBubbleElement, this._presentPopoverForMultipleFailureKinds.bind(this), iteration);
113                 }
114
115                 this.element.appendChild(status.element);
116                 appendedStatus = true;
117             }
118
119             if (!appendedStatus) {
120                 var status = new StatusLineView("unknown", StatusLineView.Status.Neutral, "last passing build");
121                 this.element.appendChild(status.element);
122             }
123         }
124
125         function appendBuild(queues, label)
126         {
127             queues.forEach(function(queue) {
128                 var releaseLabel = document.createElement("a");
129                 releaseLabel.classList.add("queueLabel");
130                 releaseLabel.textContent = label;
131                 releaseLabel.href = queue.overviewURL;
132                 releaseLabel.target = "_blank";
133                 this.element.appendChild(releaseLabel);
134
135                 appendBuilderQueueStatus.call(this, queue);
136             }.bind(this));
137         }
138
139         appendBuild.call(this, this.releaseQueues, "Release");
140         appendBuild.call(this, this.debugQueues, "Debug");
141     },
142
143     _popoverContentForLayoutTestRegressions: function(iteration)
144     {
145         var hasTestHistory = typeof testHistory !== "undefined";
146
147         var content = document.createElement("div");
148         content.className = "test-results-popover";
149
150         this._addIterationHeadingToPopover(iteration, content, "layout test failures");
151         this._addDividerToPopover(content);
152
153         if (!iteration.layoutTestResults.regressions) {
154             var message = document.createElement("div");
155             message.className = "loading-failure";
156             message.textContent = "Test results couldn\u2019t be loaded";
157             content.appendChild(message);
158             return content;
159         }
160
161         function addFailureInfoLink(rowElement, className, text, url)
162         {
163             var linkElement = document.createElement("a");
164             linkElement.className = className;
165             linkElement.textContent = text;
166             linkElement.href = url;
167             linkElement.target = "_blank";
168             rowElement.appendChild(linkElement);
169         }
170
171         function addFailureInfoText(rowElement, className, text)
172         {
173             var spanElement = document.createElement("span");
174             spanElement.className = className;
175             spanElement.textContent = text;
176             rowElement.appendChild(spanElement);
177         }
178
179         var sortedRegressions = iteration.layoutTestResults.regressions.slice().sort(function(a, b) { return (a.path === b.path) ? 0 : (a.path > b.path) ? 1 : -1; });
180
181         for (var i = 0, end = sortedRegressions.length; i != end; ++i) {
182             var test = sortedRegressions[i];
183
184             var rowElement = document.createElement("div");
185
186             var testPathElement = document.createElement("span");
187             testPathElement.className = "test-path";
188             testPathElement.textContent = test.path;
189             rowElement.appendChild(testPathElement);
190
191             if (test.crash)
192                 addFailureInfoLink(rowElement, "failure-kind-indicator", "crash", iteration.queue.buildbot.layoutTestCrashLogURLForIteration(iteration, test.path));
193
194             if (test.timeout)
195                 addFailureInfoText(rowElement, "failure-kind-indicator", "timeout");
196
197             if (test.has_diff) {
198                 addFailureInfoLink(rowElement, "additional-link", "diff", iteration.queue.buildbot.layoutTestDiffURLForIteration(iteration, test.path));
199
200                 if (iteration.hasPrettyPatch)
201                     addFailureInfoLink(rowElement, "additional-link", "pretty\xa0diff", iteration.queue.buildbot.layoutTestPrettyDiffURLForIteration(iteration, test.path));
202             }
203
204             if (test.has_image_diff) {
205                 addFailureInfoLink(rowElement, "additional-link", "images", iteration.queue.buildbot.layoutTestImagesURLForIteration(iteration, test.path));
206                 addFailureInfoLink(rowElement, "additional-link", "image\xa0diff", iteration.queue.buildbot.layoutTestImageDiffURLForIteration(iteration, test.path));
207             }
208
209             if (test.has_stderr)
210                 addFailureInfoLink(rowElement, "additional-link", "stderr", iteration.queue.buildbot.layoutTestStderrURLForIteration(iteration, test.path));
211
212             if (hasTestHistory)
213                 addFailureInfoLink(rowElement, "test-history-link", "history", testHistory.historyPageURLForTest(test.path));
214
215             content.appendChild(rowElement);
216         }
217
218         // Work around bug 80159: -webkit-user-select:none not respected when copying content.
219         // We set clipboard data manually, temporarily making non-selectable content hidden
220         // to easily get accurate selection text.
221         content.oncopy = function(event) {
222             var iterator = document.createNodeIterator(
223                 event.currentTarget,
224                 NodeFilter.SHOW_ELEMENT,
225                 {
226                     acceptNode: function(element) {
227                         if (window.getComputedStyle(element).webkitUserSelect !== "none")
228                             return NodeFilter.FILTER_ACCEPT;
229                         return NodeFilter.FILTER_SKIP;
230                     }
231                 }
232             );
233
234             while ((node = iterator.nextNode()))
235                 node.style.visibility = "visible";
236
237             event.currentTarget.style.visibility = "hidden";
238             event.clipboardData.setData('text', window.getSelection());
239             event.currentTarget.style.visibility = "";
240             return false;
241         }
242
243         return content;
244     },
245
246     _presentPopoverForLayoutTestRegressions: function(element, popover, iteration)
247     {
248         if (iteration.layoutTestResults.regressions)
249             var content = this._popoverContentForLayoutTestRegressions(iteration);
250         else {
251             var content = document.createElement("div");
252             content.className = "test-results-popover";
253
254             this._addIterationHeadingToPopover(iteration, content, "layout test failures");
255             this._addDividerToPopover(content);
256
257             var loadingIndicator = document.createElement("div");
258             loadingIndicator.className = "loading-indicator";
259             loadingIndicator.textContent = "Loading\u2026";
260             content.appendChild(loadingIndicator);
261
262             iteration.loadLayoutTestResults(function() {
263                 popover.content = this._popoverContentForLayoutTestRegressions(iteration);
264             }.bind(this));
265         }
266         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
267         popover.content = content;
268         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
269         return true;
270     },
271
272     _presentPopoverForMultipleFailureKinds: function(element, popover, iteration)
273     {
274         function addResultKind(message, url) {
275             var line = document.createElement("a");
276             line.className = "failing-test-kind-summary";
277             line.href = url;
278             line.textContent = message;
279             line.target = "_blank";
280             content.appendChild(line);            
281         }
282
283         var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
284         var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
285         var apiTestResults = iteration.apiTestResults || {failureCount: 0};
286         var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
287         var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
288         var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
289         var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
290
291         var content = document.createElement("div");
292         content.className = "test-results-popover";
293
294         this._addIterationHeadingToPopover(iteration, content);
295         this._addDividerToPopover(content);
296
297         if (layoutTestResults.failureCount) {
298             var message = (layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + "\uff0b" : layoutTestResults.failureCount) + "\u00a0" +
299                 (layoutTestResults.failureCount === 1 ? "layout test failure" : "layout test failures");
300             addResultKind(message, iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
301         }
302
303         if (javascriptTestResults.failureCount) {
304             var message = javascriptTestResults.failureCount + "\u00a0" + (javascriptTestResults.failureCount === 1 ? "javascript test failure" : "javascript test failures");
305             addResultKind(message, iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration));
306         }
307
308         if (apiTestResults.failureCount) {
309             var message = apiTestResults.failureCount + "\u00a0" + (apiTestResults.failureCount === 1 ? "api test failure" : "api test failures");
310             addResultKind(message, iteration.queue.buildbot.apiTestResultsURLForIteration(iteration));
311         }
312
313         if (platformAPITestResults.failureCount) {
314             var message = platformAPITestResults.failureCount + "\u00a0" + (platformAPITestResults.failureCount === 1 ? "platform api test failure" : "platform api test failures");
315             addResultKind(message, iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration));
316         }
317
318         if (pythonTestResults.failureCount) {
319             var message = pythonTestResults.failureCount + "\u00a0" + (pythonTestResults.failureCount === 1 ? "webkitpy test failure" : "webkitpy test failures");
320             addResultKind(message, iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration));
321         }
322
323         if (perlTestResults.errorOccurred)
324             addResultKind("webkitperl tests failed", iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration));
325
326         if (bindingTestResults.errorOccurred)
327             addResultKind("bindings tests failed", iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration));
328
329         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
330         popover.content = content;
331         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
332         return true;
333     }
334 };