5 font-family: Helvetica, sans-serif;
9 body > p:first-of-type {
22 tr:not(.results-row) td {
26 tr:not(.results-row) td:first-of-type {
30 td:not(:first-of-type) {
31 text-transform: lowercase;
43 -webkit-user-select: none;
44 -moz-user-select: none;
53 background-color: white;
56 .results-row iframe, .results-row img {
61 .results-row[data-expanded="false"] {
71 background-color: rgba(255, 255, 255, 0.85);
72 border: 1px solid silver;
77 background-color: white;
80 border: 1px solid gray;
81 display: inline-block;
91 .current .expand-button {
101 .test-link.flagged:after {
105 .stopped-running-early-message {
106 border: 3px solid #d00;
108 display: inline-block;
113 display: inline-block;
114 border: 1px solid gray;
118 .result-container iframe, .result-container img {
124 background-color: white;
131 box-shadow: 2px 2px 2px #888;
132 -webkit-transition: opacity .2s;
136 background-color: white;
139 #options-menu label {
144 pointer-events: none;
152 background-color: silver;
155 .pixel-zoom-container {
162 display: -webkit-box;
164 pointer-events: none;
165 background-color: silver;
167 border: 1px solid gray;
168 box-shadow: 0 0 5px rgba(0, 0, 0, 0.75);
171 .pixel-zoom-container > * {
174 border: 1px solid black;
177 background-color: white;
180 .pixel-zoom-container .scaled-image-container {
187 .scaled-image-container > img {
191 image-rendering: -webkit-optimize-contrast;
198 <style id="unexpected-style"></style>
202 function globalState()
209 hasImageFailures: false,
210 hasTextFailures: false,
213 shouldToggleImages: true,
214 nonFlakyFailingTests: [],
217 unexpectedPassTests: []
223 function ADD_RESULTS(input)
225 globalState().results = input;
229 <script src="full_results.json"></script>
232 function stripExtension(test)
234 var index = test.lastIndexOf('.');
235 return test.substring(0, index);
238 function matchesSelector(node, selector)
240 if (node.webkitMatchesSelector)
241 return node.webkitMatchesSelector(selector);
243 if (node.mozMatchesSelector)
244 return node.mozMatchesSelector(selector);
247 function parentOfType(node, selector)
249 while (node = node.parentNode) {
250 if (matchesSelector(node, selector))
258 function remove(node)
260 node.parentNode.removeChild(node);
263 function forEach(nodeList, handler)
265 Array.prototype.forEach.call(nodeList, handler);
268 function resultIframe(src)
270 // FIXME: use audio tags for AUDIO tests?
271 var layoutTestsIndex = src.indexOf('LayoutTests');
273 if (layoutTestsIndex != -1) {
274 var hasTrac = src.indexOf('trac.webkit.org') != -1;
275 var prefix = hasTrac ? 'trac.webkit.org/.../' : '';
276 name = prefix + src.substring(layoutTestsIndex + 'LayoutTests/'.length);
278 var lastDashIndex = src.lastIndexOf('-pretty');
279 if (lastDashIndex == -1)
280 lastDashIndex = src.lastIndexOf('-');
281 name = src.substring(lastDashIndex + 1);
284 var tagName = (src.lastIndexOf('.png') == -1) ? 'iframe' : 'img';
286 if (tagName != 'img')
287 src += '?format=txt';
288 return '<div class=result-container><div class=label>' + name + '</div><' + tagName + ' src="' + src + '"></' + tagName + '></div>';
291 function togglingImage(prefix)
293 return '<div class=result-container><div class="label imageText"></div><img class=animatedImage data-prefix="' +
294 prefix + '"></img></div>';
297 function toggleExpectations(element)
299 var expandLink = element;
300 if (expandLink.className != 'expand-button-text')
301 expandLink = expandLink.querySelector('.expand-button-text');
303 if (expandLink.textContent == '+')
304 expandExpectations(expandLink);
306 collapseExpectations(expandLink);
309 function collapseExpectations(expandLink)
311 expandLink.textContent = '+';
312 var existingResultsRow = parentOfType(expandLink, 'tbody').querySelector('.results-row');
313 if (existingResultsRow)
314 updateExpandedState(existingResultsRow, false);
317 function updateExpandedState(row, isExpanded)
319 row.setAttribute('data-expanded', isExpanded);
320 updateImageTogglingTimer();
323 function appendHTML(node, html)
325 if (node.insertAdjacentHTML)
326 node.insertAdjacentHTML('beforeEnd', html);
328 node.innerHTML += html;
331 function expandExpectations(expandLink)
333 var row = parentOfType(expandLink, 'tr');
334 var parentTbody = row.parentNode;
335 var existingResultsRow = parentTbody.querySelector('.results-row');
337 var enDash = '\u2013';
338 expandLink.textContent = enDash;
339 if (existingResultsRow) {
340 updateExpandedState(existingResultsRow, true);
344 var newRow = document.createElement('tr');
345 newRow.className = 'results-row';
346 var newCell = document.createElement('td');
347 newCell.colSpan = row.querySelectorAll('td').length;
349 var resultLinks = row.querySelectorAll('.result-link');
350 var hasTogglingImages = false;
351 for (var i = 0; i < resultLinks.length; i++) {
352 var link = resultLinks[i];
354 if (link.textContent == 'images') {
355 hasTogglingImages = true;
356 result = togglingImage(link.getAttribute('data-prefix'));
358 result = resultIframe(link.href);
360 appendHTML(newCell, result);
363 newRow.appendChild(newCell);
364 parentTbody.appendChild(newRow);
366 updateExpandedState(newRow, true);
368 updateImageTogglingTimer();
371 function updateImageTogglingTimer()
373 var hasVisibleAnimatedImage = document.querySelector('.results-row[data-expanded="true"] .animatedImage');
374 if (!hasVisibleAnimatedImage) {
375 clearInterval(globalState().togglingImageInterval);
376 globalState().togglingImageInterval = null;
380 if (!globalState().togglingImageInterval) {
382 globalState().togglingImageInterval = setInterval(toggleImages, 2000);
386 function async(func, args)
388 setTimeout(function() { func.apply(null, args); }, 100);
391 function visibleExpandLinks()
393 if (onlyShowUnexpectedFailures())
394 return document.querySelectorAll('tbody:not(.expected) .expand-button-text');
396 return document.querySelectorAll('.expand-button-text');
399 function expandAllExpectations()
401 var expandLinks = visibleExpandLinks();
402 for (var i = 0, len = expandLinks.length; i < len; i++)
403 async(expandExpectations, [expandLinks[i]]);
406 function collapseAllExpectations()
408 var expandLinks = visibleExpandLinks();
409 for (var i = 0, len = expandLinks.length; i < len; i++)
410 async(collapseExpectations, [expandLinks[i]]);
413 function shouldUseTracLinks()
415 return !globalState().results.layout_tests_dir || !location.toString().indexOf('file://') == 0;
418 function testLink(test)
421 if (shouldUseTracLinks()) {
422 var revision = globalState().results.revision;
423 basePath = 'http://trac.webkit.org';
424 basePath += revision ? ('/export/' + revision) : '/browser';
425 basePath += '/trunk/LayoutTests/';
427 basePath = globalState().results.layout_tests_dir + '/';
428 return '<a class=test-link href="' + basePath + test + '">' + test + '</a>';
431 function testLinkWithExpandButton(test)
433 return '<span class=expand-button onclick="toggleExpectations(this)"><span class=expand-button-text>+</span></span>' + testLink(test);
436 function resultLink(testPrefix, suffix, contents)
438 return referenceLink(testPrefix, testPrefix + suffix, contents);
441 function referenceLink(testPrefix, reference_filename, contents)
443 return '<a class=result-link href="' + reference_filename + '" data-prefix="' + testPrefix + '">' + contents + '</a> ';
446 function isFailureExpected(expected, actual)
448 var isExpected = true;
449 if (actual != 'SKIP') {
450 var expectedArray = expected.split(' ');
451 var actualArray = actual.split(' ');
452 for (var i = 0; i < actualArray.length; i++) {
453 var actualValue = actualArray[i];
454 if (expectedArray.indexOf(actualValue) == -1 &&
455 (expectedArray.indexOf('FAIL') == -1 ||
456 (actualValue != 'IMAGE' && actualValue != 'TEXT' && actualValue != 'IMAGE+TEXT')))
463 function processGlobalStateFor(testObject)
465 var test = testObject.name;
466 if (testObject.has_stderr)
467 globalState().testsWithStderr.push(testObject);
469 globalState().hasHttpTests = globalState().hasHttpTests || test.indexOf('http/') == 0;
471 var actual = testObject.actual;
472 var expected = testObject.expected || 'PASS';
473 if (globalState().results.uses_expectations_file)
474 testObject.isExpected = isFailureExpected(expected, actual);
476 if (actual == 'MISSING') {
477 // FIXME: make sure that new-run-webkit-tests spits out an -actual.txt file for
478 // tests with MISSING results.
479 globalState().missingResults.push(testObject);
483 if (actual.indexOf(' ') != -1) {
484 globalState().flakyTests.push(testObject);
488 if (actual == 'PASS' && expected != 'PASS') {
489 globalState().unexpectedPassTests.push(testObject);
493 if (actual == 'CRASH' && expected != 'CRASH') {
494 globalState().crashTests.push(testObject);
498 if (actual == 'TIMEOUT' && expected != 'TIMEOUT') {
499 globalState().timeoutTests.push(testObject);
503 globalState().nonFlakyFailingTests.push(testObject);
506 function toggleImages()
508 var images = document.querySelectorAll('.animatedImage');
509 var imageTexts = document.querySelectorAll('.imageText');
510 for (var i = 0, len = images.length; i < len; i++) {
511 var image = images[i];
512 var text = imageTexts[i];
513 if (text.textContent == 'Expected Image') {
514 text.textContent = 'Actual Image';
515 image.src = image.getAttribute('data-prefix') + '-actual.png';
517 text.textContent = 'Expected Image';
518 image.src = image.getAttribute('data-prefix') + '-expected.png';
523 function textResultLinks(prefix)
525 var html = resultLink(prefix, '-expected.txt', 'expected') +
526 resultLink(prefix, '-actual.txt', 'actual') +
527 resultLink(prefix, '-diff.txt', 'diff');
529 if (globalState().results.has_pretty_patch)
530 html += resultLink(prefix, '-pretty-diff.html', 'pretty diff');
532 if (globalState().results.has_wdiff)
533 html += resultLink(prefix, '-wdiff.html', 'wdiff');
538 function tableRow(testObject)
541 if (globalState().results.uses_expectations_file)
542 row += ' class="' + (testObject.isExpected ? 'expected' : '') + '"';
543 if (testObject.is_mismatch_reftest)
544 row += ' mismatchreftest=true';
547 row += '<td>' + testLinkWithExpandButton(testObject.name) + '</td>';
549 var test_prefix = stripExtension(testObject.name);
552 var actual = testObject.actual;
553 var expected = testObject.expected;
554 if (actual.indexOf('TEXT') != -1) {
555 globalState().hasTextFailures = true;
556 row += textResultLinks(test_prefix);
559 if (actual.indexOf('AUDIO') != -1) {
560 row += resultLink(test_prefix, '-expected.wav', 'expected audio');
561 row += resultLink(test_prefix, '-actual.wav', 'actual audio');
564 if (actual.indexOf('MISSING') != -1) {
566 if (testObject.is_missing_audio)
567 row += resultLink(test_prefix, '-actual.wav', 'audio result');
568 if (testObject.is_missing_text)
569 row += resultLink(test_prefix, '-actual.txt', 'result');
574 if (actual.indexOf('IMAGE') != -1) {
575 globalState().hasImageFailures = true;
577 if (testObject.is_mismatch_reftest) {
578 if (testObject.ref_file)
579 row += referenceLink(test_prefix, testObject.ref_file, 'ref mismatch html');
581 row += resultLink(test_prefix, '-expected-mismatch.html', 'ref mismatch html');
582 row += resultLink(test_prefix, '-actual.png', 'actual');
584 if (testObject.is_reftest) {
585 if (testObject.ref_file)
586 row += referenceLink(test_prefix, testObject.ref_file, 'ref html');
588 row += resultLink(test_prefix, '-expected.html', 'ref html');
590 if (globalState().shouldToggleImages) {
591 row += resultLink(test_prefix, '-diffs.html', 'images');
593 row += resultLink(test_prefix, '-expected.png', 'expected');
594 row += resultLink(test_prefix, '-actual.png', 'actual');
597 var diff = testObject.image_diff_percent;
598 row += resultLink(test_prefix, '-diff.png', 'diff (' + diff + '%)');
602 if (actual.indexOf('MISSING') != -1) {
604 if (testObject.is_missing_image)
605 row += resultLink(test_prefix, '-actual.png', 'png result');
610 if (globalState().results.uses_expectations_file || actual.indexOf(' ') != -1)
611 row += '<td>' + actual + '</td>';
613 if (globalState().results.uses_expectations_file)
614 row += '<td>' + expected + '</td>';
616 row += '</tr></tbody>';
620 function forEachTest(handler, opt_tree, opt_prefix)
622 var tree = opt_tree || globalState().results.tests;
623 var prefix = opt_prefix || '';
625 for (var key in tree) {
626 var newPrefix = prefix ? (prefix + '/' + key) : key;
627 if ('actual' in tree[key]) {
628 var testObject = tree[key];
629 testObject.name = newPrefix;
632 forEachTest(handler, tree[key], newPrefix);
636 function hasUnexpected(tests)
638 return tests.some(function (test) { return !test.isExpected; });
641 function testList(tests, header, tableId)
645 var html = '<h1>' + header + ' (' + tests.length + '):</h1><table id="' + tableId + '"';
646 if (!hasUnexpected(tests))
647 html += ' class=expected';
650 // FIXME: add the expected failure column for all the test lists if globalState().results.uses_expectations_file
651 if (tableId == 'passes-table')
652 html += '<thead><th>test</th><th>expected failure</th></thead>';
654 for (var i = 0; i < tests.length; i++) {
655 var testObject = tests[i];
656 var test = testObject.name;
658 if (globalState().results.uses_expectations_file)
659 html += ' class="' + (testObject.isExpected ? 'expected' : '') + '"';
661 html += (tableId == 'passes-table') ? testLink(test) : testLinkWithExpandButton(test);
664 if (tableId == 'stderr-table')
665 html += resultLink(stripExtension(test), '-stderr.txt', 'stderr');
666 else if (tableId == 'passes-table')
667 html += testObject.expected;
668 else if (tableId == 'crash-tests-table')
669 html += resultLink(stripExtension(test), '-crash-log.txt', 'crash log');
670 else if (tableId == 'timeout-tests-table') {
671 // FIXME: only include timeout actual/diff results here if we actually spit out results for timeout tests.
672 html += textResultLinks(stripExtension(test));
675 html += '</td></tr></tbody>';
681 function toArray(nodeList)
683 return Array.prototype.slice.call(nodeList);
686 function trim(string)
688 return string.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
691 // Just a namespace for code management.
692 var TableSorter = {};
694 TableSorter._forwardArrow = '<svg style="width:10px;height:10px"><polygon points="0,0 10,0 5,10" style="fill:#ccc"></svg>';
696 TableSorter._backwardArrow = '<svg style="width:10px;height:10px"><polygon points="0,10 10,10 5,0" style="fill:#ccc"></svg>';
698 TableSorter._sortedContents = function(header, arrow)
700 return arrow + ' ' + trim(header.textContent) + ' ' + arrow;
703 TableSorter._updateHeaderClassNames = function(newHeader)
705 var sortHeader = document.querySelector('.sortHeader');
707 if (sortHeader == newHeader) {
708 var isAlreadyReversed = sortHeader.classList.contains('reversed');
709 if (isAlreadyReversed)
710 sortHeader.classList.remove('reversed');
712 sortHeader.classList.add('reversed');
714 sortHeader.textContent = sortHeader.textContent;
715 sortHeader.classList.remove('sortHeader');
716 sortHeader.classList.remove('reversed');
720 newHeader.classList.add('sortHeader');
723 TableSorter._textContent = function(tbodyRow, column)
725 return tbodyRow.querySelectorAll('td')[column].textContent;
728 TableSorter._sortRows = function(newHeader, reversed)
730 var testsTable = document.getElementById('results-table');
731 var headers = toArray(testsTable.querySelectorAll('th'));
732 var sortColumn = headers.indexOf(newHeader);
734 var rows = toArray(testsTable.querySelectorAll('tbody'));
736 rows.sort(function(a, b) {
737 // Only need to support lexicographic sort for now.
738 var aText = TableSorter._textContent(a, sortColumn);
739 var bText = TableSorter._textContent(b, sortColumn);
741 // Forward sort equal values by test name.
742 if (sortColumn && aText == bText) {
743 var aTestName = TableSorter._textContent(a, 0);
744 var bTestName = TableSorter._textContent(b, 0);
745 if (aTestName == bTestName)
747 return aTestName < bTestName ? -1 : 1;
751 return aText < bText ? 1 : -1;
753 return aText < bText ? -1 : 1;
756 for (var i = 0; i < rows.length; i++)
757 testsTable.appendChild(rows[i]);
760 TableSorter.sortColumn = function(columnNumber)
762 var newHeader = document.getElementById('results-table').querySelectorAll('th')[columnNumber];
763 TableSorter._sort(newHeader);
766 TableSorter.handleClick = function(e)
768 var newHeader = e.target;
769 if (newHeader.localName != 'th')
771 TableSorter._sort(newHeader);
774 TableSorter._sort = function(newHeader)
776 TableSorter._updateHeaderClassNames(newHeader);
778 var reversed = newHeader.classList.contains('reversed');
779 var sortArrow = reversed ? TableSorter._backwardArrow : TableSorter._forwardArrow;
780 newHeader.innerHTML = TableSorter._sortedContents(newHeader, sortArrow);
782 TableSorter._sortRows(newHeader, reversed);
785 var PixelZoomer = {};
787 PixelZoomer.showOnDelay = true;
788 PixelZoomer._zoomFactor = 6;
790 var kResultWidth = 800;
791 var kResultHeight = 600;
793 var kZoomedResultWidth = kResultWidth * PixelZoomer._zoomFactor;
794 var kZoomedResultHeight = kResultHeight * PixelZoomer._zoomFactor;
796 PixelZoomer._zoomImageContainer = function(url)
798 var container = document.createElement('div');
799 container.className = 'zoom-image-container';
801 var title = url.match(/\-([^\-]*)\.png/)[1];
803 var label = document.createElement('div');
804 label.className = 'label';
805 label.appendChild(document.createTextNode(title));
806 container.appendChild(label);
808 var imageContainer = document.createElement('div');
809 imageContainer.className = 'scaled-image-container';
811 var image = new Image();
813 image.style.width = kZoomedResultWidth + 'px';
814 image.style.height = kZoomedResultHeight + 'px';
815 image.style.border = '1px solid black';
816 imageContainer.appendChild(image);
817 container.appendChild(imageContainer);
822 PixelZoomer._createContainer = function(e)
824 var tbody = parentOfType(e.target, 'tbody');
825 var row = tbody.querySelector('tr');
826 var imageDiffLinks = row.querySelectorAll('a[href$=".png"]');
828 var container = document.createElement('div');
829 container.className = 'pixel-zoom-container';
833 var togglingImageLink = row.querySelector('a[href$="-diffs.html"]');
834 if (togglingImageLink) {
835 var prefix = togglingImageLink.getAttribute('data-prefix');
836 container.appendChild(PixelZoomer._zoomImageContainer(prefix + '-expected.png'));
837 container.appendChild(PixelZoomer._zoomImageContainer(prefix + '-actual.png'));
840 for (var i = 0; i < imageDiffLinks.length; i++)
841 container.appendChild(PixelZoomer._zoomImageContainer(imageDiffLinks[i].href));
843 document.body.appendChild(container);
844 PixelZoomer._drawAll();
847 PixelZoomer._draw = function(imageContainer)
849 var image = imageContainer.querySelector('img');
850 var containerBounds = imageContainer.getBoundingClientRect();
851 image.style.left = (containerBounds.width / 2 - PixelZoomer._percentX * kZoomedResultWidth) + 'px';
852 image.style.top = (containerBounds.height / 2 - PixelZoomer._percentY * kZoomedResultHeight) + 'px';
855 PixelZoomer._drawAll = function()
857 forEach(document.querySelectorAll('.pixel-zoom-container .scaled-image-container'), PixelZoomer._draw);
860 PixelZoomer.handleMouseOut = function(e)
862 if (e.relatedTarget && e.relatedTarget.tagName != 'IFRAME')
865 // If e.relatedTarget is null, we've moused out of the document.
866 var container = document.querySelector('.pixel-zoom-container');
871 PixelZoomer.handleMouseMove = function(e) {
872 if (PixelZoomer._mouseMoveTimeout)
873 clearTimeout(PixelZoomer._mouseMoveTimeout);
875 if (parentOfType(e.target, '.pixel-zoom-container'))
878 var container = document.querySelector('.pixel-zoom-container');
880 var resultContainer = (e.target.className == 'result-container') ?
881 e.target : parentOfType(e.target, '.result-container');
882 if (!resultContainer || !resultContainer.querySelector('img')) {
888 var targetLocation = e.target.getBoundingClientRect();
889 PixelZoomer._percentX = (e.clientX - targetLocation.left) / targetLocation.width;
890 PixelZoomer._percentY = (e.clientY - targetLocation.top) / targetLocation.height;
893 if (PixelZoomer.showOnDelay) {
894 PixelZoomer._mouseMoveTimeout = setTimeout(function() {
895 PixelZoomer._createContainer(e);
900 PixelZoomer._createContainer(e);
904 PixelZoomer._drawAll();
907 document.addEventListener('mousemove', PixelZoomer.handleMouseMove, false);
908 document.addEventListener('mouseout', PixelZoomer.handleMouseOut, false);
910 var TestNavigator = {};
912 TestNavigator.reset = function() {
913 TestNavigator.currentTestIndex = -1;
914 TestNavigator.flaggedTests = {};
917 TestNavigator.handleKeyEvent = function(event)
919 if (event.metaKey || event.shiftKey || event.ctrlKey)
922 switch (String.fromCharCode(event.charCode)) {
924 TestNavigator._scrollToFirstTest();
927 TestNavigator._scrollToNextTest();
930 TestNavigator._scrollToPreviousTest();
933 TestNavigator._scrollToLastTest();
936 TestNavigator._expandCurrentTest();
939 TestNavigator._collapseCurrentTest();
942 TestNavigator._toggleCurrentTest();
945 TestNavigator._toggleCurrentTestFlagged();
950 TestNavigator._scrollToFirstTest = function()
952 if (TestNavigator._setCurrentTest(0))
953 TestNavigator._scrollToCurrentTest();
956 TestNavigator._scrollToLastTest = function()
958 var links = visibleExpandLinks();
959 if (TestNavigator._setCurrentTest(links.length - 1))
960 TestNavigator._scrollToCurrentTest();
963 TestNavigator._scrollToNextTest = function()
965 if (TestNavigator.currentTestIndex == -1)
966 TestNavigator._scrollToFirstTest();
967 else if (TestNavigator._setCurrentTest(TestNavigator.currentTestIndex + 1))
968 TestNavigator._scrollToCurrentTest();
971 TestNavigator._scrollToPreviousTest = function()
973 if (TestNavigator.currentTestIndex == -1)
974 TestNavigator._scrollToLastTest();
975 else if (TestNavigator._setCurrentTest(TestNavigator.currentTestIndex - 1))
976 TestNavigator._scrollToCurrentTest();
979 TestNavigator._currentTestLink = function()
981 var links = visibleExpandLinks();
982 return links[TestNavigator.currentTestIndex];
985 TestNavigator._expandCurrentTest = function()
987 expandExpectations(TestNavigator._currentTestLink());
990 TestNavigator._collapseCurrentTest = function()
992 collapseExpectations(TestNavigator._currentTestLink());
995 TestNavigator._toggleCurrentTest = function()
997 toggleExpectations(TestNavigator._currentTestLink());
1000 TestNavigator._toggleCurrentTestFlagged = function()
1002 var testLink = parentOfType(TestNavigator._currentTestLink(), 'tbody').querySelector('.test-link');
1003 var testName = testLink.innerText;
1005 if (testLink.classList.contains('flagged')) {
1006 testLink.classList.remove('flagged');
1007 delete TestNavigator.flaggedTests[testName];
1009 testLink.classList.add('flagged');
1010 TestNavigator.flaggedTests[testName] = 1;
1013 TestNavigator.updateFlaggedTests();
1016 TestNavigator.updateFlaggedTests = function()
1018 var flaggedTestTextbox = document.getElementById('flagged-tests');
1019 if (!flaggedTestTextbox) {
1020 var flaggedTestContainer = document.createElement('div');
1021 flaggedTestContainer.id = 'flagged-test-container';
1022 flaggedTestContainer.innerHTML = '<h2>Flagged Tests</h2><pre id="flagged-tests" contentEditable></pre>';
1023 document.body.appendChild(flaggedTestContainer);
1025 flaggedTestTextbox = document.getElementById('flagged-tests');
1028 var flaggedTests = Object.keys(this.flaggedTests);
1029 flaggedTests.sort();
1030 var separator = document.getElementById('use-newlines').checked ? '\n' : ' ';
1031 flaggedTestTextbox.innerHTML = flaggedTests.join(separator);
1032 document.getElementById('flagged-test-container').style.display = flaggedTests.length ? '' : 'none';
1035 TestNavigator._setCurrentTest = function(testIndex)
1037 var links = visibleExpandLinks();
1038 if (testIndex < 0 || testIndex >= links.length)
1041 var currExpandLink = links[TestNavigator.currentTestIndex];
1043 parentOfType(currExpandLink, 'tr').classList.remove('current');
1045 TestNavigator.currentTestIndex = testIndex;
1047 currExpandLink = links[TestNavigator.currentTestIndex];
1048 parentOfType(currExpandLink, 'tr').classList.add('current');
1053 TestNavigator._scrollToCurrentTest = function()
1055 var targetLink = TestNavigator._currentTestLink();
1059 var rowRect = targetLink.getBoundingClientRect();
1060 // rowRect is in client coords (i.e. relative to viewport), so we just want to add its top to the current scroll position.
1061 window.scrollTo(window.scrollX, window.scrollY + rowRect.top - 20);
1064 TestNavigator.onlyShowUnexpectedFailuresChanged = function()
1066 var currentTestLink = document.querySelector('.current .expand-button-text');
1067 if (!currentTestLink)
1070 // If our currentTest became hidden, reset the currentTestIndex.
1071 if (onlyShowUnexpectedFailures() && parentOfType(currentTestLink, 'tbody').classList.contains('expected'))
1072 TestNavigator._scrollToFirstTest();
1074 // Recompute TestNavigator.currentTestIndex
1075 var links = visibleExpandLinks();
1076 TestNavigator.currentTestIndex = links.indexOf(currentTestLink);
1077 window.console.log('TestNavigator.currentTestIndex is ', TestNavigator.currentTestIndex)
1081 document.addEventListener('keypress', TestNavigator.handleKeyEvent, false);
1084 function onlyShowUnexpectedFailures()
1086 return document.getElementById('unexpected-results').checked;
1089 function handleUnexpectedResultsChange()
1091 OptionWriter.save();
1092 updateExpectedFailures();
1095 function updateExpectedFailures()
1097 document.getElementById('unexpected-style').textContent = onlyShowUnexpectedFailures() ?
1098 '.expected { display: none; }' : '';
1100 TestNavigator.onlyShowUnexpectedFailuresChanged();
1103 var OptionWriter = {};
1105 OptionWriter._key = 'run-webkit-tests-options';
1107 OptionWriter.save = function()
1109 var options = document.querySelectorAll('label input');
1111 for (var i = 0, len = options.length; i < len; i++) {
1112 var option = options[i];
1113 data[option.id] = option.checked;
1115 localStorage.setItem(OptionWriter._key, JSON.stringify(data));
1118 OptionWriter.apply = function()
1120 var json = localStorage.getItem(OptionWriter._key);
1126 var data = JSON.parse(json);
1127 for (var id in data) {
1128 var input = document.getElementById(id);
1130 input.checked = data[id];
1135 function updateAllOptions()
1137 forEach(document.querySelectorAll('#options-menu input'), function(input) { input.onchange(); });
1140 function handleToggleUseNewlines()
1142 OptionWriter.save();
1143 TestNavigator.updateFlaggedTests();
1146 function handleToggleImagesChange()
1148 OptionWriter.save();
1149 updateTogglingImages();
1152 function updateTogglingImages()
1154 var shouldToggle = document.getElementById('toggle-images').checked;
1155 globalState().shouldToggleImages = shouldToggle;
1158 forEach(document.querySelectorAll('table:not(#missing-table) tbody:not([mismatchreftest]) a[href$=".png"]'), convertToTogglingHandler(function(prefix) {
1159 return resultLink(prefix, '-diffs.html', 'images');
1161 forEach(document.querySelectorAll('table:not(#missing-table) tbody:not([mismatchreftest]) img[src$=".png"]'), convertToTogglingHandler(togglingImage));
1163 forEach(document.querySelectorAll('a[href$="-diffs.html"]'), convertToNonTogglingHandler(resultLink));
1164 forEach(document.querySelectorAll('.animatedImage'), convertToNonTogglingHandler(function (absolutePrefix, suffix) {
1165 return resultIframe(absolutePrefix + suffix);
1169 updateImageTogglingTimer();
1172 function getResultContainer(node)
1174 return (node.tagName == 'IMG') ? parentOfType(node, '.result-container') : node;
1177 function convertToTogglingHandler(togglingImageFunction)
1179 return function(node) {
1180 var url = (node.tagName == 'IMG') ? node.src : node.href;
1181 if (url.match('-expected.png$'))
1182 remove(getResultContainer(node));
1183 else if (url.match('-actual.png$')) {
1184 var name = parentOfType(node, 'tbody').querySelector('.test-link').textContent;
1185 getResultContainer(node).outerHTML = togglingImageFunction(stripExtension(name));
1190 function convertToNonTogglingHandler(resultFunction)
1192 return function(node) {
1193 var prefix = node.getAttribute('data-prefix');
1194 getResultContainer(node).outerHTML = resultFunction(prefix, '-expected.png', 'expected') + resultFunction(prefix, '-actual.png', 'actual');
1198 function toggleOptionsMenu()
1200 var menu = document.getElementById('options-menu');
1201 menu.className = (menu.className == 'hidden-menu') ? '' : 'hidden-menu';
1204 function handleMouseDown(e)
1206 if (!parentOfType(e.target, '#options-menu') && e.target.id != 'options-link')
1207 document.getElementById('options-menu').className = 'hidden-menu';
1210 document.addEventListener('mousedown', handleMouseDown, false);
1212 function failingTestsTable(tests, title, id)
1217 var numberofUnexpectedFailures = 0;
1218 var tableRowHtml = '';
1219 for (var i = 0; i < tests.length; i++){
1220 tableRowHtml += tableRow(tests[i]);
1221 if (!tests[i].isExpected)
1222 numberofUnexpectedFailures++;
1225 var header = '<div';
1226 if (!hasUnexpected(tests))
1227 header += ' class=expected';
1229 header += '><h1>' + title + ' (' + numberofUnexpectedFailures + '):</h1>' +
1230 '<table id="' + id + '"><thead><tr>' +
1232 '<th id="text-results-header">results</th>' +
1233 '<th id="image-results-header">image results</th>';
1235 if (globalState().results.uses_expectations_file)
1236 header += '<th>actual failure</th><th>expected failure</th>';
1238 if (id == 'flaky-tests-table')
1239 header += '<th>failures</th>';
1241 header += '</tr></thead>';
1244 return header + tableRowHtml + '</table></div>';
1248 function generatePage()
1250 forEachTest(processGlobalStateFor);
1252 var html = '<div id=toolbar>' +
1253 '<div class="note">Use the i, j, k and l keys to navigate, e, c to expand and collapse, and f to flag</div>' +
1254 '<a href="javascript:void()" onclick="expandAllExpectations()">expand all</a> ' +
1255 '<a href="javascript:void()" onclick="collapseAllExpectations()">collapse all</a> ' +
1256 '<a href="javascript:void()" id=options-link onclick="toggleOptionsMenu()">options</a>' +
1257 '<div id=options-menu class=hidden-menu>' +
1258 '<label><input id="unexpected-results" type=checkbox checked onchange="handleUnexpectedResultsChange()">Only unexpected results</label>' +
1259 '<label><input id="toggle-images" type=checkbox checked onchange="handleToggleImagesChange()">Toggle images</label>' +
1260 '<label title="Use newlines instead of spaces to separate flagged tests"><input id="use-newlines" type=checkbox checked onchange="handleToggleUseNewlines()">Use newlines</input>' +
1263 if (globalState().results.interrupted)
1264 html += "<p class='stopped-running-early-message'>Testing exited early.</p>"
1266 if (globalState().crashTests.length)
1267 html += testList(globalState().crashTests, 'Tests that crashed', 'crash-tests-table');
1269 html += failingTestsTable(globalState().nonFlakyFailingTests,
1270 'Tests where results did not match expected results', 'results-table');
1272 html += failingTestsTable(globalState().missingResults,
1273 'Tests that had no expected results (probably new)', 'missing-table');
1275 html += failingTestsTable(globalState().flakyTests,
1276 'Flaky tests (failed the first run and got a different result on retry)', 'flaky-tests-table');
1278 if (globalState().timeoutTests.length)
1279 html += testList(globalState().timeoutTests, 'Tests that timed out', 'timeout-tests-table');
1281 if (globalState().testsWithStderr.length)
1282 html += testList(globalState().testsWithStderr, 'Tests that had stderr output', 'stderr-table');
1284 if (globalState().results.uses_expectations_file && globalState().unexpectedPassTests.length)
1285 html += testList(globalState().unexpectedPassTests, 'Tests expected to fail but passed', 'passes-table');
1287 if (globalState().hasHttpTests) {
1288 html += '<p>httpd access log: <a href="access_log.txt">access_log.txt</a></p>' +
1289 '<p>httpd error log: <a href="error_log.txt">error_log.txt</a></p>';
1292 document.body.innerHTML = html;
1294 if (document.getElementById('results-table')) {
1295 document.getElementById('results-table').addEventListener('click', TableSorter.handleClick, false);
1296 TableSorter.sortColumn(0);
1297 if (!globalState().results.uses_expectations_file)
1298 parentOfType(document.getElementById('unexpected-results'), 'label').style.display = 'none';
1299 if (!globalState().hasTextFailures)
1300 document.getElementById('text-results-header').textContent = '';
1301 if (!globalState().hasImageFailures) {
1302 document.getElementById('image-results-header').textContent = '';
1303 parentOfType(document.getElementById('toggle-images'), 'label').style.display = 'none';
1307 TestNavigator.reset();
1308 OptionWriter.apply();
1311 <!-- HACK: when json_results_test.js is included, loading this page runs the tests.
1312 It is not copied to the layout-test-results output directory. -->
1313 <script src="resources/results-test.js"></script>
1314 <body onload="generatePage()"></body>