// Runner for html5lib-tests .dat files. // // Each wrapper page (html5lib_url.html, html5lib_write.html, …) loads this // script with `` // and lists each .dat file as ``. // We fetch resources/NAME.dat, parse it, and run each entry: document tests // parse via the page's run_type; entries with #document-fragment always parse // via innerHTML. (() => { "use strict"; const NAMESPACES = { html: "http://www.w3.org/1999/xhtml", math: "http://www.w3.org/1998/Math/MathML", mathml: "http://www.w3.org/1998/Math/MathML", svg: "http://www.w3.org/2000/svg", xlink: "http://www.w3.org/1999/xlink", xml: "http://www.w3.org/XML/1998/namespace", xmlns: "http://www.w3.org/2000/xmlns/", }; const PREFIXES = {}; for (const prefix of Object.keys(NAMESPACES)) { PREFIXES[NAMESPACES[prefix]] = prefix; } PREFIXES[NAMESPACES.mathml] = "math"; function serializeTree(root) { root.normalize(); const lines = []; // The .dat #document format prefixes each line with "|" + N spaces of // indent, where the document itself has no "|" line, its direct children // get 1 space, their children get 3, then 5, 7, … (i.e. 2*depth - 1). const walk = (node, depth) => { const pad = depth > 0 ? " ".repeat(2 * depth - 1) : ""; const innerPad = " ".repeat(2 * depth + 1); switch (node.nodeType) { case Node.DOCUMENT_TYPE_NODE: if (node.name) { if (node.publicId || node.systemId) { lines.push(`|${pad}`); } else { lines.push(`|${pad}`); } } else { lines.push(`|${pad}`); } break; case Node.DOCUMENT_NODE: lines.push("#document"); break; case Node.DOCUMENT_FRAGMENT_NODE: lines.push("#document-fragment"); break; case Node.COMMENT_NODE: lines.push(`|${pad}`); break; case Node.TEXT_NODE: lines.push(`|${pad}"${node.nodeValue}"`); break; case Node.ELEMENT_NODE: { if (node.getAttribute("data-skip") !== null) return; const tag = (node.namespaceURI && node.namespaceURI !== NAMESPACES.html) ? `${PREFIXES[node.namespaceURI]} ${node.localName}` : node.localName; lines.push(`|${pad}<${tag}>`); const attrs = [...node.attributes].map(a => [ (a.namespaceURI ? PREFIXES[a.namespaceURI] + " " : "") + a.localName, a.value, ]); attrs.sort((a, b) => a[0] === b[0] ? 0 : (a[0] > b[0] ? 1 : -1)); for (const [name, value] of attrs) { lines.push(`|${innerPad}${name}="${value}"`); } if (node.namespaceURI === NAMESPACES.html && node.localName === "template") { lines.push(`|${innerPad}content`); for (const child of node.content.childNodes) walk(child, depth + 2); } break; } } for (const child of node.childNodes) walk(child, depth + 1); }; walk(root, 0); return lines.join("\n"); } // Per-flavor runners. Each fills `iframe` with the parser's output, then // serializes and asserts. record() captures input/expected/actual on the // shared map so failure diffs can be rendered after the run. function makeDocRunner(inject) { return ({ iframe, t, id, input, expected, record }) => { record({ id, input, expected, actual: null }); iframe.onload = () => t.step(() => { iframe.onload = null; const actual = serializeTree(iframe.contentDocument); record({ id, input, expected, actual }); assert_equals(actual, expected); t.done(); }); inject(iframe, input, t); }; } const RUNNERS = { url: makeDocRunner((iframe, input, t) => { const blob = new Blob([input], { type: "text/html" }); const url = URL.createObjectURL(blob); iframe.src = url; t.add_cleanup(() => URL.revokeObjectURL(url)); }), write: makeDocRunner((iframe, input) => { iframe.contentDocument.open(); iframe.contentDocument.write(input); iframe.contentDocument.close(); }), write_single: makeDocRunner((iframe, input) => { iframe.contentDocument.open(); for (const ch of input) iframe.contentDocument.write(ch); iframe.contentDocument.close(); }), innerHTML: ({ iframe, t, id, input, expected, container, record }) => { const parts = container.split(" "); const containerEl = parts.length > 1 ? document.createElementNS(NAMESPACES[parts[0]], `${parts[0]}:${parts[1]}`) : document.createElement(container); containerEl.innerHTML = input; const root = (containerEl.namespaceURI === NAMESPACES.html && containerEl.localName === "template") ? containerEl.content : containerEl; let actual = serializeTree(root); record({ id, input, expected, actual, container }); // serializeTree emits "#document-fragment"; the .dat expected tree uses // "#document" as its root marker. const lines = actual.split("\n"); assert_not_equals(lines[0], "