Add a split version of runtest.html
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Mon, 19 Sep 2011 12:12:05 -0600
changeset 589 b47a9462a361
parent 588 11bb09aa6b33
child 590 3a8b64badbdb
Add a split version of runtest.html

Fixes: http://www.w3.org/Bugs/Public/show_bug.cgi?id=14069
conformancetest/runtest.html
conformancetest/splitruntest.html
editing.html
source.html
tests.js
--- 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, "&amp;")
-			.replace(/"/g, "&quot;");
-		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-&mdash;-last-update-16-september-2011>Work in Progress &mdash; Last Update 16 September 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-19-september-2011>Work in Progress &mdash; Last Update 19 September 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;<a href=mailto:ayg@aryeh.name>ayg@aryeh.name</a>&gt;
@@ -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, "&amp;")
+			.replace(/"/g, "&quot;");
+		ret += '"';
+	}
+	return ret + ">";
+}
+//@}
+
 // vim: foldmarker=@{,@} foldmethod=marker