1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* A test suite that runs WHATWG HTML parser tests.
* The tests are from html5lib.
*
* http://html5lib.googlecode.com/
*/
/**
* A few utility functions.
*/
function log() {}
function startsWith(s, s2) {
return s.indexOf(s2) == 0;
}
function trimString(s) {
return s.replace(/^\s+/, "").replace(/\s+$/, "");
}
/**
* Parses an individual testcase into an array containing the input
* string, a string representing the expected tree (DOM), and a list
* of error messages.
*
* @param A string containing a single testcase
*/
function parseTestcase(testcase) {
var lines = testcase.split("\n");
/* check that the first non-empty, non-comment line is #data */
for (let line of lines) {
if (!line || startsWith(line, "##")) {
continue;
}
if (line == "#data") {
break;
}
log(lines);
throw new Error("Unknown test format.");
}
var input = [];
var output = [];
var errors = [];
var fragment = [];
var currentList = input;
for (let line of lines) {
if (startsWith(line, "##todo")) {
todo(false, line.substring(6));
continue;
}
if (
!(
startsWith(line, "#error") ||
startsWith(line, "#document") ||
startsWith(line, "#document-fragment") ||
startsWith(line, "#data")
)
) {
currentList.push(line);
} else if (line == "#errors") {
currentList = errors;
} else if (line == "#document") {
currentList = output;
} else if (line == "#document-fragment") {
currentList = fragment;
}
}
while (!output[output.length - 1]) {
output.pop(); // zap trailing blank lines
}
// logger.log(input.length, output.length, errors.length);
return [input.join("\n"), output.join("\n"), errors, fragment[0]];
}
/**
* A generator function that accepts a list of strings. Each list
* member corresponds to the contents of a ".dat" file from the
* html5lib test suite.
*
* @param The list of strings
*/
function* test_parser(testlist) {
for (var testgroup of testlist) {
var tests = testgroup.split("#data\n");
tests = tests.filter(test => test).map(test => "#data\n" + test);
for (var test of tests) {
yield parseTestcase(test);
}
}
}
/**
* Transforms a DOM document to a string matching the format in
* the test cases.
*
* @param the DOM document
*/
function docToTestOutput(doc) {
var walker = doc.createTreeWalker(doc, NodeFilter.SHOW_ALL, null);
return addLevels(walker, "", "| ").slice(0, -1); // remove the last newline
}
/**
* Creates a walker for a fragment that skips over the root node.
*
* @param an element
*/
function createFragmentWalker(elt) {
return elt.ownerDocument.createTreeWalker(
elt,
NodeFilter.SHOW_ALL,
function (node) {
return elt == node ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT;
}
);
}
/**
* Transforms the descendants of an element to a string matching the format
* in the test cases.
*
* @param an element
*/
function fragmentToTestOutput(elt) {
var walker = createFragmentWalker(elt);
return addLevels(walker, "", "| ").slice(0, -1); // remove the last newline
}
function addLevels(walker, buf, indent) {
if (walker.firstChild()) {
do {
buf += indent;
switch (walker.currentNode.nodeType) {
case Node.ELEMENT_NODE:
buf += "<";
var ns = walker.currentNode.namespaceURI;
if ("http://www.w3.org/1998/Math/MathML" == ns) {
buf += "math ";
} else if ("http://www.w3.org/2000/svg" == ns) {
buf += "svg ";
} else if ("http://www.w3.org/1999/xhtml" != ns) {
buf += "otherns ";
}
buf += walker.currentNode.localName + ">";
if (walker.currentNode.hasAttributes()) {
var valuesByName = {};
var attrs = walker.currentNode.attributes;
for (let i = 0; i < attrs.length; ++i) {
var localName = attrs[i].localName;
var name;
var attrNs = attrs[i].namespaceURI;
if (null == attrNs) {
name = localName;
} else if ("http://www.w3.org/XML/1998/namespace" == attrNs) {
name = "xml " + localName;
} else if ("http://www.w3.org/1999/xlink" == attrNs) {
name = "xlink " + localName;
} else if ("http://www.w3.org/2000/xmlns/" == attrNs) {
name = "xmlns " + localName;
} else {
name = "otherns " + localName;
}
valuesByName[name] = attrs[i].value;
}
var keys = Object.keys(valuesByName).sort();
for (let i = 0; i < keys.length; ++i) {
buf +=
"\n" +
indent +
" " +
keys[i] +
'="' +
valuesByName[keys[i]] +
'"';
}
}
break;
case Node.DOCUMENT_TYPE_NODE:
buf += "<!DOCTYPE " + walker.currentNode.name;
if (walker.currentNode.publicId || walker.currentNode.systemId) {
buf += ' "';
buf += walker.currentNode.publicId;
buf += '" "';
buf += walker.currentNode.systemId;
buf += '"';
}
buf += ">";
break;
case Node.COMMENT_NODE:
buf += "<!-- " + walker.currentNode.nodeValue + " -->";
break;
case Node.TEXT_NODE:
buf += '"' + walker.currentNode.nodeValue + '"';
break;
}
buf += "\n";
// In the case of template elements, children do not get inserted as
// children of the template element, instead they are inserted
// as children of the template content (which is a document fragment).
if (walker.currentNode.constructor.name === "HTMLTemplateElement") {
buf += indent + " content\n";
// Walk through the template content.
var templateWalker = createFragmentWalker(walker.currentNode.content);
buf = addLevels(templateWalker, buf, indent + " ");
}
buf = addLevels(walker, buf, indent + " ");
} while (walker.nextSibling());
walker.parentNode();
}
return buf;
}
|