| OLD | NEW |
| (Empty) |
| 1 <!DOCTYPE html> | |
| 2 <html> | |
| 3 <head> | |
| 4 <title>Range merging with Selection.addRange()</title> | |
| 5 <script src="../../resources/js-test.js"></script> | |
| 6 </head> | |
| 7 <body> | |
| 8 <script> | |
| 9 description('Selection.addRange() should properly merge intersecting (and don\'t
merge discrete) ranges.'); | |
| 10 | |
| 11 var selection = window.getSelection(); | |
| 12 | |
| 13 // Utility functions: | |
| 14 function createPosition(container, offset) | |
| 15 { | |
| 16 return {'container': container, 'offset': offset}; | |
| 17 } | |
| 18 | |
| 19 function createRange(startPosition, endPosition) | |
| 20 { | |
| 21 var range = new Range(); | |
| 22 range.setStart(startPosition.container, startPosition.offset); | |
| 23 range.setEnd(endPosition.container, endPosition.offset); | |
| 24 return range; | |
| 25 } | |
| 26 | |
| 27 function nodeToString(node) | |
| 28 { | |
| 29 switch (node.nodeType) { | |
| 30 case Node.ELEMENT_NODE: | |
| 31 return '[<' + node.tagName + '>: #' + node.id + ']'; | |
| 32 case Node.TEXT_NODE: | |
| 33 return '[Text: ' + node.data + ']'; | |
| 34 default: | |
| 35 return node.toString(); | |
| 36 } | |
| 37 } | |
| 38 | |
| 39 function positionToString(position) | |
| 40 { | |
| 41 return '(' + nodeToString(position.container) + ', ' + position.offset + ')'
; | |
| 42 } | |
| 43 | |
| 44 function selectionShouldBe(expectedStartPosition, expectedEndPosition) | |
| 45 { | |
| 46 var range = selection.getRangeAt(0); | |
| 47 var actualStartPosition = createPosition(range.startContainer, range.startOf
fset); | |
| 48 var actualEndPosition = createPosition(range.endContainer, range.endOffset); | |
| 49 if (actualStartPosition.container === expectedStartPosition.container | |
| 50 && actualStartPosition.offset === expectedStartPosition.offset | |
| 51 && actualEndPosition.container === expectedEndPosition.container | |
| 52 && actualEndPosition.offset === expectedEndPosition.offset) { | |
| 53 testPassed('Selection was: start = ' + positionToString(expectedStartPos
ition) + ', end = ' + positionToString(expectedEndPosition)); | |
| 54 } else { | |
| 55 testFailed('Selection should be: start = ' + positionToString(expectedSt
artPosition) + ', end = ' + positionToString(expectedEndPosition) + '\nbut was:
start = ' + positionToString(actualStartPosition) + ', end = ' + positionToStrin
g(actualEndPosition)); | |
| 56 } | |
| 57 } | |
| 58 | |
| 59 function runSingleTest(testFunction, initializePositionsFunction, containerIsEdi
table) | |
| 60 { | |
| 61 selection.removeAllRanges(); | |
| 62 var container = document.createElement('div'); | |
| 63 container.id = 'container'; | |
| 64 if (containerIsEditable) | |
| 65 container.contentEditable = true; | |
| 66 document.body.appendChild(container); | |
| 67 var positions = initializePositionsFunction(container); | |
| 68 debug('Running: ' + testFunction.name + ' (initializePositionsFunction = ' +
initializePositionsFunction.name + ', containerIsEditable = ' + containerIsEdit
able + ')'); | |
| 69 testFunction(positions); | |
| 70 document.body.removeChild(container); | |
| 71 } | |
| 72 | |
| 73 // Actual tests: | |
| 74 | |
| 75 // To have better coverage over the possible code paths, each test is parametari
zed over four document positions; | |
| 76 // these positions are guaranteed to be ordered in the document order, but each
position may vary in each test run. | |
| 77 // | |
| 78 // You can assume the selection is cleared before each test run. | |
| 79 | |
| 80 function testExpandLeftToRight(positions) | |
| 81 { | |
| 82 selection.addRange(createRange(positions[0], positions[2])); | |
| 83 selection.addRange(createRange(positions[1], positions[3])); | |
| 84 selectionShouldBe(positions[0], positions[3]); | |
| 85 } | |
| 86 | |
| 87 function testExpandRightToLeft(positions) | |
| 88 { | |
| 89 selection.addRange(createRange(positions[1], positions[3])); | |
| 90 selection.addRange(createRange(positions[0], positions[2])); | |
| 91 selectionShouldBe(positions[0], positions[3]); | |
| 92 } | |
| 93 | |
| 94 function testExpandLeftToRightAdjacent(positions) | |
| 95 { | |
| 96 selection.addRange(createRange(positions[1], positions[2])); | |
| 97 selection.addRange(createRange(positions[2], positions[3])); | |
| 98 selectionShouldBe(positions[1], positions[3]); | |
| 99 } | |
| 100 | |
| 101 function testExpandRightToLeftAdjacent(positions) | |
| 102 { | |
| 103 selection.addRange(createRange(positions[1], positions[2])); | |
| 104 selection.addRange(createRange(positions[0], positions[1])); | |
| 105 selectionShouldBe(positions[0], positions[2]); | |
| 106 } | |
| 107 | |
| 108 function testExpandBothEnds(positions) | |
| 109 { | |
| 110 selection.addRange(createRange(positions[1], positions[2])); | |
| 111 selection.addRange(createRange(positions[0], positions[3])); | |
| 112 selectionShouldBe(positions[0], positions[3]); | |
| 113 } | |
| 114 | |
| 115 function testDontExpand(positions) | |
| 116 { | |
| 117 selection.addRange(createRange(positions[0], positions[3])); | |
| 118 selection.addRange(createRange(positions[1], positions[2])); | |
| 119 selectionShouldBe(positions[0], positions[3]); | |
| 120 } | |
| 121 | |
| 122 function testAddSameRange(positions) | |
| 123 { | |
| 124 selection.addRange(createRange(positions[1], positions[2])); | |
| 125 selection.addRange(createRange(positions[1], positions[2])); | |
| 126 selectionShouldBe(positions[1], positions[2]); | |
| 127 } | |
| 128 | |
| 129 function testRejectDistantRangeAtRight(positions) | |
| 130 { | |
| 131 selection.addRange(createRange(positions[0], positions[1])); | |
| 132 selection.addRange(createRange(positions[2], positions[3])); | |
| 133 selectionShouldBe(positions[0], positions[1]); | |
| 134 } | |
| 135 | |
| 136 function testRejectDistantRangeAtLeft(positions) | |
| 137 { | |
| 138 selection.addRange(createRange(positions[2], positions[3])); | |
| 139 selection.addRange(createRange(positions[0], positions[1])); | |
| 140 selectionShouldBe(positions[2], positions[3]); | |
| 141 } | |
| 142 | |
| 143 function testRejectDistantCollapsedRangeAtRight(positions) | |
| 144 { | |
| 145 selection.addRange(createRange(positions[0], positions[1])); | |
| 146 selection.addRange(createRange(positions[2], positions[2])); | |
| 147 selectionShouldBe(positions[0], positions[1]); | |
| 148 } | |
| 149 | |
| 150 function testRejectDistantCollapsedRangeAtLeft(positions) | |
| 151 { | |
| 152 selection.addRange(createRange(positions[2], positions[3])); | |
| 153 selection.addRange(createRange(positions[1], positions[1])); | |
| 154 selectionShouldBe(positions[2], positions[3]); | |
| 155 } | |
| 156 | |
| 157 // Position initializers: | |
| 158 | |
| 159 // Each initializer function takes an argument |container| which denotes the roo
t element which can be filled with | |
| 160 // arbitrary contents. This element is created and added to the document before
each test run, and removed from | |
| 161 // the document after each test run. | |
| 162 | |
| 163 function initializeTextPositions(container) | |
| 164 { | |
| 165 container.innerHTML = '12345'; | |
| 166 var text = container.firstChild; | |
| 167 return [createPosition(text, 1), createPosition(text, 2), createPosition(tex
t, 3), createPosition(text, 4)]; | |
| 168 } | |
| 169 | |
| 170 function initializeOuterElementPositions(container) | |
| 171 { | |
| 172 container.innerHTML = '<span id="a">1</span><span id="b">2</span><span id="c
">3</span><span id="d">4</span><span id="e">5</span>'; | |
| 173 return [createPosition(container, 1), createPosition(container, 2), createPo
sition(container, 3), createPosition(container, 4)]; | |
| 174 } | |
| 175 | |
| 176 function initializeInnerElementPositions(container) | |
| 177 { | |
| 178 container.innerHTML = '<span id="a">1</span><span id="b">2</span><span id="c
">3</span><span id="d">4</span><span id="e">5</span>'; | |
| 179 return [createPosition(container.childNodes[1], 0), createPosition(container
.childNodes[2], 0), createPosition(container.childNodes[3], 0), createPosition(c
ontainer.childNodes[4], 0)]; | |
| 180 } | |
| 181 | |
| 182 function initializeVisiblyEquivalentPositionsBeforeNodes(container) | |
| 183 { | |
| 184 container.innerHTML = '<span id="a"><span id="b"><span id="c"></span></span>
</span>'; | |
| 185 return [createPosition(container, 0), createPosition(container.firstChild, 0
), createPosition(container.firstChild.firstChild, 0), createPosition(container.
firstChild.firstChild.firstChild, 0)]; | |
| 186 } | |
| 187 | |
| 188 function initializeVisiblyEquivalentPositionsAfterNodes(container) | |
| 189 { | |
| 190 container.innerHTML = '<span id="a"><span id="b"><span id="c"></span></span>
</span>'; | |
| 191 return [createPosition(container.firstChild.firstChild.firstChild, 0), creat
ePosition(container.firstChild.firstChild, 1), createPosition(container.firstChi
ld, 1), createPosition(container, 1)]; | |
| 192 } | |
| 193 | |
| 194 var tests = [ | |
| 195 testExpandLeftToRight, | |
| 196 testExpandRightToLeft, | |
| 197 testExpandLeftToRightAdjacent, | |
| 198 testExpandRightToLeftAdjacent, | |
| 199 testExpandBothEnds, | |
| 200 testDontExpand, | |
| 201 testAddSameRange, | |
| 202 testRejectDistantRangeAtRight, | |
| 203 testRejectDistantRangeAtLeft, | |
| 204 testRejectDistantCollapsedRangeAtRight, | |
| 205 testRejectDistantCollapsedRangeAtLeft | |
| 206 ]; | |
| 207 var positionInitializers = [ | |
| 208 initializeTextPositions, | |
| 209 initializeOuterElementPositions, | |
| 210 initializeInnerElementPositions, | |
| 211 initializeVisiblyEquivalentPositionsBeforeNodes, | |
| 212 initializeVisiblyEquivalentPositionsAfterNodes | |
| 213 ]; | |
| 214 | |
| 215 tests.forEach(function (testFunction) { | |
| 216 positionInitializers.forEach(function (initializePositionsFunction) { | |
| 217 [false, true].forEach(function (containerIsEditable) { | |
| 218 runSingleTest(testFunction, initializePositionsFunction, containerIs
Editable); | |
| 219 }); | |
| 220 }); | |
| 221 }); | |
| 222 </script> | |
| 223 </body> | |
| 224 </html> | |
| OLD | NEW |