OLD | NEW |
| (Empty) |
1 <!-- | |
2 Copyright 2012 Google Inc. All Rights Reserved. | |
3 | |
4 Licensed under the Apache License, Version 2.0 (the "License"); | |
5 you may not use this file except in compliance with the License. | |
6 You may obtain a copy of the License at | |
7 | |
8 http://www.apache.org/licenses/LICENSE-2.0 | |
9 | |
10 Unless required by applicable law or agreed to in writing, software | |
11 distributed under the License is distributed on an "AS IS" BASIS, | |
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 See the License for the specific language governing permissions and | |
14 limitations under the License. | |
15 --> | |
16 <!DOCTYPE html> | |
17 <meta charset="UTF-8"> | |
18 | |
19 <style> | |
20 | |
21 #status-box { | |
22 position: fixed; | |
23 bottom: 0; | |
24 right: 0; | |
25 background: white; | |
26 border: 1px solid black; | |
27 padding-right: 5px; | |
28 } | |
29 | |
30 h1 { | |
31 display: inline; | |
32 } | |
33 | |
34 table { | |
35 border-collapse: collapse; | |
36 } | |
37 | |
38 td { | |
39 border: 1px solid black; | |
40 } | |
41 | |
42 iframe { | |
43 width: 800px; | |
44 height: 600px; | |
45 } | |
46 | |
47 tbody { | |
48 color: gray; | |
49 } | |
50 | |
51 td.log ul li { | |
52 white-space: pre; | |
53 font-family: monospace; | |
54 } | |
55 | |
56 /* Fail styles */ | |
57 tbody.fail { | |
58 color: red; | |
59 } | |
60 | |
61 li.fail { | |
62 color: red; | |
63 } | |
64 | |
65 tbody.timeout { | |
66 color: yellow; | |
67 } | |
68 | |
69 li.timeout { | |
70 color: yellow; | |
71 } | |
72 | |
73 | |
74 /* Pass styles */ | |
75 tbody.pass { | |
76 color: green; | |
77 } | |
78 | |
79 li.pass { | |
80 color: green; | |
81 } | |
82 | |
83 tbody.pass-expected-failure { | |
84 color: #00aa00; | |
85 } | |
86 | |
87 li.pass-expected-failure { | |
88 color: #00aa00; | |
89 } | |
90 | |
91 tbody.skipped { | |
92 color: #00aa00; | |
93 } | |
94 | |
95 li.skipped { | |
96 color: #00aa00; | |
97 } | |
98 | |
99 /* No-test styles */ | |
100 tbody.no-tests { | |
101 color: #ff8a00; | |
102 } | |
103 | |
104 </style> | |
105 <script src="browserdetect/bowser.js"></script> | |
106 <script src="testcases.js"></script> | |
107 | |
108 <div id=status-box> | |
109 <h1 id=status>Pending</h1> | |
110 Tests: <span id=tests></span> | |
111 Loading: <span id=loading></span> (<span id=loaded></span>) | |
112 Running: <span id=running></span> (<span id=finished></span>) | |
113 Posting: <span id=posting></span> (<span id=posted></span>) | |
114 </div> | |
115 | |
116 <table id="results"></table> | |
117 <div id="spacer"></div> | |
118 | |
119 <script> | |
120 'use strict'; | |
121 window.onerror = function(msg, url, line) { | |
122 // Ignore errors caused by webdriver | |
123 if (msg.match(/webdriver/)) | |
124 return; | |
125 | |
126 if (document.getElementById('javascript-errors') == null) { | |
127 document.body.innerHTML = '<pre id="javascript-errors">JAVASCRIPT ERRORS\n\n
</pre>'; | |
128 } | |
129 | |
130 var e = document.getElementById('javascript-errors'); | |
131 var msg = 'Javascript Error in ' + url + '\n' + | |
132 'Line ' + line + ': ' + msg + '\n'; | |
133 e.innerHTML += msg; | |
134 }; | |
135 </script> | |
136 | |
137 <script> | |
138 'use strict'; | |
139 | |
140 (function() { | |
141 window.finished = false; | |
142 window.getTestRunnerProgress = function() { | |
143 return { | |
144 completed: testStates['POSTED'].length, | |
145 total: tests.length, | |
146 }; | |
147 } | |
148 | |
149 var results = []; | |
150 | |
151 if (/coverage/.test(window.location.hash)) { | |
152 // Trigger coverage runs for child tests. | |
153 window.__coverage__ = {}; | |
154 // Share resources loaded by child tests to avoid instrumenting the same | |
155 // source file multiple times. | |
156 window.__resources__ = {original: {}}; | |
157 } | |
158 | |
159 window.addEventListener('load', function() { | |
160 document.querySelector('#spacer').style.height = | |
161 getComputedStyle(document.querySelector('body')).height; | |
162 }); | |
163 function trueOffsetTop(element) { | |
164 var t = 0; | |
165 if (element.offsetParent) { | |
166 do { | |
167 t += element.offsetTop; | |
168 } while (element = element.offsetParent); | |
169 return t; | |
170 } | |
171 } | |
172 | |
173 /** | |
174 * Get a value for busting the cache. | |
175 */ | |
176 var cacheBuster = '' + window.Date.now(); | |
177 | |
178 /** | |
179 * Get the most accurate version of time possible. | |
180 * | |
181 * @return {number} Time as this very moment. | |
182 */ | |
183 function now() { | |
184 try { | |
185 return window.performance.now(); | |
186 } catch (e) { | |
187 return Date.now(); | |
188 } | |
189 } | |
190 | |
191 /** | |
192 * Creates HTML elements in a table for a test. | |
193 * | |
194 * +-----------+------+-------+ | |
195 * | Test Name | Link | Count | | |
196 * +-----------+------+-------+ | |
197 * | Log of test run. | | |
198 * +--------------------------+ | |
199 * | iFrame containing test | | |
200 * +--------------------------+ | |
201 * | |
202 * @param {String} testName Name of the test suite being run. | |
203 * @param {String} testURL The url of the test suite. | |
204 * @return {Element} tbody containing newly created table rows. | |
205 */ | |
206 function createTestRows(testName, testURL) { | |
207 var tableGroup = document.createElement('tbody'); | |
208 tableGroup.id = testName.replace('.', '-'); | |
209 | |
210 var basicInfoRow = document.createElement('tr'); | |
211 basicInfoRow.className = 'info-row'; | |
212 tableGroup.appendChild(basicInfoRow); | |
213 var iframeRow = document.createElement('tr'); | |
214 tableGroup.appendChild(iframeRow); | |
215 var logRow = document.createElement('tr'); | |
216 tableGroup.appendChild(logRow); | |
217 | |
218 // Name | |
219 var header = document.createElement('h1'); | |
220 header.textContent = testName; | |
221 | |
222 var headerCell = document.createElement('td'); | |
223 headerCell.appendChild(header); | |
224 basicInfoRow.appendChild(headerCell); | |
225 | |
226 // Link | |
227 var link = document.createElement('a'); | |
228 link.textContent = testName; | |
229 link.href = testURL; | |
230 | |
231 var linkCell = document.createElement('td'); | |
232 linkCell.appendChild(link); | |
233 basicInfoRow.appendChild(linkCell); | |
234 | |
235 // Test count | |
236 var countCell = document.createElement('td'); | |
237 countCell.className = 'count'; | |
238 basicInfoRow.appendChild(countCell); | |
239 | |
240 // Timing info | |
241 var timingCell = document.createElement('td'); | |
242 timingCell.className = 'timing'; | |
243 basicInfoRow.appendChild(timingCell); | |
244 | |
245 // iframe containing a preview of object | |
246 var iframe = document.createElement('iframe'); | |
247 iframe.src = testURL + '?' + cacheBuster + '#message'; | |
248 | |
249 var iframeCell = document.createElement('td'); | |
250 iframeCell.colSpan = '4'; | |
251 iframeCell.appendChild(iframe); | |
252 iframeRow.appendChild(iframeCell); | |
253 | |
254 // table row containing the complete test log | |
255 var logCell = document.createElement('td'); | |
256 logCell.className = 'log'; | |
257 logCell.colSpan = '4'; | |
258 logRow.appendChild(logCell); | |
259 | |
260 /** | |
261 * Normally we would use "display: none;" but that causes Firefox to return | |
262 * null for getComputedStyle, so instead we have to use this visibility hack. | |
263 */ | |
264 tableGroup.showInfo = function() { | |
265 logRow.style.visibility = 'visible'; | |
266 logRow.style.position = ''; | |
267 logRow.style.height = 'auto'; | |
268 | |
269 iframeRow.style.visibility = 'visible'; | |
270 iframeRow.style.position = ''; | |
271 iframeRow.style.height = 'auto'; | |
272 }; | |
273 tableGroup.hideInfo = function() { | |
274 logRow.style.visibility = 'hidden'; | |
275 logRow.style.position = 'absolute'; | |
276 logRow.style.height = '0'; | |
277 | |
278 iframeRow.style.visibility = 'hidden'; | |
279 iframeRow.style.position = 'absolute'; | |
280 iframeRow.style.height = '0'; | |
281 }; | |
282 tableGroup.toggleInfo = function() { | |
283 if (logRow.style.visibility == 'hidden') { | |
284 tableGroup.showInfo(); | |
285 } else { | |
286 tableGroup.hideInfo(); | |
287 } | |
288 }; | |
289 basicInfoRow.onclick = tableGroup.toggleInfo; | |
290 | |
291 tableGroup.hideInfo(); | |
292 | |
293 return tableGroup; | |
294 } | |
295 | |
296 /* @type {?number} */ var runTestsId = null; | |
297 | |
298 /** | |
299 * Checks all the tests are in the loaded state and then kicks of running | |
300 * the tests. | |
301 */ | |
302 function runTestsIfLoaded() { | |
303 if (testStates['LOADING'].length > 0) | |
304 return; | |
305 | |
306 if (runTestsId == null) { | |
307 runTestsId = window.setInterval(runTests, 10); | |
308 } | |
309 } | |
310 | |
311 function generateCoverageReport() { | |
312 // TODO: generate a pretty report, prompt to save the JSON, post it somewhere | |
313 for (var file in window.__coverage__) { | |
314 var results = window.__coverage__[file]; | |
315 var hits = 0; | |
316 var statements = 0; | |
317 for (var stmt in results.s) { | |
318 statements++; | |
319 if (results.s[stmt] > 0) { | |
320 hits++; | |
321 } | |
322 } | |
323 var percent = (100 * hits / statements).toFixed(2); | |
324 console.log(file + ' statement coverage ' + | |
325 percent + '% (' + hits + '/' + statements + ')'); | |
326 } | |
327 } | |
328 | |
329 /** | |
330 * This object tracks which state a test is in. An individual test should only | |
331 * ever be in one of the lists. You use the changeTestState() function to move | |
332 * from one state to another. | |
333 * | |
334 * Tests start off in the loading state and move downwards until ending up in | |
335 * the finished state. | |
336 */ | |
337 var testStates = {}; | |
338 testStates['LOADING'] = []; /* Test which is being loaded. */ | |
339 testStates['LOADED'] = []; /* Test which have yet to start. */ | |
340 testStates['RUNNING'] = []; /* Test that is currently running. */ | |
341 testStates['FINISHED'] = []; /* Test that are finished. */ | |
342 testStates['POSTING'] = []; /* Test that is currently posting results to server
. */ | |
343 testStates['POSTED'] = []; /* Test which have completed and sent their | |
344 results to the server. */ | |
345 | |
346 /** | |
347 * Track if we should skip the POSTING state, occurs if posting returns an error
. | |
348 */ | |
349 /* @type {Boolean} */ var skipPosting = false; | |
350 | |
351 /** | |
352 * Changes the state of the given test to the given state. | |
353 * This function doesn't check that the state transition actually make sense. | |
354 * | |
355 * @param {Element} test DOM object representing the test. The id will contain | |
356 * the test name. | |
357 * @param {States} The new state to transition too. | |
358 */ | |
359 function changeTestState(test, newState) { | |
360 var i = null; | |
361 | |
362 if (newState == 'POSTING' && skipPosting) { | |
363 newState = 'POSTED'; | |
364 } | |
365 | |
366 var oldState = test.state; | |
367 | |
368 // If we have already posted, we should never leave... | |
369 if (oldState == 'POSTED') { | |
370 throw 'Unexpected state change! Trying to leave POSTED state to ' + newState
; | |
371 return; | |
372 } | |
373 | |
374 if (typeof oldState != 'undefined') { | |
375 var i = testStates[oldState].indexOf(test); | |
376 testStates[oldState].splice(i, 1); | |
377 } | |
378 | |
379 test.state = newState; | |
380 testStates[test.state].unshift(test); | |
381 | |
382 function testSort(a, b) { | |
383 return a.id.localeCompare(b.id); | |
384 } | |
385 testStates[test.state].sort(testSort); | |
386 | |
387 switch(newState) { | |
388 case 'LOADING': | |
389 runTestsIfLoaded(); | |
390 break; | |
391 | |
392 case 'LOADED': | |
393 if (oldState != 'LOADING') { | |
394 throw 'Unexpected state change! ' + oldState + ' changing to LOADED'; | |
395 } | |
396 break; | |
397 | |
398 case 'RUNNING': | |
399 if (oldState != 'LOADED') { | |
400 throw 'Unexpected state change! ' + oldState + ' changing to RUNNING'; | |
401 } | |
402 | |
403 test.start = now(); | |
404 var testWindow = test.querySelector('iframe').contentWindow; | |
405 var msg = {type: 'start', url: testWindow.location.href + ''}; | |
406 testWindow.postMessage(msg, '*'); | |
407 break; | |
408 | |
409 case 'FINISHED': | |
410 // If a test doesn't use any timing stuff it could come from the LOADING or | |
411 // LOADED state into the FINISHED state bypassing the RUNNING state. | |
412 if (oldState != 'LOADING' && oldState != 'LOADED' && oldState != 'RUNNING')
{ | |
413 throw 'Unexpected state change! ' + oldState + ' changing to FINISHED'; | |
414 } | |
415 | |
416 var timingInfo = test.querySelector('.timing'); | |
417 if (test.start) { | |
418 test.finished = now(); | |
419 timingInfo.textContent = (test.finished - test.start).toFixed(2) + 'ms'; | |
420 } else { | |
421 timingInfo.textContent = '(Load only)'; | |
422 } | |
423 | |
424 var test_iframe = test.querySelector('iframe'); | |
425 processResults(test, expectedFailuresForBrowser(test_iframe.contentWindow.ex
pected_failures)); | |
426 | |
427 break; | |
428 | |
429 case 'POSTING': | |
430 if (oldState != 'FINISHED') { | |
431 throw 'Unexpected state change! ' + oldState + ' changing to POSTING'; | |
432 } | |
433 | |
434 if (test.className == 'fail') { | |
435 // Open up the window of the failed result | |
436 test.showInfo(); | |
437 // Scroll to it. | |
438 window.scroll(0, trueOffsetTop(test)); | |
439 } | |
440 | |
441 // Open the status window for taking of screenshots | |
442 var data = new FormData(); | |
443 data.append('data', JSON.stringify(test.results_processed)); | |
444 | |
445 var xhr = new XMLHttpRequest(); | |
446 xhr.onload = function (e) { | |
447 if (e.target.status >= 400) { | |
448 skipPosting = true; | |
449 } | |
450 // Move from running to finished state | |
451 changeTestState(this, 'POSTED'); | |
452 }.bind(test); | |
453 xhr.open('POST', 'test-results-post.html', true); | |
454 xhr.send(data); | |
455 | |
456 break; | |
457 | |
458 case 'POSTED': | |
459 // If we are skipping POSTING, tests can transition straight from FINISHED | |
460 // state into the POSTED state. | |
461 if (oldState != 'POSTING' && (!skipPosting || oldState != 'FINISHED')) { | |
462 throw 'Unexpected state change! ' + oldState + ' changing to POSTED'; | |
463 } | |
464 break; | |
465 } | |
466 } | |
467 | |
468 /** | |
469 * Elements for reporting the overall status. | |
470 */ | |
471 /* @type {Element} */ var statusElement = document.querySelector('#status'); | |
472 | |
473 /* @type {Element} */ var testElement = document.querySelector('#tests'); | |
474 | |
475 /* @type {Element} */ var loadingElement = document.querySelector('#loading'); | |
476 /* @type {Element} */ var loadedElement = document.querySelector('#loaded'); | |
477 | |
478 /* @type {Element} */ var runningElement = document.querySelector('#running'); | |
479 /* @type {Element} */ var finishedElement = document.querySelector('#finished'); | |
480 | |
481 /* @type {Element} */ var postingElement = document.querySelector('#posting'); | |
482 /* @type {Element} */ var postedElement = document.querySelector('#posted'); | |
483 | |
484 /** | |
485 * Update the status dialog with information about the current status. | |
486 */ | |
487 function updateStatus() { | |
488 testElement.textContent = tests.length; | |
489 | |
490 loadingElement.textContent = testStates['LOADING'].length; | |
491 loadedElement.textContent = testStates['LOADED'].length; | |
492 | |
493 runningElement.textContent = testStates['RUNNING'].length; | |
494 finishedElement.textContent = testStates['FINISHED'].length; | |
495 | |
496 postingElement.textContent = testStates['POSTING'].length; | |
497 postedElement.textContent = testStates['POSTED'].length; | |
498 | |
499 if (testStates['LOADED'].length > 0) { | |
500 statusElement.textContent = 'Loading'; | |
501 } else if (testStates['RUNNING'].length > 0) { | |
502 statusElement.textContent = 'Running'; | |
503 } else if (testStates['POSTING'].length > 0) { | |
504 statusElement.textContent = 'Posting results'; | |
505 } else if (testStates['POSTED'].length == tests.length) { | |
506 statusElement.textContent = 'Finished'; | |
507 | |
508 window.clearInterval(updateStatusId); | |
509 window.clearInterval(runTestsId); | |
510 window.finished = true; | |
511 if (window.__coverage__) { | |
512 generateCoverageReport(); | |
513 } | |
514 } | |
515 } | |
516 | |
517 /* @type {?number} */ var updateStatusId = setInterval(updateStatus, 100); | |
518 | |
519 | |
520 var testRunners = []; | |
521 | |
522 /** | |
523 * Create the iframes for each test. | |
524 */ | |
525 window.onload = function createTestRunners() { | |
526 // Filter the tests | |
527 var filter = window.location.href.split('?')[1]; | |
528 if (filter) { | |
529 filter = new RegExp(filter); | |
530 tests = tests.filter(function(v) { | |
531 return filter.exec(v); | |
532 }); | |
533 } | |
534 | |
535 function testSort(a, b) { | |
536 a = a.replace('.', '-'); | |
537 b = b.replace('.', '-'); | |
538 return a.localeCompare(b); | |
539 } | |
540 tests.sort(testSort); | |
541 | |
542 for (var i = 0; i < tests.length; i++) { | |
543 var testName = tests[i]; | |
544 var testURL = 'testcases/' + testName; | |
545 | |
546 var test = createTestRows(testName, testURL); | |
547 testRunners.unshift(test); | |
548 | |
549 changeTestState(test, 'LOADING'); | |
550 | |
551 document.querySelector('#results').appendChild(test); | |
552 test.querySelector('iframe').contentWindow.onload = function() { | |
553 // You can get multiple onload events, only deal with the first one. | |
554 if (this.state == 'LOADING') { | |
555 changeTestState(this, 'LOADED'); | |
556 runTestsIfLoaded(); | |
557 } | |
558 }.bind(test); | |
559 } | |
560 | |
561 }; | |
562 | |
563 /** | |
564 * Start as many loaded tests as possible, wait for results and then post | |
565 * them. | |
566 */ | |
567 function runTests() { | |
568 // Start a test running | |
569 if (testStates['LOADED'].length > 0 && | |
570 testStates['RUNNING'].length < 1) { | |
571 changeTestState(testStates['LOADED'][0], 'RUNNING'); | |
572 } | |
573 | |
574 // Start a test posting | |
575 if (testStates['FINISHED'].length > 0 && | |
576 testStates['POSTING'].length < 1) { | |
577 changeTestState(testStates['FINISHED'][0], 'POSTING'); | |
578 } | |
579 | |
580 // Deal with tests which are taking too long... | |
581 for (var i in testStates['RUNNING']) { | |
582 var test = testStates['RUNNING'][i]; | |
583 if (now() - test.start > 300e3) { | |
584 // Only way to stop all the javascript and anything else running in the | |
585 // test is to clear the document. | |
586 var test_iframe = test.querySelector('iframe'); | |
587 test_iframe.src = "data:text/html;charset=utf-8,<!DOCTYPE html><html><body
>TIMEOUT</body></html>"; | |
588 | |
589 test.results_raw = []; | |
590 changeTestState(test, 'FINISHED'); | |
591 } | |
592 } | |
593 } | |
594 | |
595 /* @type {Object.<string, Object>} */ var testResults = {}; | |
596 /** | |
597 * Callback that occurs when the test has finished running. | |
598 */ | |
599 window.addEventListener( | |
600 'message', | |
601 function(evt) { | |
602 // If the test timed out, this will fail with a cross-origin error. | |
603 try { | |
604 var testname = evt.source.location.pathname.split('/').pop().replace('.',
'-'); | |
605 } catch (e) { | |
606 return; | |
607 } | |
608 | |
609 var test = document.getElementById(testname); | |
610 | |
611 // We only respond to complete as postMessage doesn't guarantee order so | |
612 // result messages can come in after the complete message. | |
613 if (evt.data['type'] != 'complete') | |
614 return; | |
615 | |
616 // Try changing to state FINISHED, but this message may be after the a | |
617 // timeout or transition. | |
618 try { | |
619 test.results_raw = evt.data['tests']; | |
620 changeTestState(test, 'FINISHED'); | |
621 } catch (e) { | |
622 console.warn(e); | |
623 } | |
624 }, | |
625 false); | |
626 | |
627 | |
628 /** | |
629 * Filters expected test failures for ones that match the current browser | |
630 * configuration. | |
631 * | |
632 * @param {Array.<Object>} Test failure expectations for all browser | |
633 * configurations. | |
634 * @return {Object.<string, string>} Mapping from test regex to failure reason | |
635 * for current browser. | |
636 */ | |
637 function expectedFailuresForBrowser(expected_failures) { | |
638 var browser_expected_failures = {}; | |
639 if (typeof expected_failures === 'object') { | |
640 expected_failures.forEach(function(expected_failure) { | |
641 if (expected_failure.browser_configurations.some( | |
642 browserConfigurationMatches)) { | |
643 expected_failure.tests.forEach(function(test) { | |
644 browser_expected_failures[test] = expected_failure.message; | |
645 }); | |
646 } | |
647 }); | |
648 } | |
649 return browser_expected_failures; | |
650 } | |
651 | |
652 /** | |
653 * Returns whether the browser configuration pattern matches the browser | |
654 * configuration reported by bowser.js. | |
655 * Example: | |
656 * { name: 'Chrome', version: '28|29' } | |
657 * matches browser configuration: | |
658 * { name: 'Chrome', webkit: true, chrome: true, version: '28.0' } | |
659 * | |
660 * @param {Object} Key - RegExp value pairs to check against. | |
661 * @return {bool} | |
662 */ | |
663 function browserConfigurationMatches(browser_configuration) { | |
664 for (var browser_feature in browser_configuration) { | |
665 var valueRegex = new RegExp(browser_configuration[browser_feature]); | |
666 if (!valueRegex.test(bowser[browser_feature])) { | |
667 return false; | |
668 } | |
669 } | |
670 return true; | |
671 } | |
672 | |
673 /** | |
674 * Processes the test's results and put the information into the test object. | |
675 * | |
676 * @param {Element} test DOM object representing the test. | |
677 * @param {Array.<Object>} results List of testharness.js result objects. | |
678 */ | |
679 function processResults(test, browser_expected_failures) { | |
680 var counts = { | |
681 passed: 0, | |
682 failed: 0, | |
683 skipped: 0, | |
684 expected_failed: 0, // This count is included in the above | |
685 total: test.results_raw.length | |
686 }; | |
687 var results = []; | |
688 var newResultsDiv = document.createElement('ul'); | |
689 | |
690 for(var i = 0; i < test.results_raw.length; i++) { | |
691 var result = test.results_raw[i]; | |
692 results[i] = result; | |
693 | |
694 // Invert expected_failures | |
695 var expected_failure = null; | |
696 for(var tname in browser_expected_failures) { | |
697 var tomatch = tname; | |
698 if (tname.charAt(0) == '/' && tname.charAt(tname.length-1) == '/') { | |
699 tomatch = new RegExp(tname.substr(1, tname.length-2)); | |
700 } | |
701 if (result['name'].match(tomatch)) { | |
702 expected_failure = browser_expected_failures[tname]; | |
703 } | |
704 } | |
705 if (expected_failure !== null && result.status != result.NOTRUN) { | |
706 if (result.status != result.FAIL) { | |
707 result.message = "Expected failure (" + expected_failure + "), actually
" + | |
708 {0: 'PASS', 2: 'TIMEOUT', 3:'NOTRUN'}[result.status] + " " + | |
709 result.message; | |
710 result.status = result.FAIL; | |
711 } else { | |
712 result.status = result.PASS; | |
713 result.message = "Expected failure (" + expected_failure + "): " + resul
t.message; | |
714 } | |
715 counts.expected_failed++; | |
716 } | |
717 | |
718 var output = document.createElement('li'); | |
719 output.innerHTML += result.name + ': '; | |
720 | |
721 switch (result.status) { | |
722 case result.PASS: | |
723 if (!expected_failure) { | |
724 output.className = 'pass'; | |
725 } else { | |
726 output.className = 'pass-expected-failure'; | |
727 } | |
728 counts.passed++; | |
729 break; | |
730 | |
731 case result.TIMEOUT: | |
732 output.className = 'timeout'; | |
733 counts.failed++; | |
734 break; | |
735 | |
736 case result.NOTRUN: | |
737 output.className = 'skipped'; | |
738 counts.skipped++; | |
739 break; | |
740 | |
741 case result.FAIL: | |
742 default: | |
743 output.className = 'failed'; | |
744 counts.failed++; | |
745 } | |
746 | |
747 output.innerHTML += output.className; | |
748 if (result.message != null) { | |
749 output.innerHTML += ' - ' + result.message; | |
750 } | |
751 newResultsDiv.appendChild(output); | |
752 } | |
753 test.querySelector('.log').appendChild(newResultsDiv); | |
754 | |
755 var debug = ''; | |
756 try { | |
757 debug = test.querySelector('iframe').contentDocument.getElementById('debug')
.innerText; | |
758 } catch (e) { | |
759 debug = 'No debug... :('; | |
760 } | |
761 | |
762 if (counts.total > 0) { | |
763 test.results_processed = { | |
764 type: 'result', | |
765 testName: test.id, | |
766 results: results, | |
767 debug: debug | |
768 }; | |
769 | |
770 if (counts.failed == 0) { | |
771 if (counts.expected_failed > 0) { | |
772 test.className = 'pass-expected-failure'; | |
773 } else if (counts.skipped > 0) { | |
774 test.className = 'skipped'; | |
775 } else { | |
776 test.className = 'pass'; | |
777 } | |
778 } else { | |
779 test.className = 'fail'; | |
780 } | |
781 | |
782 var summary = counts.total + ' tests; '; | |
783 if (counts.passed > 0) { | |
784 summary += ' ' + counts.passed + ' passed'; | |
785 if (counts.expected_failed > 0) { | |
786 summary += ' (with ' + counts.expected_failed + ' expected failures)'; | |
787 } | |
788 } | |
789 if (counts.failed > 0) { | |
790 summary += ' ' + counts.failed + ' failed'; | |
791 } | |
792 if (counts.skipped > 0) { | |
793 summary += ' ' + counts.skipped + ' skipped'; | |
794 } | |
795 test.querySelector('.count').textContent = summary; | |
796 } else { | |
797 test.results_processed = { | |
798 type: 'result', | |
799 testName: test.id, | |
800 results: [], | |
801 }; | |
802 test.className = 'no-tests'; | |
803 test.querySelector('.count').textContent = 'TIMEOUT'; | |
804 } | |
805 } | |
806 | |
807 })(); | |
808 | |
809 | |
810 </script> | |
OLD | NEW |