blob: 098837f0ce5e397fa3c41b66314f1f4ef9267c04 [file] [log] [blame]
<!doctype html>
<title>Range.extractContents() tests</title>
<link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name>
<meta name=timeout content=long>
<p>To debug test failures, add a query parameter "subtest" with the test id (like
"?subtest=5"). Only that test will be run. Then you can look at the resulting
iframe in the DOM.
<div id=log></div>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=../common.js></script>
<script>
"use strict";
testDiv.parentNode.removeChild(testDiv);
var actualIframe = document.createElement("iframe");
actualIframe.style.display = "none";
document.body.appendChild(actualIframe);
var expectedIframe = document.createElement("iframe");
expectedIframe.style.display = "none";
document.body.appendChild(expectedIframe);
function restoreIframe(iframe, i) {
// Most of this function is designed to work around the fact that Opera
// doesn't let you add a doctype to a document that no longer has one, in
// any way I can figure out. I eventually compromised on something that
// will still let Opera pass most tests that don't actually involve
// doctypes.
while (iframe.contentDocument.firstChild
&& iframe.contentDocument.firstChild.nodeType != Node.DOCUMENT_TYPE_NODE) {
iframe.contentDocument.removeChild(iframe.contentDocument.firstChild);
}
while (iframe.contentDocument.lastChild
&& iframe.contentDocument.lastChild.nodeType != Node.DOCUMENT_TYPE_NODE) {
iframe.contentDocument.removeChild(iframe.contentDocument.lastChild);
}
if (!iframe.contentDocument.firstChild) {
// This will throw an exception in Opera if we reach here, which is why
// I try to avoid it. It will never happen in a browser that obeys the
// spec, so it's really just insurance. I don't think it actually gets
// hit by anything.
iframe.contentDocument.appendChild(iframe.contentDocument.implementation.createDocumentType("html", "", ""));
}
iframe.contentDocument.appendChild(referenceDoc.documentElement.cloneNode(true));
iframe.contentWindow.setupRangeTests();
iframe.contentWindow.testRangeInput = testRanges[i];
iframe.contentWindow.run();
}
function testExtractContents(i) {
restoreIframe(actualIframe, i);
restoreIframe(expectedIframe, i);
var actualRange = actualIframe.contentWindow.testRange;
var expectedRange = expectedIframe.contentWindow.testRange;
var actualFrag, expectedFrag;
var actualRoots, expectedRoots;
domTests[i].step(function() {
assert_equals(actualIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for actual extractContents()");
assert_equals(expectedIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for simulated extractContents()");
assert_equals(typeof actualRange, "object",
"typeof Range produced in actual iframe");
assert_equals(typeof expectedRange, "object",
"typeof Range produced in expected iframe");
// Just to be pedantic, we'll test not only that the tree we're
// modifying is the same in expected vs. actual, but also that all the
// nodes originally in it were the same. Typically some nodes will
// become detached when the algorithm is run, but they still exist and
// references can still be kept to them, so they should also remain the
// same.
//
// We initialize the list to all nodes, and later on remove all the
// ones which still have parents, since the parents will presumably be
// tested for isEqualNode() and checking the children would be
// redundant.
var actualAllNodes = [];
var node = furthestAncestor(actualRange.startContainer);
do {
actualAllNodes.push(node);
} while (node = nextNode(node));
var expectedAllNodes = [];
var node = furthestAncestor(expectedRange.startContainer);
do {
expectedAllNodes.push(node);
} while (node = nextNode(node));
expectedFrag = myExtractContents(expectedRange);
if (typeof expectedFrag == "string") {
assert_throws(expectedFrag, function() {
actualRange.extractContents();
});
} else {
actualFrag = actualRange.extractContents();
}
actualRoots = [];
for (var j = 0; j < actualAllNodes.length; j++) {
if (!actualAllNodes[j].parentNode) {
actualRoots.push(actualAllNodes[j]);
}
}
expectedRoots = [];
for (var j = 0; j < expectedAllNodes.length; j++) {
if (!expectedAllNodes[j].parentNode) {
expectedRoots.push(expectedAllNodes[j]);
}
}
for (var j = 0; j < actualRoots.length; j++) {
assertNodesEqual(actualRoots[j], expectedRoots[j], j ? "detached node #" + j : "tree root");
if (j == 0) {
// Clearly something is wrong if the node lists are different
// lengths. We want to report this only after we've already
// checked the main tree for equality, though, so it doesn't
// mask more interesting errors.
assert_equals(actualRoots.length, expectedRoots.length,
"Actual and expected DOMs were broken up into a different number of pieces by extractContents() (this probably means you created or detached nodes when you weren't supposed to)");
}
}
});
domTests[i].done();
positionTests[i].step(function() {
assert_equals(actualIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for actual extractContents()");
assert_equals(expectedIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for simulated extractContents()");
assert_equals(typeof actualRange, "object",
"typeof Range produced in actual iframe");
assert_equals(typeof expectedRange, "object",
"typeof Range produced in expected iframe");
assert_true(actualRoots[0].isEqualNode(expectedRoots[0]),
"The resulting DOMs were not equal, so comparing positions makes no sense");
if (typeof expectedFrag == "string") {
// It's no longer true that, e.g., startContainer and endContainer
// must always be the same
return;
}
assert_equals(actualRange.startContainer, actualRange.endContainer,
"startContainer and endContainer must always be the same after extractContents()");
assert_equals(actualRange.startOffset, actualRange.endOffset,
"startOffset and endOffset must always be the same after extractContents()");
assert_equals(expectedRange.startContainer, expectedRange.endContainer,
"Test bug! Expected startContainer and endContainer must always be the same after extractContents()");
assert_equals(expectedRange.startOffset, expectedRange.endOffset,
"Test bug! Expected startOffset and endOffset must always be the same after extractContents()");
assert_equals(actualRange.startOffset, expectedRange.startOffset,
"Unexpected startOffset after extractContents()");
// How do we decide that the two nodes are equal, since they're in
// different trees? Since the DOMs are the same, it's enough to check
// that the index in the parent is the same all the way up the tree.
// But we can first cheat by just checking they're actually equal.
assert_true(actualRange.startContainer.isEqualNode(expectedRange.startContainer),
"Unexpected startContainer after extractContents(), expected " +
expectedRange.startContainer.nodeName.toLowerCase() + " but got " +
actualRange.startContainer.nodeName.toLowerCase());
var currentActual = actualRange.startContainer;
var currentExpected = expectedRange.startContainer;
var actual = "";
var expected = "";
while (currentActual && currentExpected) {
actual = indexOf(currentActual) + "-" + actual;
expected = indexOf(currentExpected) + "-" + expected;
currentActual = currentActual.parentNode;
currentExpected = currentExpected.parentNode;
}
actual = actual.substr(0, actual.length - 1);
expected = expected.substr(0, expected.length - 1);
assert_equals(actual, expected,
"startContainer superficially looks right but is actually the wrong node if you trace back its index in all its ancestors (I'm surprised this actually happened");
});
positionTests[i].done();
fragTests[i].step(function() {
assert_equals(actualIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for actual extractContents()");
assert_equals(expectedIframe.contentWindow.unexpectedException, null,
"Unexpected exception thrown when setting up Range for simulated extractContents()");
assert_equals(typeof actualRange, "object",
"typeof Range produced in actual iframe");
assert_equals(typeof expectedRange, "object",
"typeof Range produced in expected iframe");
if (typeof expectedFrag == "string") {
// Comparing makes no sense
return;
}
assertNodesEqual(actualFrag, expectedFrag,
"returned fragment");
});
fragTests[i].done();
}
// First test a detached Range, synchronously
test(function() {
var range = document.createRange();
range.detach();
assert_array_equals(range.extractContents().childNodes, []);
}, "Detached Range");
var iStart = 0;
var iStop = testRanges.length;
if (/subtest=[0-9]+/.test(location.search)) {
var matches = /subtest=([0-9]+)/.exec(location.search);
iStart = Number(matches[1]);
iStop = Number(matches[1]) + 1;
}
var domTests = [];
var positionTests = [];
var fragTests = [];
for (var i = iStart; i < iStop; i++) {
domTests[i] = async_test("Resulting DOM for range " + i + " " + testRanges[i]);
positionTests[i] = async_test("Resulting cursor position for range " + i + " " + testRanges[i]);
fragTests[i] = async_test("Returned fragment for range " + i + " " + testRanges[i]);
}
var referenceDoc = document.implementation.createHTMLDocument("");
referenceDoc.removeChild(referenceDoc.documentElement);
actualIframe.onload = function() {
expectedIframe.onload = function() {
for (var i = iStart; i < iStop; i++) {
testExtractContents(i);
}
}
expectedIframe.src = "Range-test-iframe.html";
referenceDoc.appendChild(actualIframe.contentDocument.documentElement.cloneNode(true));
}
actualIframe.src = "Range-test-iframe.html";
</script>