1903 lines
50 KiB
JavaScript
1903 lines
50 KiB
JavaScript
/*
|
|
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
// requires jQuery
|
|
|
|
const kTestSuiteVersion = '20101001';
|
|
const kTestSuiteHome = '../' + kTestSuiteVersion + '/';
|
|
const kTestInfoDataFile = 'testinfo.data';
|
|
|
|
const kChapterData = [
|
|
{
|
|
'file' : 'about.html',
|
|
'title' : 'About the CSS 2.1 Specification',
|
|
},
|
|
{
|
|
'file' : 'intro.html',
|
|
'title' : 'Introduction to CSS 2.1',
|
|
},
|
|
{
|
|
'file' : 'conform.html',
|
|
'title' : 'Conformance: Requirements and Recommendations',
|
|
},
|
|
{
|
|
'file' : "syndata.html",
|
|
'title' : 'Syntax and basic data types',
|
|
},
|
|
{
|
|
'file' : 'selector.html' ,
|
|
'title' : 'Selectors',
|
|
},
|
|
{
|
|
'file' : 'cascade.html',
|
|
'title' : 'Assigning property values, Cascading, and Inheritance',
|
|
},
|
|
{
|
|
'file' : 'media.html',
|
|
'title' : 'Media types',
|
|
},
|
|
{
|
|
'file' : 'box.html' ,
|
|
'title' : 'Box model',
|
|
},
|
|
{
|
|
'file' : 'visuren.html',
|
|
'title' : 'Visual formatting model',
|
|
},
|
|
{
|
|
'file' :'visudet.html',
|
|
'title' : 'Visual formatting model details',
|
|
},
|
|
{
|
|
'file' : 'visufx.html',
|
|
'title' : 'Visual effects',
|
|
},
|
|
{
|
|
'file' : 'generate.html',
|
|
'title' : 'Generated content, automatic numbering, and lists',
|
|
},
|
|
{
|
|
'file' : 'page.html',
|
|
'title' : 'Paged media',
|
|
},
|
|
{
|
|
'file' : 'colors.html',
|
|
'title' : 'Colors and Backgrounds',
|
|
},
|
|
{
|
|
'file' : 'fonts.html',
|
|
'title' : 'Fonts',
|
|
},
|
|
{
|
|
'file' : 'text.html',
|
|
'title' : 'Text',
|
|
},
|
|
{
|
|
'file' : 'tables.html',
|
|
'title' : 'Tables',
|
|
},
|
|
{
|
|
'file' : 'ui.html',
|
|
'title' : 'User interface',
|
|
},
|
|
{
|
|
'file' : 'aural.html',
|
|
'title' : 'Appendix A. Aural style sheets',
|
|
},
|
|
{
|
|
'file' : 'refs.html',
|
|
'title' : 'Appendix B. Bibliography',
|
|
},
|
|
{
|
|
'file' : 'changes.html',
|
|
'title' : 'Appendix C. Changes',
|
|
},
|
|
{
|
|
'file' : 'sample.html',
|
|
'title' : 'Appendix D. Default style sheet for HTML 4',
|
|
},
|
|
{
|
|
'file' : 'zindex.html',
|
|
'title' : 'Appendix E. Elaborate description of Stacking Contexts',
|
|
},
|
|
{
|
|
'file' : 'propidx.html',
|
|
'title' : 'Appendix F. Full property table',
|
|
},
|
|
{
|
|
'file' : 'grammar.html',
|
|
'title' : 'Appendix G. Grammar of CSS',
|
|
},
|
|
{
|
|
'file' : 'other.html',
|
|
'title' : 'Other',
|
|
},
|
|
];
|
|
|
|
|
|
const kHTML4Data = {
|
|
'path' : 'html4',
|
|
'suffix' : '.htm'
|
|
};
|
|
|
|
const kXHTML1Data = {
|
|
'path' : 'xhtml1',
|
|
'suffix' : '.xht'
|
|
};
|
|
|
|
// Results popup
|
|
const kResultsSelector = [
|
|
{
|
|
'name': 'All Tests',
|
|
'handler' : function(self) { self.showResultsForAllTests(); },
|
|
'exporter' : function(self) { self.exportResultsForAllTests(); }
|
|
},
|
|
{
|
|
'name': 'Completed Tests',
|
|
'handler' : function(self) { self.showResultsForCompletedTests(); },
|
|
'exporter' : function(self) { self.exportResultsForCompletedTests(); }
|
|
},
|
|
{
|
|
'name': 'Passing Tests',
|
|
'handler' : function(self) { self.showResultsForTestsWithStatus('pass'); },
|
|
'exporter' : function(self) { self.exportResultsForTestsWithStatus('pass'); }
|
|
},
|
|
{
|
|
'name': 'Failing Tests',
|
|
'handler' : function(self) { self.showResultsForTestsWithStatus('fail'); },
|
|
'exporter' : function(self) { self.exportResultsForTestsWithStatus('fail'); }
|
|
},
|
|
{
|
|
'name': 'Skipped Tests',
|
|
'handler' : function(self) { self.showResultsForTestsWithStatus('skipped'); },
|
|
'exporter' : function(self) { self.exportResultsForTestsWithStatus('skipped'); }
|
|
},
|
|
{
|
|
'name': 'Invalid Tests',
|
|
'handler' : function(self) { self.showResultsForTestsWithStatus('invalid'); },
|
|
'exporter' : function(self) { self.exportResultsForTestsWithStatus('invalid'); }
|
|
},
|
|
{
|
|
'name': 'Tests where HTML4 and XHTML1 results differ',
|
|
'handler' : function(self) { self.showResultsForTestsWithMismatchedResults(); },
|
|
'exporter' : function(self) { self.exportResultsForTestsWithMismatchedResults(); }
|
|
},
|
|
{
|
|
'name': 'Tests Not Run',
|
|
'handler' : function(self) { self.showResultsForTestsNotRun(); },
|
|
'exporter' : function(self) { self.exportResultsForTestsNotRun(); }
|
|
}
|
|
];
|
|
|
|
function Test(testInfoLine)
|
|
{
|
|
var fields = testInfoLine.split('\t');
|
|
|
|
this.id = fields[0];
|
|
this.reference = fields[1];
|
|
this.title = fields[2];
|
|
this.flags = fields[3];
|
|
this.links = fields[4];
|
|
this.assertion = fields[5];
|
|
|
|
this.paged = false;
|
|
this.testHTML = true;
|
|
this.testXHTML = true;
|
|
|
|
if (this.flags) {
|
|
this.paged = this.flags.indexOf('paged') != -1;
|
|
|
|
if (this.flags.indexOf('nonHTML') != -1)
|
|
this.testHTML = false;
|
|
|
|
if (this.flags.indexOf('HTMLonly') != -1)
|
|
this.testXHTML = false;
|
|
}
|
|
|
|
this.completedHTML = false; // true if this test has a result (pass, fail or skip)
|
|
this.completedXHTML = false; // true if this test has a result (pass, fail or skip)
|
|
|
|
this.statusHTML = '';
|
|
this.statusXHTML = '';
|
|
|
|
if (!this.links)
|
|
this.links = "other.html"
|
|
}
|
|
|
|
Test.prototype.runForFormat = function(format)
|
|
{
|
|
if (format == 'html4')
|
|
return this.testHTML;
|
|
|
|
if (format == 'xhtml1')
|
|
return this.testXHTML;
|
|
|
|
return true;
|
|
}
|
|
|
|
Test.prototype.completedForFormat = function(format)
|
|
{
|
|
if (format == 'html4')
|
|
return this.completedHTML;
|
|
|
|
if (format == 'xhtml1')
|
|
return this.completedXHTML;
|
|
|
|
return true;
|
|
}
|
|
|
|
Test.prototype.statusForFormat = function(format)
|
|
{
|
|
if (format == 'html4')
|
|
return this.statusHTML;
|
|
|
|
if (format == 'xhtml1')
|
|
return this.statusXHTML;
|
|
|
|
return true;
|
|
}
|
|
|
|
function ChapterSection(link)
|
|
{
|
|
var result= link.match(/^([.\w]+)(#.+)?$/);
|
|
if (result != null) {
|
|
this.file = result[1];
|
|
this.anchor = result[2];
|
|
}
|
|
|
|
this.testCountHTML = 0;
|
|
this.testCountXHTML = 0;
|
|
|
|
this.tests = [];
|
|
}
|
|
|
|
ChapterSection.prototype.countTests = function()
|
|
{
|
|
this.testCountHTML = 0;
|
|
this.testCountXHTML = 0;
|
|
|
|
for (var i = 0; i < this.tests.length; ++i) {
|
|
var currTest = this.tests[i];
|
|
|
|
if (currTest.testHTML)
|
|
++this.testCountHTML;
|
|
|
|
if (currTest.testXHTML)
|
|
++this.testCountXHTML;
|
|
}
|
|
}
|
|
|
|
function Chapter(chapterInfo)
|
|
{
|
|
this.file = chapterInfo.file;
|
|
this.title = chapterInfo.title;
|
|
this.testCountHTML = 0;
|
|
this.testCountXHTML = 0;
|
|
this.sections = []; // array of ChapterSection
|
|
}
|
|
|
|
Chapter.prototype.description = function(format)
|
|
{
|
|
|
|
|
|
return this.title + ' (' + this.testCount(format) + ' tests, ' + this.untestedCount(format) + ' untested)';
|
|
}
|
|
|
|
Chapter.prototype.countTests = function()
|
|
{
|
|
this.testCountHTML = 0;
|
|
this.testCountXHTML = 0;
|
|
|
|
for (var i = 0; i < this.sections.length; ++i) {
|
|
var currSection = this.sections[i];
|
|
|
|
currSection.countTests();
|
|
|
|
this.testCountHTML += currSection.testCountHTML;
|
|
this.testCountXHTML += currSection.testCountXHTML;
|
|
}
|
|
}
|
|
|
|
Chapter.prototype.testCount = function(format)
|
|
{
|
|
if (format == 'html4')
|
|
return this.testCountHTML;
|
|
|
|
if (format == 'xhtml1')
|
|
return this.testCountXHTML;
|
|
|
|
return 0;
|
|
}
|
|
|
|
Chapter.prototype.untestedCount = function(format)
|
|
{
|
|
var completedProperty = format == 'html4' ? 'completedHTML' : 'completedXHTML';
|
|
|
|
var count = 0;
|
|
for (var i = 0; i < this.sections.length; ++i) {
|
|
var currSection = this.sections[i];
|
|
for (var j = 0; j < currSection.tests.length; ++j) {
|
|
count += currSection.tests[j].completedForFormat(format) ? 0 : 1;
|
|
}
|
|
}
|
|
return count;
|
|
|
|
}
|
|
|
|
// Utils
|
|
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }
|
|
|
|
function TestSuite()
|
|
{
|
|
this.chapterSections = {}; // map of links to ChapterSections
|
|
this.tests = {}; // map of test id to test info
|
|
|
|
this.chapters = {}; // map of file name to chapter
|
|
this.currentChapter = null;
|
|
|
|
this.currentChapterTests = []; // array of tests for the current chapter.
|
|
this.currChapterTestIndex = -1; // index of test in the current chapter
|
|
|
|
this.format = '';
|
|
this.formatChanged('html4');
|
|
|
|
this.testInfoLoaded = false;
|
|
|
|
this.populatingDatabase = false;
|
|
|
|
var testInfoPath = kTestSuiteHome + kTestInfoDataFile;
|
|
this.loadTestInfo(testInfoPath);
|
|
}
|
|
|
|
TestSuite.prototype.loadTestInfo = function(testInfoPath)
|
|
{
|
|
var _self = this;
|
|
this.asyncLoad(testInfoPath, 'data', function(data, status) {
|
|
_self.testInfoDataLoaded(data, status);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.testInfoDataLoaded = function(data, status)
|
|
{
|
|
if (status != 'success') {
|
|
alert("Failed to load testinfo.data. Database of tests will not be initialized.");
|
|
return;
|
|
}
|
|
|
|
this.parseTests(data);
|
|
this.buildChapters();
|
|
|
|
this.testInfoLoaded = true;
|
|
|
|
this.fillChapterPopup();
|
|
|
|
this.initializeControls();
|
|
|
|
this.openDatabase();
|
|
}
|
|
|
|
TestSuite.prototype.parseTests = function(data)
|
|
{
|
|
var lines = data.split('\n');
|
|
|
|
// First line is column labels
|
|
for (var i = 1; i < lines.length; ++i) {
|
|
var test = new Test(lines[i]);
|
|
if (test.id.length > 0)
|
|
this.tests[test.id] = test;
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.buildChapters = function()
|
|
{
|
|
for (var testID in this.tests) {
|
|
var currTest = this.tests[testID];
|
|
|
|
// FIXME: tests with more than one link will be presented to the user
|
|
// twice. Be smarter about avoiding this.
|
|
var testLinks = currTest.links.split(',');
|
|
for (var i = 0; i < testLinks.length; ++i) {
|
|
var link = testLinks[i];
|
|
var section = this.chapterSections[link];
|
|
if (!section) {
|
|
section = new ChapterSection(link);
|
|
this.chapterSections[link] = section;
|
|
}
|
|
|
|
section.tests.push(currTest);
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < kChapterData.length; ++i) {
|
|
var chapter = new Chapter(kChapterData[i]);
|
|
chapter.index = i;
|
|
this.chapters[chapter.file] = chapter;
|
|
}
|
|
|
|
for (var sectionName in this.chapterSections) {
|
|
var section = this.chapterSections[sectionName];
|
|
|
|
var file = section.file;
|
|
var chapter = this.chapters[file];
|
|
if (!chapter)
|
|
window.console.log('failed to find chapter ' + file + ' in chapter data.');
|
|
chapter.sections.push(section);
|
|
}
|
|
|
|
for (var chapterName in this.chapters) {
|
|
var currChapter = this.chapters[chapterName];
|
|
currChapter.sections.sort();
|
|
currChapter.countTests();
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.indexOfChapter = function(chapter)
|
|
{
|
|
for (var i = 0; i < kChapterData.length; ++i) {
|
|
if (kChapterData[i].file == chapter.file)
|
|
return i;
|
|
}
|
|
|
|
window.console.log('indexOfChapter for ' + chapter.file + ' failed');
|
|
return -1;
|
|
}
|
|
|
|
TestSuite.prototype.chapterAtIndex = function(index)
|
|
{
|
|
if (index < 0 || index >= kChapterData.length)
|
|
return null;
|
|
|
|
return this.chapters[kChapterData[index].file];
|
|
}
|
|
|
|
TestSuite.prototype.fillChapterPopup = function()
|
|
{
|
|
var select = document.getElementById('chapters')
|
|
select.innerHTML = ''; // Remove all children.
|
|
|
|
for (var i = 0; i < kChapterData.length; ++i) {
|
|
var chapterData = kChapterData[i];
|
|
var chapter = this.chapters[chapterData.file];
|
|
|
|
var option = document.createElement('option');
|
|
option.innerText = chapter.description(this.format);
|
|
option._chapter = chapter;
|
|
|
|
select.appendChild(option);
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.updateChapterPopup = function()
|
|
{
|
|
var select = document.getElementById('chapters')
|
|
var currOption = select.firstChild;
|
|
|
|
for (var i = 0; i < kChapterData.length; ++i) {
|
|
var chapterData = kChapterData[i];
|
|
var chapter = this.chapters[chapterData.file];
|
|
if (!chapter)
|
|
continue;
|
|
currOption.innerText = chapter.description(this.format);
|
|
currOption = currOption.nextSibling;
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.buildTestListForChapter = function(chapter)
|
|
{
|
|
this.currentChapterTests = this.testListForChapter(chapter);
|
|
}
|
|
|
|
TestSuite.prototype.testListForChapter = function(chapter)
|
|
{
|
|
var testList = [];
|
|
|
|
for (var i in chapter.sections) {
|
|
var currSection = chapter.sections[i];
|
|
|
|
for (var j = 0; j < currSection.tests.length; ++j) {
|
|
var currTest = currSection.tests[j];
|
|
if (currTest.runForFormat(this.format))
|
|
testList.push(currTest);
|
|
}
|
|
}
|
|
|
|
// FIXME: test may occur more than once.
|
|
testList.sort(function(a, b) {
|
|
return a.id.localeCompare(b.id);
|
|
});
|
|
|
|
return testList;
|
|
}
|
|
|
|
TestSuite.prototype.initializeControls = function()
|
|
{
|
|
var chaptersPopup = document.getElementById('chapters');
|
|
|
|
var _self = this;
|
|
chaptersPopup.addEventListener('change', function() {
|
|
_self.chapterPopupChanged();
|
|
}, false);
|
|
|
|
this.chapterPopupChanged();
|
|
|
|
// Results popup
|
|
var resultsPopup = document.getElementById('results-popup');
|
|
resultsPopup.innerHTML = '';
|
|
|
|
for (var i = 0; i < kResultsSelector.length; ++i) {
|
|
var option = document.createElement('option');
|
|
option.innerText = kResultsSelector[i].name;
|
|
|
|
resultsPopup.appendChild(option);
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.chapterPopupChanged = function()
|
|
{
|
|
var chaptersPopup = document.getElementById('chapters');
|
|
var selectedChapter = chaptersPopup.options[chaptersPopup.selectedIndex]._chapter;
|
|
|
|
this.setSelectedChapter(selectedChapter);
|
|
}
|
|
|
|
TestSuite.prototype.fillTestList = function()
|
|
{
|
|
var statusProperty = this.format == 'html4' ? 'statusHTML' : 'statusXHTML';
|
|
|
|
var testList = document.getElementById('test-list');
|
|
testList.innerHTML = '';
|
|
|
|
for (var i = 0; i < this.currentChapterTests.length; ++i) {
|
|
var currTest = this.currentChapterTests[i];
|
|
|
|
var option = document.createElement('option');
|
|
option.innerText = currTest.id;
|
|
option.className = currTest[statusProperty];
|
|
option._test = currTest;
|
|
testList.appendChild(option);
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.updateTestList = function()
|
|
{
|
|
var statusProperty = this.format == 'html4' ? 'statusHTML' : 'statusXHTML';
|
|
var testList = document.getElementById('test-list');
|
|
|
|
var options = testList.getElementsByTagName('option');
|
|
for (var i = 0; i < options.length; ++i) {
|
|
var currOption = options[i];
|
|
currOption.className = currOption._test[statusProperty];
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.setSelectedChapter = function(chapter)
|
|
{
|
|
this.currentChapter = chapter;
|
|
this.buildTestListForChapter(this.currentChapter);
|
|
this.currChapterTestIndex = -1;
|
|
|
|
this.fillTestList();
|
|
this.goToTestIndex(0);
|
|
|
|
var chaptersPopup = document.getElementById('chapters');
|
|
chaptersPopup.selectedIndex = this.indexOfChapter(chapter);
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.passTest = function()
|
|
{
|
|
this.recordResult(this.currentTestName(), 'pass');
|
|
this.nextTest();
|
|
}
|
|
|
|
TestSuite.prototype.failTest = function()
|
|
{
|
|
this.recordResult(this.currentTestName(), 'fail');
|
|
this.nextTest();
|
|
}
|
|
|
|
TestSuite.prototype.invalidTest = function()
|
|
{
|
|
this.recordResult(this.currentTestName(), 'invalid');
|
|
this.nextTest();
|
|
}
|
|
|
|
TestSuite.prototype.skipTest = function(reason)
|
|
{
|
|
this.recordResult(this.currentTestName(), 'skipped', reason);
|
|
this.nextTest();
|
|
}
|
|
|
|
TestSuite.prototype.nextTest = function()
|
|
{
|
|
if (this.currChapterTestIndex < this.currentChapterTests.length - 1)
|
|
this.goToTestIndex(this.currChapterTestIndex + 1);
|
|
else {
|
|
var currChapterIndex = this.indexOfChapter(this.currentChapter);
|
|
this.goToChapterIndex(currChapterIndex + 1);
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.previousTest = function()
|
|
{
|
|
if (this.currChapterTestIndex > 0)
|
|
this.goToTestIndex(this.currChapterTestIndex - 1);
|
|
else {
|
|
var currChapterIndex = this.indexOfChapter(this.currentChapter);
|
|
if (currChapterIndex > 0)
|
|
this.goToChapterIndex(currChapterIndex - 1);
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.goToNextIncompleteTest = function()
|
|
{
|
|
var completedProperty = this.format == 'html4' ? 'completedHTML' : 'completedXHTML';
|
|
|
|
// Look to the end of this chapter.
|
|
for (var i = this.currChapterTestIndex + 1; i < this.currentChapterTests.length; ++i) {
|
|
if (!this.currentChapterTests[i][completedProperty]) {
|
|
this.goToTestIndex(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Start looking through later chapter
|
|
var currChapterIndex = this.indexOfChapter(this.currentChapter);
|
|
for (var c = currChapterIndex + 1; c < kChapterData.length; ++c) {
|
|
var chapterData = this.chapterAtIndex(c);
|
|
|
|
var testIndex = this.firstIncompleteTestIndex(chapterData);
|
|
if (testIndex != -1) {
|
|
this.goToChapterIndex(c);
|
|
this.goToTestIndex(testIndex);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.firstIncompleteTestIndex = function(chapter)
|
|
{
|
|
var completedProperty = this.format == 'html4' ? 'completedHTML' : 'completedXHTML';
|
|
|
|
var chapterTests = this.testListForChapter(chapter);
|
|
for (var i = 0; i < chapterTests.length; ++i) {
|
|
if (!chapterTests[i][completedProperty])
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.goToTestByName = function(testName)
|
|
{
|
|
var match = testName.match(/^(?:(html4|xhtml1)\/)?([\w-_]+)(\.xht|\.htm)?/);
|
|
if (!match)
|
|
return false;
|
|
|
|
var prefix = match[1];
|
|
var testId = match[2];
|
|
var extension = match[3];
|
|
|
|
var format = this.format;
|
|
if (prefix)
|
|
format = prefix;
|
|
else if (extension) {
|
|
if (extension == kXHTML1Data.suffix)
|
|
format = kXHTML1Data.path;
|
|
else if (extension == kHTML4Data.suffix)
|
|
format = kHTML4Data.path;
|
|
}
|
|
|
|
this.switchToFormat(format);
|
|
|
|
var test = this.tests[testId];
|
|
if (!test)
|
|
return false;
|
|
|
|
// Find the first chapter.
|
|
var links = test.links.split(',');
|
|
if (links.length == 0) {
|
|
window.console.log('test ' + test.id + 'had no links.');
|
|
return false;
|
|
}
|
|
|
|
var firstLink = links[0];
|
|
var result = firstLink.match(/^([.\w]+)(#.+)?$/);
|
|
if (result)
|
|
firstLink = result[1];
|
|
|
|
// Find the chapter and index of the test.
|
|
for (var i = 0; i < kChapterData.length; ++i) {
|
|
var chapterData = kChapterData[i];
|
|
if (chapterData.file == firstLink) {
|
|
|
|
this.goToChapterIndex(i);
|
|
|
|
for (var j = 0; j < this.currentChapterTests.length; ++j) {
|
|
var currTest = this.currentChapterTests[j];
|
|
if (currTest.id == testId) {
|
|
this.goToTestIndex(j);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
TestSuite.prototype.goToTestIndex = function(index)
|
|
{
|
|
if (index >= 0 && index < this.currentChapterTests.length) {
|
|
this.currChapterTestIndex = index;
|
|
this.loadCurrentTest();
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.goToChapterIndex = function(chapterIndex)
|
|
{
|
|
if (chapterIndex >= 0 && chapterIndex < kChapterData.length) {
|
|
var chapterFile = kChapterData[chapterIndex].file;
|
|
this.setSelectedChapter(this.chapters[chapterFile]);
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.currentTestName = function()
|
|
{
|
|
if (this.currChapterTestIndex < 0 || this.currChapterTestIndex >= this.currentChapterTests.length)
|
|
return undefined;
|
|
|
|
return this.currentChapterTests[this.currChapterTestIndex].id;
|
|
}
|
|
|
|
TestSuite.prototype.loadCurrentTest = function()
|
|
{
|
|
var theTest = this.currentChapterTests[this.currChapterTestIndex];
|
|
if (!theTest) {
|
|
this.configureForManualTest();
|
|
this.clearTest();
|
|
return;
|
|
}
|
|
|
|
if (theTest.reference) {
|
|
this.configureForRefTest();
|
|
this.loadRef(theTest);
|
|
} else {
|
|
this.configureForManualTest();
|
|
}
|
|
|
|
this.loadTest(theTest);
|
|
|
|
this.updateProgressLabel();
|
|
|
|
document.getElementById('test-list').selectedIndex = this.currChapterTestIndex;
|
|
}
|
|
|
|
TestSuite.prototype.updateProgressLabel = function()
|
|
{
|
|
document.getElementById('test-index').innerText = this.currChapterTestIndex + 1;
|
|
document.getElementById('chapter-test-count').innerText = this.currentChapterTests.length;
|
|
}
|
|
|
|
TestSuite.prototype.configureForRefTest = function()
|
|
{
|
|
$('#test-content').addClass('with-ref');
|
|
}
|
|
|
|
TestSuite.prototype.configureForManualTest = function()
|
|
{
|
|
$('#test-content').removeClass('with-ref');
|
|
}
|
|
|
|
TestSuite.prototype.loadTest = function(test)
|
|
{
|
|
var iframe = document.getElementById('test-frame');
|
|
iframe.src = 'about:blank';
|
|
|
|
var url = this.urlForTest(test.id);
|
|
window.setTimeout(function() {
|
|
iframe.src = url;
|
|
}, 0);
|
|
|
|
document.getElementById('test-title').innerText = test.title;
|
|
document.getElementById('test-url').innerText = this.pathForTest(test.id);
|
|
document.getElementById('test-assertion').innerText = test.assertion;
|
|
document.getElementById('test-flags').innerText = test.flags;
|
|
|
|
this.processFlags(test);
|
|
}
|
|
|
|
TestSuite.prototype.processFlags = function(test)
|
|
{
|
|
if (test.paged)
|
|
$('#test-content').addClass('print');
|
|
else
|
|
$('#test-content').removeClass('print');
|
|
|
|
var showWarning = false;
|
|
var warning = '';
|
|
if (test.flags.indexOf('font') != -1)
|
|
warning = 'Requires a specific font to be installed.';
|
|
|
|
if (test.flags.indexOf('http') != -1) {
|
|
if (warning != '')
|
|
warning += ' ';
|
|
warning += 'Must be tested over HTTP, with custom HTTP headers.';
|
|
}
|
|
|
|
if (test.paged) {
|
|
if (warning != '')
|
|
warning += ' ';
|
|
warning += 'Test via the browser\'s Print Preview.';
|
|
}
|
|
|
|
document.getElementById('warning').innerText = warning;
|
|
|
|
if (warning.length > 0)
|
|
$('#test-content').addClass('warn');
|
|
else
|
|
$('#test-content').removeClass('warn');
|
|
|
|
}
|
|
|
|
TestSuite.prototype.clearTest = function()
|
|
{
|
|
var iframe = document.getElementById('test-frame');
|
|
iframe.src = 'about:blank';
|
|
|
|
document.getElementById('test-title').innerText = '';
|
|
document.getElementById('test-url').innerText = '';
|
|
document.getElementById('test-assertion').innerText = '';
|
|
document.getElementById('test-flags').innerText = '';
|
|
|
|
$('#test-content').removeClass('print');
|
|
$('#test-content').removeClass('warn');
|
|
document.getElementById('warning').innerText = '';
|
|
}
|
|
|
|
TestSuite.prototype.loadRef = function(test)
|
|
{
|
|
// Suites 20101001 and earlier used .xht refs, even for HTML tests, so strip off
|
|
// the extension and use the same format as the test.
|
|
var ref = test.reference.replace(/(\.xht)?$/, '');
|
|
|
|
var iframe = document.getElementById('ref-frame');
|
|
iframe.src = this.urlForTest(ref);
|
|
}
|
|
|
|
TestSuite.prototype.pathForTest = function(testName)
|
|
{
|
|
var prefix = this.formatInfo.path;
|
|
var suffix = this.formatInfo.suffix;
|
|
|
|
return prefix + '/' + testName + suffix;
|
|
}
|
|
|
|
TestSuite.prototype.urlForTest = function(testName)
|
|
{
|
|
return kTestSuiteHome + this.pathForTest(testName);
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.recordResult = function(testName, resolution, comment)
|
|
{
|
|
if (!testName)
|
|
return;
|
|
|
|
this.beginAppendingOutput();
|
|
this.appendResultToOutput(this.formatInfo, testName, resolution, comment);
|
|
this.endAppendingOutput();
|
|
|
|
if (comment == undefined)
|
|
comment = '';
|
|
|
|
this.storeTestResult(testName, this.format, resolution, comment, navigator.userAgent);
|
|
|
|
var htmlStatus = null;
|
|
var xhtmlStatus = null;
|
|
if (this.format == 'html4')
|
|
htmlStatus = resolution;
|
|
if (this.format == 'xhtml1')
|
|
xhtmlStatus = resolution;
|
|
|
|
this.markTestCompleted(testName, htmlStatus, xhtmlStatus);
|
|
this.updateTestList();
|
|
|
|
this.updateSummaryData();
|
|
this.updateChapterPopup();
|
|
}
|
|
|
|
TestSuite.prototype.beginAppendingOutput = function()
|
|
{
|
|
}
|
|
|
|
TestSuite.prototype.endAppendingOutput = function()
|
|
{
|
|
var output = document.getElementById('output');
|
|
output.scrollTop = output.scrollHeight;
|
|
}
|
|
|
|
TestSuite.prototype.appendResultToOutput = function(formatData, testName, resolution, comment)
|
|
{
|
|
var output = document.getElementById('output');
|
|
|
|
var result = formatData.path + '/' + testName + formatData.suffix + '\t' + resolution;
|
|
if (comment)
|
|
result += '\t(' + comment + ')';
|
|
|
|
var line = document.createElement('p');
|
|
line.className = resolution;
|
|
line.appendChild(document.createTextNode(result));
|
|
output.appendChild(line);
|
|
}
|
|
|
|
TestSuite.prototype.clearOutput = function()
|
|
{
|
|
document.getElementById('output').innerHTML = '';
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.switchToFormat = function(formatString)
|
|
{
|
|
if (formatString == 'html4')
|
|
document.harness.format.html4.checked = true;
|
|
else
|
|
document.harness.format.xhtml1.checked = true;
|
|
|
|
this.formatChanged(formatString);
|
|
}
|
|
|
|
TestSuite.prototype.formatChanged = function(formatString)
|
|
{
|
|
if (this.format == formatString)
|
|
return;
|
|
|
|
this.format = formatString;
|
|
|
|
if (formatString == 'html4')
|
|
this.formatInfo = kHTML4Data;
|
|
else
|
|
this.formatInfo = kXHTML1Data;
|
|
|
|
// try to keep the current test selected
|
|
var selectedTestName;
|
|
if (this.currChapterTestIndex >= 0 && this.currChapterTestIndex < this.currentChapterTests.length)
|
|
selectedTestName = this.currentChapterTests[this.currChapterTestIndex].id;
|
|
|
|
if (this.currentChapter) {
|
|
this.buildTestListForChapter(this.currentChapter);
|
|
this.fillTestList();
|
|
this.goToTestByName(selectedTestName);
|
|
}
|
|
|
|
this.updateChapterPopup();
|
|
this.updateTestList();
|
|
this.updateProgressLabel();
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.asyncLoad = function(url, type, handler)
|
|
{
|
|
$.get(url, handler, type);
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.exportResults = function(resultTypeIndex)
|
|
{
|
|
var resultInfo = kResultsSelector[resultTypeIndex];
|
|
if (!resultInfo)
|
|
return;
|
|
|
|
resultInfo.exporter(this);
|
|
}
|
|
|
|
TestSuite.prototype.exportHeader = function()
|
|
{
|
|
var result = '# Safari 5.0.2' + ' ' + navigator.platform + '\n';
|
|
result += '# ' + navigator.userAgent + '\n';
|
|
result += '# http://test.csswg.org/suites/css2.1/' + kTestSuiteVersion + '/\n';
|
|
result += 'testname\tresult\n';
|
|
|
|
return result;
|
|
}
|
|
|
|
TestSuite.prototype.createExportLine = function(formatData, testName, resolution, comment)
|
|
{
|
|
var result = formatData.path + '/' + testName + '\t' + resolution;
|
|
if (comment)
|
|
result += '\t(' + comment + ')';
|
|
return result;
|
|
}
|
|
|
|
TestSuite.prototype.exportQueryComplete = function(data)
|
|
{
|
|
window.open("data:text/plain," + escape(data))
|
|
}
|
|
|
|
TestSuite.prototype.resultsPopupChanged = function(index)
|
|
{
|
|
var resultInfo = kResultsSelector[index];
|
|
if (!resultInfo)
|
|
return;
|
|
|
|
this.clearOutput();
|
|
resultInfo.handler(this);
|
|
|
|
var enableExport = resultInfo.exporter != undefined;
|
|
document.getElementById('export-button').disabled = !enableExport;
|
|
}
|
|
|
|
/* ------------------------- Import ------------------------------- */
|
|
/*
|
|
Import format is the same as the export format, namely:
|
|
|
|
testname<tab>result
|
|
|
|
with optional trailing <tab>comment.
|
|
|
|
html4/absolute-non-replaced-height-002<tab>pass
|
|
xhtml1/absolute-non-replaced-height-002<tab>?
|
|
|
|
Lines starting with # are ignored.
|
|
The "testname<tab>result" line is ignored.
|
|
*/
|
|
TestSuite.prototype.importResults = function(data)
|
|
{
|
|
var testsToImport = [];
|
|
|
|
var lines = data.split('\n');
|
|
for (var i = 0; i < lines.length; ++i) {
|
|
var currLine = lines[i];
|
|
if (currLine.length == 0 || currLine.charAt(0) == '#')
|
|
continue;
|
|
|
|
var match = currLine.match(/^(html4|xhtml1)\/([\w-_]+)\t([\w?]+)\t?(.+)?$/);
|
|
if (match) {
|
|
var test = { 'id' : match[2] };
|
|
test.format = match[1];
|
|
test.result = match[3];
|
|
test.comment = match[4];
|
|
|
|
if (test.result != '?')
|
|
testsToImport.push(test);
|
|
} else {
|
|
window.console.log('failed to match line \'' + currLine + '\'');
|
|
}
|
|
}
|
|
|
|
this.importTestResults(testsToImport);
|
|
|
|
this.resetTestStatus();
|
|
this.updateSummaryData();
|
|
}
|
|
|
|
|
|
|
|
/* --------------------- Clear Results --------------------------- */
|
|
/*
|
|
Clear results format is either same as the export format, or
|
|
a list of bare test IDs (e.g. absolute-non-replaced-height-001)
|
|
in which case both HTML4 and XHTML1 results are cleared.
|
|
*/
|
|
TestSuite.prototype.clearResults = function(data)
|
|
{
|
|
var testsToClear = [];
|
|
|
|
var lines = data.split('\n');
|
|
for (var i = 0; i < lines.length; ++i) {
|
|
var currLine = lines[i];
|
|
if (currLine.length == 0 || currLine.charAt(0) == '#')
|
|
continue;
|
|
|
|
// Look for format/test with possible extension
|
|
var result = currLine.match(/^((html4|xhtml1)?)\/?([\w-_]+)/);
|
|
if (result) {
|
|
var testId = result[3];
|
|
var format = result[1];
|
|
|
|
var clearHTML = format.length == 0 || format == 'html4';
|
|
var clearXHTML = format.length == 0 || format == 'xhtml1';
|
|
|
|
var result = { 'id' : testId };
|
|
result.clearHTML = clearHTML;
|
|
result.clearXHTML = clearXHTML;
|
|
|
|
testsToClear.push(result);
|
|
} else {
|
|
window.console.log('failed to match line ' + currLine);
|
|
}
|
|
}
|
|
|
|
this.clearTestResults(testsToClear);
|
|
|
|
this.resetTestStatus();
|
|
this.updateSummaryData();
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.exportResultsCompletion = function(exportTests)
|
|
{
|
|
// Lame workaround for ORDER BY not working
|
|
exportTests.sort(function(a, b) {
|
|
return a.test.localeCompare(b.test);
|
|
});
|
|
|
|
var exportLines = [];
|
|
for (var i = 0; i < exportTests.length; ++i) {
|
|
var currTest = exportTests[i];
|
|
if (currTest.html4 != '')
|
|
exportLines.push(currTest.html4);
|
|
if (currTest.xhtml1 != '')
|
|
exportLines.push(currTest.xhtml1);
|
|
}
|
|
|
|
var exportString = this.exportHeader() + exportLines.join('\n');
|
|
this.exportQueryComplete(exportString);
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.showResultsForCompletedTests = function()
|
|
{
|
|
this.beginAppendingOutput();
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForCompletedTests(
|
|
function(item) {
|
|
if (item.hstatus)
|
|
_self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
|
|
|
|
if (item.xstatus)
|
|
_self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
|
|
},
|
|
function() {
|
|
_self.endAppendingOutput();
|
|
}
|
|
);
|
|
}
|
|
|
|
TestSuite.prototype.exportResultsForCompletedTests = function()
|
|
{
|
|
var exportTests = []; // each test will have html and xhtml items on it
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForCompletedTests(
|
|
function(item) {
|
|
var htmlLine = '';
|
|
if (item.hstatus)
|
|
htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus, item.hcomment);
|
|
|
|
var xhtmlLine = '';
|
|
if (item.xstatus)
|
|
xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus, item.xcomment);
|
|
|
|
exportTests.push({
|
|
'test' : item.test,
|
|
'html4' : htmlLine,
|
|
'xhtml1' : xhtmlLine });
|
|
},
|
|
function() {
|
|
_self.exportResultsCompletion(exportTests);
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.showResultsForAllTests = function()
|
|
{
|
|
this.beginAppendingOutput();
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForAllTests('test',
|
|
function(item) {
|
|
_self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
|
|
_self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
|
|
},
|
|
function() {
|
|
_self.endAppendingOutput();
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.exportResultsForAllTests = function()
|
|
{
|
|
var exportTests = [];
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForAllTests('test',
|
|
function(item) {
|
|
var htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus ? item.hstatus : '?', item.hcomment);
|
|
var xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus ? item.xstatus : '?', item.xcomment);
|
|
exportTests.push({
|
|
'test' : item.test,
|
|
'html4' : htmlLine,
|
|
'xhtml1' : xhtmlLine });
|
|
},
|
|
function() {
|
|
_self.exportResultsCompletion(exportTests);
|
|
}
|
|
);
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.showResultsForTestsNotRun = function()
|
|
{
|
|
this.beginAppendingOutput();
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForTestsNotRun(
|
|
function(item) {
|
|
if (!item.hstatus)
|
|
_self.appendResultToOutput(kHTML4Data, item.test, '?', item.hcomment);
|
|
if (!item.xstatus)
|
|
_self.appendResultToOutput(kXHTML1Data, item.test, '?', item.xcomment);
|
|
},
|
|
function() {
|
|
_self.endAppendingOutput();
|
|
}
|
|
);
|
|
}
|
|
|
|
TestSuite.prototype.exportResultsForTestsNotRun = function()
|
|
{
|
|
var exportTests = [];
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForTestsNotRun(
|
|
function(item) {
|
|
var htmlLine = '';
|
|
if (!item.hstatus)
|
|
htmlLine= _self.createExportLine(kHTML4Data, item.test, '?', item.hcomment);
|
|
|
|
var xhtmlLine = '';
|
|
if (!item.xstatus)
|
|
xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, '?', item.xcomment);
|
|
|
|
exportTests.push({
|
|
'test' : item.test,
|
|
'html4' : htmlLine,
|
|
'xhtml1' : xhtmlLine });
|
|
},
|
|
function() {
|
|
_self.exportResultsCompletion(exportTests);
|
|
}
|
|
);
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.showResultsForTestsWithStatus = function(status)
|
|
{
|
|
this.beginAppendingOutput();
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForTestsWithStatus(status,
|
|
function(item) {
|
|
if (item.hstatus == status)
|
|
_self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
|
|
if (item.xstatus == status)
|
|
_self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
|
|
},
|
|
function() {
|
|
_self.endAppendingOutput();
|
|
}
|
|
);
|
|
}
|
|
|
|
TestSuite.prototype.exportResultsForTestsWithStatus = function(status)
|
|
{
|
|
var exportTests = [];
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForTestsWithStatus(status,
|
|
function(item) {
|
|
var htmlLine = '';
|
|
if (item.hstatus == status)
|
|
htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus, item.hcomment);
|
|
|
|
var xhtmlLine = '';
|
|
if (item.xstatus == status)
|
|
xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus, item.xcomment);
|
|
|
|
exportTests.push({
|
|
'test' : item.test,
|
|
'html4' : htmlLine,
|
|
'xhtml1' : xhtmlLine });
|
|
},
|
|
function() {
|
|
_self.exportResultsCompletion(exportTests);
|
|
}
|
|
);
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.showResultsForTestsWithMismatchedResults = function()
|
|
{
|
|
this.beginAppendingOutput();
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForTestsWithMixedStatus(
|
|
function(item) {
|
|
_self.appendResultToOutput(kHTML4Data, item.test, item.hstatus, item.hcomment);
|
|
_self.appendResultToOutput(kXHTML1Data, item.test, item.xstatus, item.xcomment);
|
|
},
|
|
function() {
|
|
_self.endAppendingOutput();
|
|
}
|
|
);
|
|
}
|
|
|
|
TestSuite.prototype.exportResultsForTestsWithMismatchedResults = function()
|
|
{
|
|
var exportTests = [];
|
|
|
|
var _self = this;
|
|
this.queryDatabaseForTestsWithMixedStatus(
|
|
function(item) {
|
|
var htmlLine= _self.createExportLine(kHTML4Data, item.test, item.hstatus ? item.hstatus : '?', item.hcomment);
|
|
var xhtmlLine = _self.createExportLine(kXHTML1Data, item.test, item.xstatus ? item.xstatus : '?', item.xcomment);
|
|
exportTests.push({
|
|
'test' : item.test,
|
|
'html4' : htmlLine,
|
|
'xhtml1' : xhtmlLine });
|
|
},
|
|
function() {
|
|
_self.exportResultsCompletion(exportTests);
|
|
}
|
|
);
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.markTestCompleted = function(testID, htmlStatus, xhtmlStatus)
|
|
{
|
|
var test = this.tests[testID];
|
|
if (!test) {
|
|
window.console.log('markTestCompleted failed to find test ' + testID);
|
|
return;
|
|
}
|
|
|
|
if (htmlStatus) {
|
|
test.completedHTML = true;
|
|
test.statusHTML = htmlStatus;
|
|
}
|
|
if (xhtmlStatus) {
|
|
test.completedXHTML = true;
|
|
test.statusXHTML = xhtmlStatus;
|
|
}
|
|
}
|
|
|
|
TestSuite.prototype.testCompletionStateChanged = function()
|
|
{
|
|
this.updateTestList();
|
|
this.updateChapterPopup();
|
|
}
|
|
|
|
TestSuite.prototype.loadTestStatus = function()
|
|
{
|
|
var _self = this;
|
|
this.queryDatabaseForCompletedTests(
|
|
function(item) {
|
|
_self.markTestCompleted(item.test, item.hstatus, item.xstatus);
|
|
},
|
|
function() {
|
|
_self.testCompletionStateChanged();
|
|
}
|
|
);
|
|
|
|
this.updateChapterPopup();
|
|
}
|
|
|
|
TestSuite.prototype.resetTestStatus = function()
|
|
{
|
|
for (var testID in this.tests) {
|
|
var currTest = this.tests[testID];
|
|
currTest.completedHTML = false;
|
|
currTest.completedXHTML = false;
|
|
}
|
|
this.loadTestStatus();
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
TestSuite.prototype.updateSummaryData = function()
|
|
{
|
|
this.queryDatabaseForSummary(
|
|
function(results) {
|
|
|
|
var hTotal, xTotal;
|
|
var hDone, xDone;
|
|
|
|
for (var i = 0; i < results.length; ++i) {
|
|
var result = results[i];
|
|
|
|
switch (result.name) {
|
|
case 'h-total': hTotal = result.count; break;
|
|
case 'x-total': xTotal = result.count; break;
|
|
case 'h-tested': hDone = result.count; break;
|
|
case 'x-tested': xDone = result.count; break;
|
|
}
|
|
|
|
document.getElementById(result.name).innerText = result.count;
|
|
}
|
|
|
|
// We should get these all together.
|
|
if (hTotal) {
|
|
document.getElementById('h-percent').innerText = Math.round(100.0 * hDone / hTotal);
|
|
document.getElementById('x-percent').innerText = Math.round(100.0 * xDone / xTotal);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
/* ------------------------------------------------------- */
|
|
// Database stuff
|
|
|
|
function errorHandler(transaction, error)
|
|
{
|
|
alert('Database error: ' + error.message);
|
|
window.console.log('Database error: ' + error.message);
|
|
}
|
|
|
|
TestSuite.prototype.openDatabase = function()
|
|
{
|
|
if (!'openDatabase' in window) {
|
|
alert('Your browser does not support client-side SQL databases, so results will not be stored.');
|
|
return;
|
|
}
|
|
|
|
var _self = this;
|
|
this.db = window.openDatabase('css21testsuite', '', 'CSS 2.1 test suite results', 10 * 1024 * 1024);
|
|
|
|
// Migration handling. We assume migration will happen whenever the suite version changes,
|
|
// so that we can check for new or obsoleted tests.
|
|
function creation(tx) {
|
|
_self.databaseCreated(tx);
|
|
}
|
|
|
|
function migration1_0To1_1(tx) {
|
|
window.console.log('updating 1.0 to 1.1');
|
|
// We'll use the 'seen' column to cross-check with testinfo.data.
|
|
tx.executeSql('ALTER TABLE tests ADD COLUMN seen BOOLEAN DEFAULT \"FALSE\"', null, function() {
|
|
_self.syncDatabaseWithTestInfoData();
|
|
}, errorHandler);
|
|
}
|
|
|
|
if (this.db.version == '') {
|
|
_self.db.changeVersion('', '1.0', creation, null, function() {
|
|
_self.db.changeVersion('1.0', '1.1', migration1_0To1_1, null, function() {
|
|
_self.databaseReady();
|
|
}, errorHandler);
|
|
}, errorHandler);
|
|
|
|
return;
|
|
}
|
|
|
|
if (this.db.version == '1.0') {
|
|
_self.db.changeVersion('1.0', '1.1', migration1_0To1_1, null, function() {
|
|
window.console.log('ready')
|
|
_self.databaseReady();
|
|
}, errorHandler);
|
|
return;
|
|
}
|
|
|
|
this.databaseReady();
|
|
}
|
|
|
|
TestSuite.prototype.databaseCreated = function(tx)
|
|
{
|
|
window.console.log('databaseCreated');
|
|
this.populatingDatabase = true;
|
|
|
|
// hstatus: HTML4 result
|
|
// xstatus: XHTML1 result
|
|
var _self = this;
|
|
tx.executeSql('CREATE TABLE tests (test PRIMARY KEY UNIQUE, ref, title, flags, links, assertion, hstatus, hcomment, xstatus, xcomment)', null,
|
|
function(tx, results) {
|
|
_self.populateDatabaseFromTestInfoData();
|
|
}, errorHandler);
|
|
}
|
|
|
|
TestSuite.prototype.databaseReady = function()
|
|
{
|
|
this.updateSummaryData();
|
|
this.loadTestStatus();
|
|
}
|
|
|
|
TestSuite.prototype.storeTestResult = function(test, format, result, comment, useragent)
|
|
{
|
|
if (!this.db)
|
|
return;
|
|
|
|
this.db.transaction(function (tx) {
|
|
if (format == 'html4')
|
|
tx.executeSql('UPDATE tests SET hstatus=?, hcomment=? WHERE test=?\n', [result, comment, test], null, errorHandler);
|
|
else if (format == 'xhtml1')
|
|
tx.executeSql('UPDATE tests SET xstatus=?, xcomment=? WHERE test=?\n', [result, comment, test], null, errorHandler);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.importTestResults = function(results)
|
|
{
|
|
if (!this.db)
|
|
return;
|
|
|
|
this.db.transaction(function (tx) {
|
|
|
|
for (var i = 0; i < results.length; ++i) {
|
|
var currResult = results[i];
|
|
|
|
var query;
|
|
if (currResult.format == 'html4')
|
|
query = 'UPDATE tests SET hstatus=?, hcomment=? WHERE test=?\n';
|
|
else if (currResult.format == 'xhtml1')
|
|
query = 'UPDATE tests SET xstatus=?, xcomment=? WHERE test=?\n';
|
|
|
|
tx.executeSql(query, [currResult.result, currResult.comment, currResult.id], null, errorHandler);
|
|
}
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.clearTestResults = function(results)
|
|
{
|
|
if (!this.db)
|
|
return;
|
|
|
|
this.db.transaction(function (tx) {
|
|
|
|
for (var i = 0; i < results.length; ++i) {
|
|
var currResult = results[i];
|
|
|
|
if (currResult.clearHTML)
|
|
tx.executeSql('UPDATE tests SET hstatus=NULL, hcomment=NULL WHERE test=?\n', [currResult.id], null, errorHandler);
|
|
|
|
if (currResult.clearXHTML)
|
|
tx.executeSql('UPDATE tests SET xstatus=NULL, xcomment=NULL WHERE test=?\n', [currResult.id], null, errorHandler);
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.populateDatabaseFromTestInfoData = function()
|
|
{
|
|
if (!this.testInfoLoaded) {
|
|
window.console.log('Tring to populate database before testinfo.data has been loaded');
|
|
return;
|
|
}
|
|
|
|
window.console.log('populateDatabaseFromTestInfoData')
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
for (var testID in _self.tests) {
|
|
var test = _self.tests[testID];
|
|
// Version 1.0, so no 'seen' column.
|
|
tx.executeSql('INSERT INTO tests (test, ref, title, flags, links, assertion) VALUES (?, ?, ?, ?, ?, ?)',
|
|
[test.id, test.reference, test.title, test.flags, test.links, test.assertion], null, errorHandler);
|
|
}
|
|
_self.populatingDatabase = false;
|
|
});
|
|
|
|
}
|
|
|
|
TestSuite.prototype.insertTest = function(tx, test)
|
|
{
|
|
tx.executeSql('INSERT INTO tests (test, ref, title, flags, links, assertion, seen) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
|
[test.id, test.reference, test.title, test.flags, test.links, test.assertion, 'TRUE'], null, errorHandler);
|
|
}
|
|
|
|
// Deal with removed/renamed tests in a new version of the suite.
|
|
// self.tests is canonical; the database may contain stale entries.
|
|
TestSuite.prototype.syncDatabaseWithTestInfoData = function()
|
|
{
|
|
if (!this.testInfoLoaded) {
|
|
window.console.log('Trying to sync database before testinfo.data has been loaded');
|
|
return;
|
|
}
|
|
|
|
// Make an object with all tests that we'll use to track new tests.
|
|
var testsToInsert = {};
|
|
for (var testId in this.tests) {
|
|
var currTest = this.tests[testId];
|
|
testsToInsert[currTest.id] = currTest;
|
|
}
|
|
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
// Find tests that are not in the database yet.
|
|
// (Wasn't able to get INSERT ... IF NOT working.)
|
|
tx.executeSql('SELECT * FROM tests', [], function(tx, results) {
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i) {
|
|
var item = results.rows.item(i);
|
|
delete testsToInsert[item.test];
|
|
}
|
|
}, errorHandler);
|
|
});
|
|
|
|
this.db.transaction(function (tx) {
|
|
for (var testId in testsToInsert) {
|
|
var currTest = testsToInsert[testId];
|
|
window.console.log(currTest.id + ' is new; inserting');
|
|
_self.insertTest(tx, currTest);
|
|
}
|
|
});
|
|
|
|
this.db.transaction(function (tx) {
|
|
for (var testID in _self.tests)
|
|
tx.executeSql('UPDATE tests SET seen=\"TRUE\" WHERE test=?\n', [testID], null, errorHandler);
|
|
|
|
tx.executeSql('SELECT * FROM tests WHERE seen=\"FALSE\"', [], function(tx, results) {
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i) {
|
|
var item = results.rows.item(i);
|
|
window.console.log('Test ' + item.test + ' was in the database but is no longer in the suite; deleting.');
|
|
}
|
|
}, errorHandler);
|
|
|
|
// Delete rows for disappeared tests.
|
|
tx.executeSql('DELETE FROM tests WHERE seen=\"FALSE\"', [], function(tx, results) {
|
|
_self.populatingDatabase = false;
|
|
_self.databaseReady();
|
|
}, errorHandler);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.queryDatabaseForAllTests = function(sortKey, perRowHandler, completionHandler)
|
|
{
|
|
if (this.populatingDatabase)
|
|
return;
|
|
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
var query;
|
|
var args = [];
|
|
if (sortKey != '') {
|
|
query = 'SELECT * FROM tests ORDER BY ? ASC'; // ORDER BY doesn't seem to work
|
|
args.push(sortKey);
|
|
}
|
|
else
|
|
query = 'SELECT * FROM tests';
|
|
|
|
tx.executeSql(query, args, function(tx, results) {
|
|
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i)
|
|
perRowHandler(results.rows.item(i));
|
|
|
|
completionHandler();
|
|
}, errorHandler);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.queryDatabaseForTestsWithStatus = function(status, perRowHandler, completionHandler)
|
|
{
|
|
if (this.populatingDatabase)
|
|
return;
|
|
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
tx.executeSql('SELECT * FROM tests WHERE hstatus=? OR xstatus=?', [status, status], function(tx, results) {
|
|
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i)
|
|
perRowHandler(results.rows.item(i));
|
|
|
|
completionHandler();
|
|
}, errorHandler);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.queryDatabaseForTestsWithMixedStatus = function(perRowHandler, completionHandler)
|
|
{
|
|
if (this.populatingDatabase)
|
|
return;
|
|
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
tx.executeSql('SELECT * FROM tests WHERE hstatus IS NOT NULL AND xstatus IS NOT NULL AND hstatus <> xstatus', [], function(tx, results) {
|
|
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i)
|
|
perRowHandler(results.rows.item(i));
|
|
|
|
completionHandler();
|
|
}, errorHandler);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.queryDatabaseForCompletedTests = function(perRowHandler, completionHandler)
|
|
{
|
|
if (this.populatingDatabase)
|
|
return;
|
|
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
|
|
tx.executeSql('SELECT * FROM tests WHERE hstatus IS NOT NULL OR xstatus IS NOT NULL', [], function(tx, results) {
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i)
|
|
perRowHandler(results.rows.item(i));
|
|
|
|
completionHandler();
|
|
}, errorHandler);
|
|
});
|
|
}
|
|
|
|
TestSuite.prototype.queryDatabaseForTestsNotRun = function(perRowHandler, completionHandler)
|
|
{
|
|
if (this.populatingDatabase)
|
|
return;
|
|
|
|
var _self = this;
|
|
this.db.transaction(function (tx) {
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
|
|
tx.executeSql('SELECT * FROM tests WHERE hstatus IS NULL OR xstatus IS NULL', [], function(tx, results) {
|
|
|
|
var len = results.rows.length;
|
|
for (var i = 0; i < len; ++i)
|
|
perRowHandler(results.rows.item(i));
|
|
|
|
completionHandler();
|
|
}, errorHandler);
|
|
});
|
|
}
|
|
|
|
/*
|
|
|
|
completionHandler gets called an array of results,
|
|
which may be some or all of:
|
|
|
|
data = [
|
|
{ 'name' : ,
|
|
'count' :
|
|
},
|
|
]
|
|
|
|
where name is one of:
|
|
|
|
'h-total'
|
|
'h-tested'
|
|
'h-passed'
|
|
'h-failed'
|
|
'h-skipped'
|
|
|
|
'x-total'
|
|
'x-tested'
|
|
'x-passed'
|
|
'x-failed'
|
|
'x-skipped'
|
|
|
|
*/
|
|
|
|
|
|
TestSuite.prototype.countTestsWithColumnValue = function(tx, completionHandler, column, value, label)
|
|
{
|
|
var allRowsCount = 'COUNT(*)';
|
|
|
|
tx.executeSql('SELECT COUNT(*) FROM tests WHERE ' + column + '=?', [value], function(tx, results) {
|
|
var data = [];
|
|
if (results.rows.length > 0)
|
|
data.push({ 'name' : label, 'count' : results.rows.item(0)[allRowsCount] })
|
|
completionHandler(data);
|
|
}, errorHandler);
|
|
}
|
|
|
|
TestSuite.prototype.countTestsWithFlag = function(tx, completionHandler, flag)
|
|
{
|
|
var allRowsCount = 'COUNT(*)';
|
|
|
|
tx.executeSql('SELECT COUNT(*) FROM tests WHERE flags LIKE \"%' + flag + '%\"', [], function(tx, results) {
|
|
var rowCount = 0;
|
|
if (results.rows.length > 0)
|
|
rowCount = results.rows.item(0)[allRowsCount];
|
|
completionHandler(rowCount);
|
|
}, errorHandler);
|
|
}
|
|
|
|
TestSuite.prototype.queryDatabaseForSummary = function(completionHandler)
|
|
{
|
|
if (!this.db || this.populatingDatabase)
|
|
return;
|
|
|
|
var _self = this;
|
|
|
|
var htmlOnlyTestCount = 0;
|
|
var xHtmlOnlyTestCount = 0;
|
|
|
|
this.db.transaction(function (tx) {
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
|
|
var allRowsCount = 'COUNT(*)';
|
|
|
|
_self.countTestsWithFlag(tx, function(count) {
|
|
htmlOnlyTestCount = count;
|
|
}, 'htmlOnly');
|
|
|
|
_self.countTestsWithFlag(tx, function(count) {
|
|
xHtmlOnlyTestCount = count;
|
|
}, 'nonHTML');
|
|
});
|
|
|
|
this.db.transaction(function (tx) {
|
|
if (_self.populatingDatabase)
|
|
return;
|
|
|
|
var allRowsCount = 'COUNT(*)';
|
|
var html4RowsCount = 'COUNT(hstatus)';
|
|
var xhtml1RowsCount = 'COUNT(xstatus)';
|
|
|
|
tx.executeSql('SELECT COUNT(*), COUNT(hstatus), COUNT(xstatus) FROM tests', [], function(tx, results) {
|
|
|
|
var data = [];
|
|
if (results.rows.length > 0) {
|
|
var rowItem = results.rows.item(0);
|
|
data.push({ 'name' : 'h-total' , 'count' : rowItem[allRowsCount] - xHtmlOnlyTestCount })
|
|
data.push({ 'name' : 'x-total' , 'count' : rowItem[allRowsCount] - htmlOnlyTestCount })
|
|
data.push({ 'name' : 'h-tested', 'count' : rowItem[html4RowsCount] })
|
|
data.push({ 'name' : 'x-tested', 'count' : rowItem[xhtml1RowsCount] })
|
|
}
|
|
completionHandler(data);
|
|
|
|
}, errorHandler);
|
|
|
|
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'pass', 'h-passed');
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'pass', 'x-passed');
|
|
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'fail', 'h-failed');
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'fail', 'x-failed');
|
|
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'skipped', 'h-skipped');
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'skipped', 'x-skipped');
|
|
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'hstatus', 'invalid', 'h-invalid');
|
|
_self.countTestsWithColumnValue(tx, completionHandler, 'xstatus', 'invalid', 'x-invalid');
|
|
});
|
|
}
|
|
|