--- a/conformancetest/runtest.html Fri Sep 16 14:35:34 2011 -0600
+++ b/conformancetest/runtest.html Mon Sep 19 12:12:05 2011 -0600
@@ -34,7 +34,7 @@
}
});
- browserTests.forEach(runTest);
+ browserTests.forEach(runConformanceTest);
document.getElementById("test-container").parentNode
.removeChild(document.getElementById("test-container"));
@@ -43,171 +43,4 @@
document.getElementById("timing").textContent =
"Time elapsed: " + Math.floor(elapsed/60) + ":" + (elapsed % 60).toFixed(3) + " min.";
}
-
-/**
- * Input is the same format as output of generateTest in gentest.html.
- */
-function runTest(browserTest) {
- document.getElementById("test-container").innerHTML = "<div contenteditable></div><p>test";
- var testName = JSON.stringify(browserTest[1]) + " " + format_value(browserTest[0]);
- var testDiv = document.querySelector("div[contenteditable]");
- var originalRootElement, newRootElement;
- var exception = null;
- var expectedQueryResults = browserTest[3];
- var actualQueryResults = {};
- var actualQueryExceptions = {};
-
- try {
- var points = setupDiv(testDiv, browserTest[0]);
-
- var range = document.createRange();
- range.setStart(points[0], points[1]);
- range.setEnd(points[2], points[3]);
- // The points might be backwards
- if (range.collapsed) {
- range.setEnd(points[0], points[1]);
- }
- getSelection().removeAllRanges();
- getSelection().addRange(range);
-
- var originalRootElement = document.documentElement.cloneNode(true);
- originalRootElement.querySelector("[contenteditable]").parentNode
- .removeChild(originalRootElement.querySelector("[contenteditable]"));
-
- for (var command in expectedQueryResults) {
- var results = [];
- var exceptions = {};
- try { results[0] = document.queryCommandIndeterm(command) }
- catch(e) { exceptions[0] = e }
- try { results[1] = document.queryCommandState(command) }
- catch(e) { exceptions[1] = e }
- try { results[2] = document.queryCommandValue(command) }
- catch(e) { exceptions[2] = e }
- actualQueryResults[command] = results;
- actualQueryExceptions[command] = exceptions;
- }
-
- for (var i = 0; i < browserTest[1].length; i++) {
- document.execCommand(browserTest[1][i][0], false, browserTest[1][i][1]);
- }
-
- for (var command in expectedQueryResults) {
- var results = actualQueryResults[command];
- var exceptions = actualQueryExceptions[command];
- try { results[3] = document.queryCommandIndeterm(command) }
- catch(e) { exceptions[3] = e }
- try { results[4] = document.queryCommandState(command) }
- catch(e) { exceptions[4] = e }
- try { results[5] = document.queryCommandValue(command) }
- catch(e) { exceptions[5] = e }
- }
-
- var newRootElement = document.documentElement.cloneNode(true);
- newRootElement.querySelector("[contenteditable]").parentNode
- .removeChild(newRootElement.querySelector("[contenteditable]"));
-
- normalizeSerializedStyle(testDiv);
- } catch(e) {
- exception = e;
- }
-
- test(function() {
- assert_equals(exception, null, "Setup and execCommand() must not throw an exception");
-
- // Now test for modifications to non-editable content. First just
- // count children:
- assert_equals(testDiv.parentNode.childNodes.length, 2,
- "The parent div must have two children. Did something spill outside the test div?");
-
- // Check for attributes
- assert_equals(testDiv.attributes.length, 1,
- 'Wrapper div must have only one attribute (<div contenteditable="">), but has more (' +
- formatStartTag(testDiv) + ")");
-
- assert_equals(document.body.attributes.length, 0,
- "Body element must have no attributes (<body>), but has more (" +
- formatStartTag(document.body) + ")");
-
- // Check that in general, nothing outside the test div was modified.
- // TODO: Less verbose error reporting, the way some of the range tests
- // do?
- assert_equals(newRootElement.innerHTML, originalRootElement.innerHTML,
- "Everything outside the editable div must be unchanged, but some change did occur");
- }, testName + " checks for modifications to non-editable content");
-
- test(function() {
- assert_equals(exception, null, "Setup and execCommand() must not throw an exception");
-
- assert_equals(testDiv.innerHTML,
- browserTest[2].replace(/[\[\]{}]/g, ""),
- "Unexpected innerHTML (after normalizing inline style)");
- }, testName + " compare innerHTML");
-
- for (var command in expectedQueryResults) {
- var descriptions = [
- 'queryCommandIndeterm("' + command + '") before',
- 'queryCommandState("' + command + '") before',
- 'queryCommandValue("' + command + '") before',
- 'queryCommandIndeterm("' + command + '") after',
- 'queryCommandState("' + command + '") after',
- 'queryCommandValue("' + command + '") after',
- ];
- for (var i = 0; i < 6; i++) {
- test(function() {
- assert_equals(exception, null, "Setup and execCommand() must not throw an exception");
-
- if (expectedQueryResults[command][i] === null) {
- // Some ad hoc tests to verify that we have a real
- // DOMException. FIXME: This should be made more rigorous,
- // with clear steps specified for checking that something
- // is really a DOMException.
- assert_true(i in actualQueryExceptions[command],
- "An exception must be thrown in this case");
- var e = actualQueryExceptions[command][i];
- assert_equals(typeof e, "object",
- "typeof thrown object");
- assert_idl_attribute(e, "code",
- "Thrown object must be a DOMException");
- assert_idl_attribute(e, "INVALID_ACCESS_ERR",
- "Thrown object must be a DOMException");
- assert_equals(e.code, e.INVALID_ACCESS_ERR,
- "Thrown object must be an INVALID_ACCESS_ERR, so its .code and .INVALID_ACCESS_ERR attributes must be equal");
- } else if ((i == 2 || i == 5)
- && (command == "backcolor" || command == "forecolor" || command == "hilitecolor")
- && typeof actualQueryResults[command][i] == "string") {
- assert_false(i in actualQueryExceptions[command],
- "An exception must not be thrown in this case");
- // We don't return the format that the color should be in:
- // that's up to CSSOM. Thus we normalize before comparing.
- assert_equals(normalizeColor(actualQueryResults[command][i]),
- expectedQueryResults[command][i],
- "Wrong result returned (after color normalization)");
- } else {
- assert_false(i in actualQueryExceptions[command],
- "An exception must not be thrown in this case");
- assert_equals(actualQueryResults[command][i],
- expectedQueryResults[command][i],
- "Wrong result returned");
- }
- }, testName + " " + descriptions[i]);
- }
- }
-
- // Silly Firefox
- document.body.removeAttribute("bgcolor");
-}
-
-/**
- * Return a string like '<body bgcolor="#FFFFFF">'.
- */
-function formatStartTag(el) {
- var ret = "<" + el.tagName.toLowerCase();
- for (var i = 0; i < el.attributes.length; i++) {
- ret += " " + el.attributes[i].name + '="';
- ret += el.attributes[i].value.replace(/\&/g, "&")
- .replace(/"/g, """);
- ret += '"';
- }
- return ret + ">";
-}
</script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/conformancetest/splitruntest.html Mon Sep 19 12:12:05 2011 -0600
@@ -0,0 +1,77 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel=stylesheet href=../css/reset.css>
+<title>HTML editing conformance tests (split version)</title>
+<p>See the <a href=editing.html#tests>Tests</a> section of the specification
+for documentation.
+
+<p id=picker>
+
+<p id=timing></p>
+
+<div id=log></div>
+
+<div id=test-container></div>
+
+<script src=../implementation.js></script>
+<script>var testsJsLibraryOnly = true</script>
+<script src=../tests.js></script>
+<script src=data.js></script>
+<script src=http://dvcs.w3.org/hg/html/raw-file/tip/tests/resources/testharness.js></script>
+<script src=http://dvcs.w3.org/hg/html/raw-file/tip/tests/resources/testharnessreport.js></script>
+<script>
+"use strict";
+
+(function() {
+ var startTime = Date.now();
+
+ // Make document.body.innerHTML more tidy by removing unnecessary things.
+ // We can't remove the testharness.js script, because at the time of this
+ // writing, for some reason that stops it from adding appropriate CSS.
+ [].forEach.call(document.querySelectorAll("script"), function(node) {
+ if (!/testharness\.js$/.test(node.src)) {
+ node.parentNode.removeChild(node);
+ }
+ });
+
+ var groupedBrowserTests = {};
+ for (var i = 0; i < browserTests.length; i++) {
+ var commands = browserTests[i][1];
+ if (commands.length == 2
+ && commands[0][0] == "stylewithcss") {
+ // Regular test, not a multitest
+ if (!(commands[1][0] in groupedBrowserTests)) {
+ groupedBrowserTests[commands[1][0]] = [];
+ }
+ groupedBrowserTests[commands[1][0]].push(browserTests[i]);
+ } else {
+ // Multitest
+ if (!("multitest" in groupedBrowserTests)) {
+ groupedBrowserTests.multitest = [];
+ }
+ groupedBrowserTests.multitest.push(browserTests[i]);
+ }
+ }
+
+ var pickerContents = "Select a group of tests to run:";
+ for (var group in groupedBrowserTests) {
+ pickerContents += " <a href=?" + group + ">" + group + "</a>";
+ }
+ document.querySelector("#picker").innerHTML = pickerContents;
+
+ var command = location.search.replace(/^\?/, "");
+ if (!(command in groupedBrowserTests)) {
+ return;
+ }
+ browserTests = groupedBrowserTests[command];
+
+ browserTests.forEach(runConformanceTest);
+
+ document.getElementById("test-container").parentNode
+ .removeChild(document.getElementById("test-container"));
+
+ var elapsed = Math.round(Date.now() - startTime)/1000;
+ document.getElementById("timing").textContent =
+ "Time elapsed: " + Math.floor(elapsed/60) + ":" + (elapsed % 60).toFixed(3) + " min.";
+})();
+</script>
--- a/editing.html Fri Sep 16 14:35:34 2011 -0600
+++ b/editing.html Mon Sep 19 12:12:05 2011 -0600
@@ -65,7 +65,7 @@
<body class=draft>
<div class=head id=head>
<h1>HTML Editing APIs</h1>
-<h2 class="no-num no-toc" id=work-in-progress-—-last-update-16-september-2011>Work in Progress — Last Update 16 September 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-—-last-update-19-september-2011>Work in Progress — Last Update 19 September 2011</h2>
<dl>
<dt>Editor
<dd>Aryeh Gregor <<a href=mailto:ayg@aryeh.name>ayg@aryeh.name</a>>
@@ -462,6 +462,12 @@
framework, and browser implementers should be able to make them part of their
automated regression test frameworks.
+<p>For manual inspection, a version of the conformance tests is also available
+that runs only some of the tests at once: <a href=conformancetest/splitruntest.html>conformancetest/splitruntest.html</a>.
+This runs only one group of tests at a time, which takes only a few seconds
+instead of a minute or more. This makes debugging particular tests much
+faster. In all other respects, it should behave identically to runtest.html.
+
<p>Unlike the development tests, the conformance tests don't run the JavaScript
implementation as part of the test. Instead, the tests' expected values are
generated by a separate page, <a href=conformancetest/gentest.html>conformancetest/gentest.html</a>. That page
--- a/source.html Fri Sep 16 14:35:34 2011 -0600
+++ b/source.html Mon Sep 19 12:12:05 2011 -0600
@@ -390,6 +390,13 @@
framework, and browser implementers should be able to make them part of their
automated regression test frameworks.
+<p>For manual inspection, a version of the conformance tests is also available
+that runs only some of the tests at once: <a
+href=conformancetest/splitruntest.html>conformancetest/splitruntest.html</a>.
+This runs only one group of tests at a time, which takes only a few seconds
+instead of a minute or more. This makes debugging particular tests much
+faster. In all other respects, it should behave identically to runtest.html.
+
<p>Unlike the development tests, the conformance tests don't run the JavaScript
implementation as part of the test. Instead, the tests' expected values are
generated by a separate page, <a
--- a/tests.js Fri Sep 16 14:35:34 2011 -0600
+++ b/tests.js Mon Sep 19 12:12:05 2011 -0600
@@ -4619,4 +4619,175 @@
}
//@}
+/**
+ * Input is the same format as output of generateTest in gentest.html.
+ */
+function runConformanceTest(browserTest) {
+//@{
+ document.getElementById("test-container").innerHTML = "<div contenteditable></div><p>test";
+ var testName = JSON.stringify(browserTest[1]) + " " + format_value(browserTest[0]);
+ var testDiv = document.querySelector("div[contenteditable]");
+ var originalRootElement, newRootElement;
+ var exception = null;
+ var expectedQueryResults = browserTest[3];
+ var actualQueryResults = {};
+ var actualQueryExceptions = {};
+
+ try {
+ var points = setupDiv(testDiv, browserTest[0]);
+
+ var range = document.createRange();
+ range.setStart(points[0], points[1]);
+ range.setEnd(points[2], points[3]);
+ // The points might be backwards
+ if (range.collapsed) {
+ range.setEnd(points[0], points[1]);
+ }
+ getSelection().removeAllRanges();
+ getSelection().addRange(range);
+
+ var originalRootElement = document.documentElement.cloneNode(true);
+ originalRootElement.querySelector("[contenteditable]").parentNode
+ .removeChild(originalRootElement.querySelector("[contenteditable]"));
+
+ for (var command in expectedQueryResults) {
+ var results = [];
+ var exceptions = {};
+ try { results[0] = document.queryCommandIndeterm(command) }
+ catch(e) { exceptions[0] = e }
+ try { results[1] = document.queryCommandState(command) }
+ catch(e) { exceptions[1] = e }
+ try { results[2] = document.queryCommandValue(command) }
+ catch(e) { exceptions[2] = e }
+ actualQueryResults[command] = results;
+ actualQueryExceptions[command] = exceptions;
+ }
+
+ for (var i = 0; i < browserTest[1].length; i++) {
+ document.execCommand(browserTest[1][i][0], false, browserTest[1][i][1]);
+ }
+
+ for (var command in expectedQueryResults) {
+ var results = actualQueryResults[command];
+ var exceptions = actualQueryExceptions[command];
+ try { results[3] = document.queryCommandIndeterm(command) }
+ catch(e) { exceptions[3] = e }
+ try { results[4] = document.queryCommandState(command) }
+ catch(e) { exceptions[4] = e }
+ try { results[5] = document.queryCommandValue(command) }
+ catch(e) { exceptions[5] = e }
+ }
+
+ var newRootElement = document.documentElement.cloneNode(true);
+ newRootElement.querySelector("[contenteditable]").parentNode
+ .removeChild(newRootElement.querySelector("[contenteditable]"));
+
+ normalizeSerializedStyle(testDiv);
+ } catch(e) {
+ exception = e;
+ }
+
+ test(function() {
+ assert_equals(exception, null, "Setup and execCommand() must not throw an exception");
+
+ // Now test for modifications to non-editable content. First just
+ // count children:
+ assert_equals(testDiv.parentNode.childNodes.length, 2,
+ "The parent div must have two children. Did something spill outside the test div?");
+
+ // Check for attributes
+ assert_equals(testDiv.attributes.length, 1,
+ 'Wrapper div must have only one attribute (<div contenteditable="">), but has more (' +
+ formatStartTag(testDiv) + ")");
+
+ assert_equals(document.body.attributes.length, 0,
+ "Body element must have no attributes (<body>), but has more (" +
+ formatStartTag(document.body) + ")");
+
+ // Check that in general, nothing outside the test div was modified.
+ // TODO: Less verbose error reporting, the way some of the range tests
+ // do?
+ assert_equals(newRootElement.innerHTML, originalRootElement.innerHTML,
+ "Everything outside the editable div must be unchanged, but some change did occur");
+ }, testName + " checks for modifications to non-editable content");
+
+ test(function() {
+ assert_equals(exception, null, "Setup and execCommand() must not throw an exception");
+
+ assert_equals(testDiv.innerHTML,
+ browserTest[2].replace(/[\[\]{}]/g, ""),
+ "Unexpected innerHTML (after normalizing inline style)");
+ }, testName + " compare innerHTML");
+
+ for (var command in expectedQueryResults) {
+ var descriptions = [
+ 'queryCommandIndeterm("' + command + '") before',
+ 'queryCommandState("' + command + '") before',
+ 'queryCommandValue("' + command + '") before',
+ 'queryCommandIndeterm("' + command + '") after',
+ 'queryCommandState("' + command + '") after',
+ 'queryCommandValue("' + command + '") after',
+ ];
+ for (var i = 0; i < 6; i++) {
+ test(function() {
+ assert_equals(exception, null, "Setup and execCommand() must not throw an exception");
+
+ if (expectedQueryResults[command][i] === null) {
+ // Some ad hoc tests to verify that we have a real
+ // DOMException. FIXME: This should be made more rigorous,
+ // with clear steps specified for checking that something
+ // is really a DOMException.
+ assert_true(i in actualQueryExceptions[command],
+ "An exception must be thrown in this case");
+ var e = actualQueryExceptions[command][i];
+ assert_equals(typeof e, "object",
+ "typeof thrown object");
+ assert_idl_attribute(e, "code",
+ "Thrown object must be a DOMException");
+ assert_idl_attribute(e, "INVALID_ACCESS_ERR",
+ "Thrown object must be a DOMException");
+ assert_equals(e.code, e.INVALID_ACCESS_ERR,
+ "Thrown object must be an INVALID_ACCESS_ERR, so its .code and .INVALID_ACCESS_ERR attributes must be equal");
+ } else if ((i == 2 || i == 5)
+ && (command == "backcolor" || command == "forecolor" || command == "hilitecolor")
+ && typeof actualQueryResults[command][i] == "string") {
+ assert_false(i in actualQueryExceptions[command],
+ "An exception must not be thrown in this case");
+ // We don't return the format that the color should be in:
+ // that's up to CSSOM. Thus we normalize before comparing.
+ assert_equals(normalizeColor(actualQueryResults[command][i]),
+ expectedQueryResults[command][i],
+ "Wrong result returned (after color normalization)");
+ } else {
+ assert_false(i in actualQueryExceptions[command],
+ "An exception must not be thrown in this case");
+ assert_equals(actualQueryResults[command][i],
+ expectedQueryResults[command][i],
+ "Wrong result returned");
+ }
+ }, testName + " " + descriptions[i]);
+ }
+ }
+
+ // Silly Firefox
+ document.body.removeAttribute("bgcolor");
+}
+//@}
+
+/**
+ * Return a string like '<body bgcolor="#FFFFFF">'.
+ */
+function formatStartTag(el) {
+//@{
+ var ret = "<" + el.tagName.toLowerCase();
+ for (var i = 0; i < el.attributes.length; i++) {
+ ret += " " + el.attributes[i].name + '="';
+ ret += el.attributes[i].value.replace(/\&/g, "&")
+ .replace(/"/g, """);
+ ret += '"';
+ }
+ return ret + ">";
+}
+//@}
+
// vim: foldmarker=@{,@} foldmethod=marker