Range-extractContents.html (10505B)
1 <!doctype html> 2 <title>Range.extractContents() tests</title> 3 <link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name> 4 <meta name=timeout content=long> 5 <p>To debug test failures, add a query parameter "subtest" with the test id (like 6 "?subtest=5"). Only that test will be run. Then you can look at the resulting 7 iframe in the DOM. 8 <div id=log></div> 9 <script src=/resources/testharness.js></script> 10 <script src=/resources/testharnessreport.js></script> 11 <script src=../common.js></script> 12 <script> 13 "use strict"; 14 15 testDiv.parentNode.removeChild(testDiv); 16 17 var actualIframe = document.createElement("iframe"); 18 actualIframe.style.display = "none"; 19 document.body.appendChild(actualIframe); 20 21 var expectedIframe = document.createElement("iframe"); 22 expectedIframe.style.display = "none"; 23 document.body.appendChild(expectedIframe); 24 25 function restoreIframe(iframe, i) { 26 // Most of this function is designed to work around the fact that Opera 27 // doesn't let you add a doctype to a document that no longer has one, in 28 // any way I can figure out. I eventually compromised on something that 29 // will still let Opera pass most tests that don't actually involve 30 // doctypes. 31 while (iframe.contentDocument.firstChild 32 && iframe.contentDocument.firstChild.nodeType != Node.DOCUMENT_TYPE_NODE) { 33 iframe.contentDocument.removeChild(iframe.contentDocument.firstChild); 34 } 35 36 while (iframe.contentDocument.lastChild 37 && iframe.contentDocument.lastChild.nodeType != Node.DOCUMENT_TYPE_NODE) { 38 iframe.contentDocument.removeChild(iframe.contentDocument.lastChild); 39 } 40 41 if (!iframe.contentDocument.firstChild) { 42 // This will throw an exception in Opera if we reach here, which is why 43 // I try to avoid it. It will never happen in a browser that obeys the 44 // spec, so it's really just insurance. I don't think it actually gets 45 // hit by anything. 46 iframe.contentDocument.appendChild(iframe.contentDocument.implementation.createDocumentType("html", "", "")); 47 } 48 iframe.contentDocument.appendChild(referenceDoc.documentElement.cloneNode(true)); 49 iframe.contentWindow.setupRangeTests(); 50 iframe.contentWindow.testRangeInput = testRanges[i]; 51 iframe.contentWindow.run(); 52 } 53 54 function testExtractContents(i) { 55 restoreIframe(actualIframe, i); 56 restoreIframe(expectedIframe, i); 57 58 var actualRange = actualIframe.contentWindow.testRange; 59 var expectedRange = expectedIframe.contentWindow.testRange; 60 var actualFrag, expectedFrag; 61 var actualRoots, expectedRoots; 62 63 domTests[i].step(function() { 64 assert_equals(actualIframe.contentWindow.unexpectedException, null, 65 "Unexpected exception thrown when setting up Range for actual extractContents()"); 66 assert_equals(expectedIframe.contentWindow.unexpectedException, null, 67 "Unexpected exception thrown when setting up Range for simulated extractContents()"); 68 assert_equals(typeof actualRange, "object", 69 "typeof Range produced in actual iframe"); 70 assert_equals(typeof expectedRange, "object", 71 "typeof Range produced in expected iframe"); 72 73 // Just to be pedantic, we'll test not only that the tree we're 74 // modifying is the same in expected vs. actual, but also that all the 75 // nodes originally in it were the same. Typically some nodes will 76 // become detached when the algorithm is run, but they still exist and 77 // references can still be kept to them, so they should also remain the 78 // same. 79 // 80 // We initialize the list to all nodes, and later on remove all the 81 // ones which still have parents, since the parents will presumably be 82 // tested for isEqualNode() and checking the children would be 83 // redundant. 84 var actualAllNodes = []; 85 var node = furthestAncestor(actualRange.startContainer); 86 do { 87 actualAllNodes.push(node); 88 } while (node = nextNode(node)); 89 90 var expectedAllNodes = []; 91 var node = furthestAncestor(expectedRange.startContainer); 92 do { 93 expectedAllNodes.push(node); 94 } while (node = nextNode(node)); 95 96 expectedFrag = myExtractContents(expectedRange); 97 if (typeof expectedFrag == "string") { 98 assert_throws_dom( 99 expectedFrag, 100 actualIframe.contentWindow.DOMException, 101 function() { 102 actualRange.extractContents(); 103 } 104 ); 105 } else { 106 actualFrag = actualRange.extractContents(); 107 } 108 109 actualRoots = []; 110 for (var j = 0; j < actualAllNodes.length; j++) { 111 if (!actualAllNodes[j].parentNode) { 112 actualRoots.push(actualAllNodes[j]); 113 } 114 } 115 116 expectedRoots = []; 117 for (var j = 0; j < expectedAllNodes.length; j++) { 118 if (!expectedAllNodes[j].parentNode) { 119 expectedRoots.push(expectedAllNodes[j]); 120 } 121 } 122 123 for (var j = 0; j < actualRoots.length; j++) { 124 assertNodesEqual(actualRoots[j], expectedRoots[j], j ? "detached node #" + j : "tree root"); 125 126 if (j == 0) { 127 // Clearly something is wrong if the node lists are different 128 // lengths. We want to report this only after we've already 129 // checked the main tree for equality, though, so it doesn't 130 // mask more interesting errors. 131 assert_equals(actualRoots.length, expectedRoots.length, 132 "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)"); 133 } 134 } 135 }); 136 domTests[i].done(); 137 138 positionTests[i].step(function() { 139 assert_equals(actualIframe.contentWindow.unexpectedException, null, 140 "Unexpected exception thrown when setting up Range for actual extractContents()"); 141 assert_equals(expectedIframe.contentWindow.unexpectedException, null, 142 "Unexpected exception thrown when setting up Range for simulated extractContents()"); 143 assert_equals(typeof actualRange, "object", 144 "typeof Range produced in actual iframe"); 145 assert_equals(typeof expectedRange, "object", 146 "typeof Range produced in expected iframe"); 147 148 assert_true(actualRoots[0].isEqualNode(expectedRoots[0]), 149 "The resulting DOMs were not equal, so comparing positions makes no sense"); 150 151 if (typeof expectedFrag == "string") { 152 // It's no longer true that, e.g., startContainer and endContainer 153 // must always be the same 154 return; 155 } 156 assert_equals(actualRange.startContainer, actualRange.endContainer, 157 "startContainer and endContainer must always be the same after extractContents()"); 158 assert_equals(actualRange.startOffset, actualRange.endOffset, 159 "startOffset and endOffset must always be the same after extractContents()"); 160 assert_equals(expectedRange.startContainer, expectedRange.endContainer, 161 "Test bug! Expected startContainer and endContainer must always be the same after extractContents()"); 162 assert_equals(expectedRange.startOffset, expectedRange.endOffset, 163 "Test bug! Expected startOffset and endOffset must always be the same after extractContents()"); 164 165 assert_equals(actualRange.startOffset, expectedRange.startOffset, 166 "Unexpected startOffset after extractContents()"); 167 // How do we decide that the two nodes are equal, since they're in 168 // different trees? Since the DOMs are the same, it's enough to check 169 // that the index in the parent is the same all the way up the tree. 170 // But we can first cheat by just checking they're actually equal. 171 assert_true(actualRange.startContainer.isEqualNode(expectedRange.startContainer), 172 "Unexpected startContainer after extractContents(), expected " + 173 expectedRange.startContainer.nodeName.toLowerCase() + " but got " + 174 actualRange.startContainer.nodeName.toLowerCase()); 175 var currentActual = actualRange.startContainer; 176 var currentExpected = expectedRange.startContainer; 177 var actual = ""; 178 var expected = ""; 179 while (currentActual && currentExpected) { 180 actual = indexOf(currentActual) + "-" + actual; 181 expected = indexOf(currentExpected) + "-" + expected; 182 183 currentActual = currentActual.parentNode; 184 currentExpected = currentExpected.parentNode; 185 } 186 actual = actual.substr(0, actual.length - 1); 187 expected = expected.substr(0, expected.length - 1); 188 assert_equals(actual, expected, 189 "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"); 190 }); 191 positionTests[i].done(); 192 193 fragTests[i].step(function() { 194 assert_equals(actualIframe.contentWindow.unexpectedException, null, 195 "Unexpected exception thrown when setting up Range for actual extractContents()"); 196 assert_equals(expectedIframe.contentWindow.unexpectedException, null, 197 "Unexpected exception thrown when setting up Range for simulated extractContents()"); 198 assert_equals(typeof actualRange, "object", 199 "typeof Range produced in actual iframe"); 200 assert_equals(typeof expectedRange, "object", 201 "typeof Range produced in expected iframe"); 202 203 if (typeof expectedFrag == "string") { 204 // Comparing makes no sense 205 return; 206 } 207 assertNodesEqual(actualFrag, expectedFrag, 208 "returned fragment"); 209 }); 210 fragTests[i].done(); 211 } 212 213 // First test a detached Range, synchronously 214 test(function() { 215 var range = document.createRange(); 216 range.detach(); 217 assert_array_equals(range.extractContents().childNodes, []); 218 }, "Detached Range"); 219 220 var iStart = 0; 221 var iStop = testRanges.length; 222 223 if (/subtest=[0-9]+/.test(location.search)) { 224 var matches = /subtest=([0-9]+)/.exec(location.search); 225 iStart = Number(matches[1]); 226 iStop = Number(matches[1]) + 1; 227 } 228 229 var domTests = []; 230 var positionTests = []; 231 var fragTests = []; 232 233 for (var i = iStart; i < iStop; i++) { 234 domTests[i] = async_test("Resulting DOM for range " + i + " " + testRanges[i]); 235 positionTests[i] = async_test("Resulting cursor position for range " + i + " " + testRanges[i]); 236 fragTests[i] = async_test("Returned fragment for range " + i + " " + testRanges[i]); 237 } 238 239 var referenceDoc = document.implementation.createHTMLDocument(""); 240 referenceDoc.removeChild(referenceDoc.documentElement); 241 242 actualIframe.onload = function() { 243 expectedIframe.onload = function() { 244 for (var i = iStart; i < iStop; i++) { 245 testExtractContents(i); 246 } 247 } 248 expectedIframe.src = "Range-test-iframe.html"; 249 referenceDoc.appendChild(actualIframe.contentDocument.documentElement.cloneNode(true)); 250 } 251 actualIframe.src = "Range-test-iframe.html"; 252 </script>