/// Copyright (c) 2012 Ecma International. All rights reserved. /// This code is governed by the BSD license found in the LICENSE file. /* Handles updating the page with information from the runner. */ function Presenter() { var altStyle = '', logger, date, version, table, backLink, globalSection = new Section(null, "0", STANDARD), currentSection = globalSection, tests = {}, totalTests = 0; var progressBar; TOCFILEPATH = "metadata/" + STANDARD.toLowerCase() + "-toc.xml"; //**INTERFACE**************************************************************** /* Updates progress with the given test, which should have its results in it as well. */ this.addTestResult = function(test) { tests[test.id] = test; getSectionById(test.id).addTest(test); updateCounts(); //TODO: eventually remove this guard. if(test.result === 'fail') { logResult(test); } } /* Updates the displayed version. */ this.setVersion = function(v) { version = v; $(".targetTestSuiteVersion").text(v); } /* Updates the displayed date. */ this.setDate = function(d) { date = d; $(".targetTestSuiteDate").text(d); } /* Updates the displayed number of tests to run. */ this.setTotalTests = function(tests) { totalTests = tests; $('#testsToRun').text(tests); } /* Write status to the activity bar. */ this.updateStatus = function (str) { this.activityBar.text(str); } /* When starting to load a test, create a table row entry and buttons. Display file path. */ this.setTestWaiting = function(index, path) { var appendMsg = 'Waiting to load test file: ' + path + ''; appendMsg += 'Select'; appendMsg += 'Run'; $('#chapterSelector table').append(appendMsg); // Find the table row var tr = $('#chapterSelector table tr').filter(":last-child"); // Attach click listeners to the buttons tr.find("img").filter('[alt="Select"]').bind("click", {tr: tr, index: index}, function(event) { controller.toggleSelection(event.data.index); // Deselect row if(event.data.tr.hasClass("selectedChapter")) { event.data.tr.removeClass("selectedChapter"); event.data.tr.find('img').filter('[alt="Selected"]').attr({ src: 'images/select.png', alt: 'Select' }); } // Select row else { event.data.tr.addClass("selectedChapter"); event.data.tr.find('img').filter('[alt="Select"]').attr({ src: 'images/selected.png', alt: 'Selected' }); } }); } this.setTestLoading = function(index, path) { var tr = $('#chapterSelector table tr').filter(":nth-child(" + (index+1) + ")"); tr.removeClass("waiting"); tr.addClass("loading"); tr.find(":first-child").html("Loading test file: " + path); }; /* On test loaded, display the chapter name and the number of tests */ this.setTestLoaded = function(index, name, numTests) { var tr = $('#chapterSelector table tr').filter(":nth-child(" + (index+1) + ")"); tr.removeClass("loading"); tr.find("td").filter(":first-child").html(name + " (" + numTests + " tests)"); } /* Called when the tests finish executing. */ this.finished = function(elapsed) { progressBar.find(".text").html("Testing complete!"); if (isSiteDebugMode()) { this.activityBar.text('Overall Execution Time: ' + elapsed + ' minutes'); } else { this.activityBar.text(''); } } this.reset = function () { globalSection.reset(); updateCounts(); this.activityBar.text(''); logger.empty(); currentSection = globalSection; renderCurrentSection(); } /* Do some setup tasks. */ this.setup = function() { backLink = $('#backlinkDiv'); backLink.click(goBack); table = $('.results-data-table'); logger = $("#tableLogger"); progressBar = $('#progressbar'); this.activityBar = $('#nextActivity'); $('a.showSource', logger).live("click", openSourceWindow); $('a.showError', logger).live("click", openErrorWindow); $('#ancGenXMLReport').click(createXMLReportWindow); } /* Refresh display of the report */ this.refresh = function() { renderCurrentSection(); } /* The state machine for the button display. */ this.setState = function(state) { // Hide all the buttons $('.progressBarButtons img').addClass("hide"); // Only show what is needed. if(state == 'loading') { $('#btnRunAll').removeClass('hide'); $('#btnRunSelected').removeClass('hide'); } else if(state == 'paused') { $('#btnResume').removeClass('hide'); $('#btnReset').removeClass('hide'); } else if(state == 'running') { $('#btnPause').removeClass('hide'); } else if(state == 'loaded') { $('#btnRunAll').removeClass('hide'); $('#btnRunSelected').removeClass('hide'); } }; //**IMPLEMENTATION DETAILS*************************************************** /* Renders the current section into the report window. */ function renderCurrentSection() { renderBreadcrumbs(); if(globalSection.totalTests === 0) { $('#resultMessage').show(); } else { $('#resultMessage').hide(); } $('.totalCases').text(currentSection.totalTests); $('.passedCases').text(currentSection.totalPassed); $('.failedCases').text(currentSection.totalFailed); $('#failedToLoadCounterDetails').text(currentSection.totalFailedToLoad); table.empty(); table.append(currentSection.toHTML()); // Observe section selection and show source links $('a.section', table).click(sectionSelected); $('a.showSource', table).click(openSourceWindow); } /* Opens a window with a test's source code. */ function openSourceWindow(e) { var test = tests[e.target.href.match(/#(.+)$/)[1]], popWnd = window.open("", "", "scrollbars=1, resizable=1"), innerHTML = ''; innerHTML += 'Test '; innerHTML += '' + test.id + '

