| Index: LayoutTests/editing/selection/addRange-merging.html
|
| diff --git a/LayoutTests/editing/selection/addRange-merging.html b/LayoutTests/editing/selection/addRange-merging.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a4db81c85cccdceddeeb35fd43d3467200567459
|
| --- /dev/null
|
| +++ b/LayoutTests/editing/selection/addRange-merging.html
|
| @@ -0,0 +1,224 @@
|
| +<!DOCTYPE html>
|
| +<html>
|
| +<head>
|
| +<title>Range merging with Selection.addRange()</title>
|
| +<script src="../../resources/js-test.js"></script>
|
| +</head>
|
| +<body>
|
| +<script>
|
| +description('Selection.addRange() should properly merge intersecting (and don\'t merge discrete) ranges.');
|
| +
|
| +var selection = window.getSelection();
|
| +
|
| +// Utility functions:
|
| +function createPosition(container, offset)
|
| +{
|
| + return {'container': container, 'offset': offset};
|
| +}
|
| +
|
| +function createRange(startPosition, endPosition)
|
| +{
|
| + var range = new Range();
|
| + range.setStart(startPosition.container, startPosition.offset);
|
| + range.setEnd(endPosition.container, endPosition.offset);
|
| + return range;
|
| +}
|
| +
|
| +function nodeToString(node)
|
| +{
|
| + switch (node.nodeType) {
|
| + case Node.ELEMENT_NODE:
|
| + return '[<' + node.tagName + '>: #' + node.id + ']';
|
| + case Node.TEXT_NODE:
|
| + return '[Text: ' + node.data + ']';
|
| + default:
|
| + return node.toString();
|
| + }
|
| +}
|
| +
|
| +function positionToString(position)
|
| +{
|
| + return '(' + nodeToString(position.container) + ', ' + position.offset + ')';
|
| +}
|
| +
|
| +function selectionShouldBe(expectedStartPosition, expectedEndPosition)
|
| +{
|
| + var range = selection.getRangeAt(0);
|
| + var actualStartPosition = createPosition(range.startContainer, range.startOffset);
|
| + var actualEndPosition = createPosition(range.endContainer, range.endOffset);
|
| + if (actualStartPosition.container === expectedStartPosition.container
|
| + && actualStartPosition.offset === expectedStartPosition.offset
|
| + && actualEndPosition.container === expectedEndPosition.container
|
| + && actualEndPosition.offset === expectedEndPosition.offset) {
|
| + testPassed('Selection was: start = ' + positionToString(expectedStartPosition) + ', end = ' + positionToString(expectedEndPosition));
|
| + } else {
|
| + testFailed('Selection should be: start = ' + positionToString(expectedStartPosition) + ', end = ' + positionToString(expectedEndPosition) + '\nbut was: start = ' + positionToString(actualStartPosition) + ', end = ' + positionToString(actualEndPosition));
|
| + }
|
| +}
|
| +
|
| +function runSingleTest(testFunction, initializePositionsFunction, containerIsEditable)
|
| +{
|
| + selection.removeAllRanges();
|
| + var container = document.createElement('div');
|
| + container.id = 'container';
|
| + if (containerIsEditable)
|
| + container.contentEditable = true;
|
| + document.body.appendChild(container);
|
| + var positions = initializePositionsFunction(container);
|
| + debug('Running: ' + testFunction.name + ' (initializePositionsFunction = ' + initializePositionsFunction.name + ', containerIsEditable = ' + containerIsEditable + ')');
|
| + testFunction(positions);
|
| + document.body.removeChild(container);
|
| +}
|
| +
|
| +// Actual tests:
|
| +
|
| +// To have better coverage over the possible code paths, each test is parametarized over four document positions;
|
| +// these positions are guaranteed to be ordered in the document order, but each position may vary in each test run.
|
| +//
|
| +// You can assume the selection is cleared before each test run.
|
| +
|
| +function testExpandLeftToRight(positions)
|
| +{
|
| + selection.addRange(createRange(positions[0], positions[2]));
|
| + selection.addRange(createRange(positions[1], positions[3]));
|
| + selectionShouldBe(positions[0], positions[3]);
|
| +}
|
| +
|
| +function testExpandRightToLeft(positions)
|
| +{
|
| + selection.addRange(createRange(positions[1], positions[3]));
|
| + selection.addRange(createRange(positions[0], positions[2]));
|
| + selectionShouldBe(positions[0], positions[3]);
|
| +}
|
| +
|
| +function testExpandLeftToRightAdjacent(positions)
|
| +{
|
| + selection.addRange(createRange(positions[1], positions[2]));
|
| + selection.addRange(createRange(positions[2], positions[3]));
|
| + selectionShouldBe(positions[1], positions[3]);
|
| +}
|
| +
|
| +function testExpandRightToLeftAdjacent(positions)
|
| +{
|
| + selection.addRange(createRange(positions[1], positions[2]));
|
| + selection.addRange(createRange(positions[0], positions[1]));
|
| + selectionShouldBe(positions[0], positions[2]);
|
| +}
|
| +
|
| +function testExpandBothEnds(positions)
|
| +{
|
| + selection.addRange(createRange(positions[1], positions[2]));
|
| + selection.addRange(createRange(positions[0], positions[3]));
|
| + selectionShouldBe(positions[0], positions[3]);
|
| +}
|
| +
|
| +function testDontExpand(positions)
|
| +{
|
| + selection.addRange(createRange(positions[0], positions[3]));
|
| + selection.addRange(createRange(positions[1], positions[2]));
|
| + selectionShouldBe(positions[0], positions[3]);
|
| +}
|
| +
|
| +function testAddSameRange(positions)
|
| +{
|
| + selection.addRange(createRange(positions[1], positions[2]));
|
| + selection.addRange(createRange(positions[1], positions[2]));
|
| + selectionShouldBe(positions[1], positions[2]);
|
| +}
|
| +
|
| +function testRejectDistantRangeAtRight(positions)
|
| +{
|
| + selection.addRange(createRange(positions[0], positions[1]));
|
| + selection.addRange(createRange(positions[2], positions[3]));
|
| + selectionShouldBe(positions[0], positions[1]);
|
| +}
|
| +
|
| +function testRejectDistantRangeAtLeft(positions)
|
| +{
|
| + selection.addRange(createRange(positions[2], positions[3]));
|
| + selection.addRange(createRange(positions[0], positions[1]));
|
| + selectionShouldBe(positions[2], positions[3]);
|
| +}
|
| +
|
| +function testRejectDistantCollapsedRangeAtRight(positions)
|
| +{
|
| + selection.addRange(createRange(positions[0], positions[1]));
|
| + selection.addRange(createRange(positions[2], positions[2]));
|
| + selectionShouldBe(positions[0], positions[1]);
|
| +}
|
| +
|
| +function testRejectDistantCollapsedRangeAtLeft(positions)
|
| +{
|
| + selection.addRange(createRange(positions[2], positions[3]));
|
| + selection.addRange(createRange(positions[1], positions[1]));
|
| + selectionShouldBe(positions[2], positions[3]);
|
| +}
|
| +
|
| +// Position initializers:
|
| +
|
| +// Each initializer function takes an argument |container| which denotes the root element which can be filled with
|
| +// arbitrary contents. This element is created and added to the document before each test run, and removed from
|
| +// the document after each test run.
|
| +
|
| +function initializeTextPositions(container)
|
| +{
|
| + container.innerHTML = '12345';
|
| + var text = container.firstChild;
|
| + return [createPosition(text, 1), createPosition(text, 2), createPosition(text, 3), createPosition(text, 4)];
|
| +}
|
| +
|
| +function initializeOuterElementPositions(container)
|
| +{
|
| + 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>';
|
| + return [createPosition(container, 1), createPosition(container, 2), createPosition(container, 3), createPosition(container, 4)];
|
| +}
|
| +
|
| +function initializeInnerElementPositions(container)
|
| +{
|
| + 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>';
|
| + return [createPosition(container.childNodes[1], 0), createPosition(container.childNodes[2], 0), createPosition(container.childNodes[3], 0), createPosition(container.childNodes[4], 0)];
|
| +}
|
| +
|
| +function initializeVisiblyEquivalentPositionsBeforeNodes(container)
|
| +{
|
| + container.innerHTML = '<span id="a"><span id="b"><span id="c"></span></span></span>';
|
| + return [createPosition(container, 0), createPosition(container.firstChild, 0), createPosition(container.firstChild.firstChild, 0), createPosition(container.firstChild.firstChild.firstChild, 0)];
|
| +}
|
| +
|
| +function initializeVisiblyEquivalentPositionsAfterNodes(container)
|
| +{
|
| + container.innerHTML = '<span id="a"><span id="b"><span id="c"></span></span></span>';
|
| + return [createPosition(container.firstChild.firstChild.firstChild, 0), createPosition(container.firstChild.firstChild, 1), createPosition(container.firstChild, 1), createPosition(container, 1)];
|
| +}
|
| +
|
| +var tests = [
|
| + testExpandLeftToRight,
|
| + testExpandRightToLeft,
|
| + testExpandLeftToRightAdjacent,
|
| + testExpandRightToLeftAdjacent,
|
| + testExpandBothEnds,
|
| + testDontExpand,
|
| + testAddSameRange,
|
| + testRejectDistantRangeAtRight,
|
| + testRejectDistantRangeAtLeft,
|
| + testRejectDistantCollapsedRangeAtRight,
|
| + testRejectDistantCollapsedRangeAtLeft
|
| +];
|
| +var positionInitializers = [
|
| + initializeTextPositions,
|
| + initializeOuterElementPositions,
|
| + initializeInnerElementPositions,
|
| + initializeVisiblyEquivalentPositionsBeforeNodes,
|
| + initializeVisiblyEquivalentPositionsAfterNodes
|
| +];
|
| +
|
| +tests.forEach(function (testFunction) {
|
| + positionInitializers.forEach(function (initializePositionsFunction) {
|
| + [false, true].forEach(function (containerIsEditable) {
|
| + runSingleTest(testFunction, initializePositionsFunction, containerIsEditable);
|
| + });
|
| + });
|
| +});
|
| +</script>
|
| +</body>
|
| +</html>
|
|
|