mirror of https://github.com/tc39/test262.git
445 lines
17 KiB
JavaScript
445 lines
17 KiB
JavaScript
/// 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 = '<tr class="waiting"><td height="29" class="chapterName">Waiting to load test file: ' + path + '</td>';
|
|
appendMsg += '<td width="83"><img src="images/select.png" alt="Select" title="Toggle the selection of this chapter." /></td>';
|
|
appendMsg += '<td width="83"><img src="images/run.png" alt="Run" title="Run this chapter individually." /></td></tr>';
|
|
|
|
$('#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 += '<b>Test </b>';
|
|
innerHTML += '<b>' + test.id + '</b> <br /><br />\n';
|
|
|
|
if (test.description) {
|
|
innerHTML += '<b>Description</b>';
|
|
innerHTML += '<pre>' +
|
|
test.description.replace(/</g, '<').replace(/>/g, '>') +
|
|
' </pre>\n';
|
|
}
|
|
|
|
innerHTML += '<br /><br /><br /><b>Testcase</b>';
|
|
innerHTML += '<pre>' + test.code + '</pre>\n';
|
|
|
|
innerHTML += '<br /><b>Path</b>';
|
|
innerHTML += '<pre>' + test.path + '</pre>';
|
|
innerHTML += '<br /><a href="javascript:void(window.open(\'https://github.com/tc39/test262/raw/master/test'
|
|
innerHTML += test.path.replace("TestCases", "") + '\'));">' + 'GitHub source' + '</a> (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 += '<b>Test </b>';
|
|
innerHTML += '<b>' + test.id + '</b> <br /><br />\n';
|
|
|
|
innerHTML += '<b>Failure</b>';
|
|
innerHTML += '<pre>' + test.error + '</pre>\n';
|
|
|
|
innerHTML += '<br /><br /><b>Testcase</b>';
|
|
innerHTML += '<pre>' + test.code + '</pre>\n';
|
|
|
|
innerHTML += '<br /><br /><b>Broken test?</b>';
|
|
innerHTML += '<p>If you have reason to believe the JavaScript engine being tested<br />\n';
|
|
innerHTML += 'is actually OK and there\'s instead something wrong with the test<br />\n';
|
|
innerHTML += 'itself, please <a href="' + bugTemplate + '" onclick="window.moveTo(0,0);window.resizeTo(screen.width, screen.height);">file a bug.</a></p>\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 += '<tbody>';
|
|
appendStr += '<tr class=\"' + altStyle + '\">';
|
|
|
|
appendStr += '<td width=\"20%\">';
|
|
appendStr += "<a class='showSource' href='#" + test.id + "'>";
|
|
appendStr += test.id + "</a>";
|
|
appendStr += '</td>';
|
|
|
|
appendStr += '<td>' + test.description + '</td>';
|
|
|
|
appendStr += '<td align="right">';
|
|
appendStr += '<span class=\"Fail\">' + "<a class='showError' href='#" + test.id + "'>";
|
|
appendStr += 'Fail</a></span></td></tr></tbody>';
|
|
}
|
|
|
|
else if (test.result==="pass") {
|
|
if (! isSiteDebugMode()) { return;}
|
|
appendStr += '<tbody><tr class=\"' + altStyle + '\"><td width=\"20%\">';
|
|
appendStr += "<a class='showSource' href='#" + test.id + "'>";
|
|
appendStr += test.id + "</a>" + '</td><td>' + test.description;
|
|
appendStr += '</td><td align="right"><span class=\"Fail\">';
|
|
appendStr += 'Pass</span></td></tr></tbody>';
|
|
}
|
|
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 = $("<a href='#0' class='setBlack'>Test Sections > </a>");
|
|
link.bind('click', {sectionId: 0}, sectionSelected)
|
|
container.append(link);
|
|
|
|
for(var i = 0; i < sectionChain.length;i++) {
|
|
link = $("<a href='#" + sectionChain[i].id + "' class='setBlack'>" + sectionChain[i].id + ": " + sectionChain[i].name + " > </a>");
|
|
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 = '<testRun>\r\n' +
|
|
'<userAgent>' + window.navigator.userAgent + '</userAgent>\r\n' +
|
|
'<Date>' + dateNow.toDateString() + '</Date>\r\n' +
|
|
'<targetTestSuiteName>ECMAScript Test262 Site</targetTestSuiteName>\r\n' +
|
|
'<targetTestSuiteVersion>' + version + '</targetTestSuiteVersion>\r\n' +
|
|
'<targetTestSuiteDate>' + date + '</targetTestSuiteDate>\r\n' +
|
|
' <Tests>\r\n\r\n';
|
|
|
|
reportWindow = window.open();
|
|
reportWindow.document.writeln("<title>ECMAScript Test262 XML</title>");
|
|
reportWindow.document.write("<textarea id='results' style='width: 100%; height: 800px;'>");
|
|
reportWindow.document.write(xml);
|
|
reportWindow.document.write(globalSection.toXML());
|
|
reportWindow.document.write('</Tests>\r\n</testRun>\r\n</textarea>\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();
|