\n'; if (test.description) { innerHTML += 'Description'; innerHTML += '
' +
                test.description.replace(//g, '>') +
                ' 
\n'; } innerHTML += '


Testcase'; innerHTML += '
' + test.code + '
\n'; innerHTML += '
Path'; innerHTML += '
' + test.path + '
'; innerHTML += '
' + 'GitHub source' + ' (might be newer than the testcase source shown above)\n' popWnd.document.write(innerHTML); } /* Opens a window with a test's failure message. */ function openErrorWindow(e) { var test = tests[e.target.href.match(/#(.+)$/)[1]], popWnd = window.open("", "", "scrollbars=1, resizable=1"), innerHTML = ''; var bugDetails = ""; bugDetails += "DESCRIPTION\n*Please insert your description here!*\n\n"; bugDetails += "------------------\n"; bugDetails += "TEST: " + test.path + "\n"; bugDetails += "SOURCE: https://github.com/tc39/test262/raw/master/test" + test.path.replace("TestCases", "") + "\n"; bugDetails += "TEST SUITE DATE: " + date + "\n"; bugDetails += "PLATFORM: " + navigator.userAgent + "\n"; bugDetails += "ERROR: " + test.error + "\n\n"; var bugTemplate = 'https://bugs.ecmascript.org/enter_bug.cgi?product=Test262&bug_severity=normal&component=Tests&short_desc='; bugTemplate += encodeURIComponent('Invalid test? ' + test.id) + "&comment="; bugTemplate += encodeURIComponent(bugDetails); innerHTML += 'Test '; innerHTML += '' + test.id + '

\n'; innerHTML += 'Failure'; innerHTML += '
' + test.error + '
\n'; innerHTML += '

Testcase'; innerHTML += '
' + test.code + '
\n'; innerHTML += '

Broken test?'; innerHTML += '

If you have reason to believe the JavaScript engine being tested
\n'; innerHTML += 'is actually OK and there\'s instead something wrong with the test
\n'; innerHTML += 'itself, please file a bug.

\n' popWnd.document.write(innerHTML); } /* Returns the section object for the specified section id * (eg. "7.1" or "15.4.4.12"). */ function getSectionById(id) { if(id == 0) return globalSection; var match = id.match(/\d+|[A-F](?=\.)/g); var section = globalSection; if (match === null) return section; for(var i = 0; i < match.length; i++) { if(typeof section.subsections[match[i]] !== "undefined") { section = section.subsections[match[i]]; } else { break; } } return section; } /* Update the page with current status */ function updateCounts() { $('#Pass').text(globalSection.totalPassed); $('#Fail').text(globalSection.totalFailed); $('#totalCounter').text(globalSection.totalTests); $('#failedToLoadCounter1').text(globalSection.totalFailedToLoad); $('#failedToLoadCounter').text(globalSection.totalFailedToLoad); progressBar.reportprogress(globalSection.totalTests, totalTests); } /* Append a result to the run page's result log. */ function logResult(test) { var appendStr = ""; altStyle = (altStyle !== ' ') ? ' ' : 'alternate'; if (test.result==="fail") { appendStr += ''; appendStr += ''; appendStr += ''; appendStr += ""; appendStr += test.id + ""; appendStr += ''; appendStr += '' + test.description + ''; appendStr += ''; appendStr += '' + ""; appendStr += 'Fail'; } else if (test.result==="pass") { if (! isSiteDebugMode()) { return;} appendStr += ''; appendStr += ""; appendStr += test.id + "" + '' + test.description; appendStr += ''; appendStr += 'Pass'; } else { throw "Result for '" + test.id + "' must either be 'pass' or 'fail', not '" + test.result + "'!"; } logger.append(appendStr); logger.parent().attr("scrollTop", logger.parent().attr("scrollHeight")); } //************************************************************************* /* Go back to the previous section */ function goBack(e) { e.preventDefault(); if(currentSection === globalSection) return; currentSection = currentSection.parentSection; // Since users click directly on sub-chapters of the main chapters, don't go back to main // chapters. if(currentSection.parentSection === globalSection) currentSection = globalSection; renderCurrentSection(); } /* Load the table of contents xml to populate the sections. */ function loadSections() { var sectionsLoader = new XMLHttpRequest(); sectionsLoader.open("GET", TOCFILEPATH, false); sectionsLoader.send(); var xmlDoc = sectionsLoader.responseXML; var nodes = xmlDoc.documentElement.childNodes; addSectionsFromXML(nodes, globalSection); } /* Recursively parses the TOC xml, producing nested sections. */ function addSectionsFromXML(nodes, parentSection){ var subsection; for (var i = 0; i < nodes.length; i++) { if (nodes[i].nodeName === "sec") { subsection = new Section(parentSection, nodes[i].getAttribute('id'), nodes[i].getAttribute('name')); parentSection.subsections[subsection.id.match(/\d+$|[A-F]$/)] = subsection; addSectionsFromXML(nodes[i].childNodes, subsection); } } } /* Renders the breadcrumbs for report navigation. */ function renderBreadcrumbs() { var container = $('div.crumbContainer div.crumbs'); var sectionChain = []; var current = currentSection; // Walk backwards until we reach the global section. while(current !== globalSection && current.parentSection !== globalSection) { sectionChain.push(current); current = current.parentSection; } // Reverse the array since we want to print earlier sections first. sectionChain.reverse(); // Empty any existing breadcrumbs. container.empty(); // Static first link to go back to the root. var link = $("Test Sections > "); link.bind('click', {sectionId: 0}, sectionSelected) container.append(link); for(var i = 0; i < sectionChain.length;i++) { link = $("" + sectionChain[i].id + ": " + sectionChain[i].name + " > "); link.bind('click', sectionSelected) container.append(link); } // If we can go back, show the back link. if(sectionChain.length > 0) { backLink.show(); } else { backLink.hide(); } }; /* Pops up a window with an xml dump of the results of a test. */ function createXMLReportWindow() { var reportWindow; //window that will output the xml data var xmlData; //array instead of string concatenation var dateNow; var xml; // stop condition of for loop stored in a local variable to improve performance dateNow = new Date(); xml = '\r\n' + '' + window.navigator.userAgent + '\r\n' + '' + dateNow.toDateString() + '\r\n' + 'ECMAScript Test262 Site\r\n' + '' + version + '\r\n' + '' + date + '\r\n' + ' \r\n\r\n'; reportWindow = window.open(); reportWindow.document.writeln("ECMAScript Test262 XML"); reportWindow.document.write("\r\n'); reportWindow.document.close(); } /* Callback for when the user clicks on a section in the report table. */ function sectionSelected(e) { e.preventDefault(); currentSection = getSectionById(e.target.href.match(/#(.+)$/)[1]); renderCurrentSection(); table.attr("scrollTop", 0); }; //************************************************************************* // Load the sections. loadSections(); } var presenter = new Presenter();