// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.dom.RangeTest');
goog.setTestOnly('goog.dom.RangeTest');
goog.require('goog.dom');
goog.require('goog.dom.NodeType');
goog.require('goog.dom.Range');
goog.require('goog.dom.RangeType');
goog.require('goog.dom.TagName');
goog.require('goog.dom.TextRange');
goog.require('goog.dom.browserrange');
goog.require('goog.testing.dom');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
var assertRangeEquals = goog.testing.dom.assertRangeEquals;
function setUp() {
// Reset the focus; some tests may invalidate the focus to exercise various
// browser bugs.
var focusableElement = goog.dom.getElement('focusableElement');
focusableElement.focus();
focusableElement.blur();
}
function normalizeHtml(str) {
return str.toLowerCase()
.replace(/[\n\r\f"]/g, '')
.replace(/<\/li>/g, ''); // " for emacs
}
function testCreate() {
assertNotNull(
'Browser range object can be created for node',
goog.dom.Range.createFromNodeContents(goog.dom.getElement('test1')));
}
function testTableRange() {
var tr = goog.dom.getElement('cell').parentNode;
var range = goog.dom.Range.createFromNodeContents(tr);
assertEquals('Selection should have correct text', '12', range.getText());
assertEquals(
'Selection should have correct html fragment', '1
2',
normalizeHtml(range.getHtmlFragment()));
// TODO(robbyw): On IE the TR is included, on FF it is not.
// assertEquals('Selection should have correct valid html',
// ' | 1 | 2 |
',
// normalizeHtml(range.getValidHtml()));
assertEquals(
'Selection should have correct pastable html',
'',
normalizeHtml(range.getPastableHtml()));
}
function testUnorderedListRange() {
var ul = goog.dom.getElement('ulTest').firstChild;
var range = goog.dom.Range.createFromNodeContents(ul);
assertEquals(
'Selection should have correct html fragment', '12',
normalizeHtml(range.getHtmlFragment()));
// TODO(robbyw): On IE the UL is included, on FF it is not.
// assertEquals('Selection should have correct valid html',
// '12', normalizeHtml(range.getValidHtml()));
assertEquals(
'Selection should have correct pastable html', '',
normalizeHtml(range.getPastableHtml()));
}
function testOrderedListRange() {
var ol = goog.dom.getElement('olTest').firstChild;
var range = goog.dom.Range.createFromNodeContents(ol);
assertEquals(
'Selection should have correct html fragment', '12',
normalizeHtml(range.getHtmlFragment()));
// TODO(robbyw): On IE the OL is included, on FF it is not.
// assertEquals('Selection should have correct valid html',
// '12', normalizeHtml(range.getValidHtml()));
assertEquals(
'Selection should have correct pastable html', '- 1
- 2
',
normalizeHtml(range.getPastableHtml()));
}
function testCreateFromNodes() {
var start = goog.dom.getElement('test1').firstChild;
var end = goog.dom.getElement('br');
var range = goog.dom.Range.createFromNodes(start, 2, end, 0);
assertNotNull(
'Browser range object can be created for W3C node range', range);
assertEquals(
'Start node should be selected at start endpoint', start,
range.getStartNode());
assertEquals('Selection should start at offset 2', 2, range.getStartOffset());
assertEquals(
'Start node should be selected at anchor endpoint', start,
range.getAnchorNode());
assertEquals(
'Selection should be anchored at offset 2', 2, range.getAnchorOffset());
var div = goog.dom.getElement('test2');
assertEquals(
'DIV node should be selected at end endpoint', div, range.getEndNode());
assertEquals('Selection should end at offset 1', 1, range.getEndOffset());
assertEquals(
'DIV node should be selected at focus endpoint', div,
range.getFocusNode());
assertEquals(
'Selection should be focused at offset 1', 1, range.getFocusOffset());
assertTrue(
'Text content should be "xt\\s*abc"', /xt\s*abc/.test(range.getText()));
assertFalse('Nodes range is not collapsed', range.isCollapsed());
}
function testCreateControlRange() {
if (!goog.userAgent.IE) {
return;
}
var cr = document.body.createControlRange();
cr.addElement(goog.dom.getElement('logo'));
var range = goog.dom.Range.createFromBrowserRange(cr);
assertNotNull(
'Control range object can be created from browser range', range);
assertEquals(
'Created range is a control range', goog.dom.RangeType.CONTROL,
range.getType());
}
function testTextNode() {
var range = goog.dom.Range.createFromNodeContents(
goog.dom.getElement('test1').firstChild);
assertEquals(
'Created range is a text range', goog.dom.RangeType.TEXT,
range.getType());
assertEquals(
'Text node should be selected at start endpoint', 'Text',
range.getStartNode().nodeValue);
assertEquals('Selection should start at offset 0', 0, range.getStartOffset());
assertEquals(
'Text node should be selected at end endpoint', 'Text',
range.getEndNode().nodeValue);
assertEquals(
'Selection should end at offset 4', 'Text'.length, range.getEndOffset());
assertEquals(
'Container should be text node', goog.dom.NodeType.TEXT,
range.getContainer().nodeType);
assertEquals('Text content should be "Text"', 'Text', range.getText());
assertFalse('Text range is not collapsed', range.isCollapsed());
}
function testDiv() {
var range =
goog.dom.Range.createFromNodeContents(goog.dom.getElement('test2'));
assertEquals(
'Text node "abc" should be selected at start endpoint', 'abc',
range.getStartNode().nodeValue);
assertEquals('Selection should start at offset 0', 0, range.getStartOffset());
assertEquals(
'Text node "def" should be selected at end endpoint', 'def',
range.getEndNode().nodeValue);
assertEquals(
'Selection should end at offset 3', 'def'.length, range.getEndOffset());
assertEquals(
'Container should be DIV', goog.dom.getElement('test2'),
range.getContainer());
assertTrue(
'Div text content should be "abc\\s*def"',
/abc\s*def/.test(range.getText()));
assertFalse('Div range is not collapsed', range.isCollapsed());
}
function testEmptyNode() {
var range =
goog.dom.Range.createFromNodeContents(goog.dom.getElement('empty'));
assertEquals(
'DIV be selected at start endpoint', goog.dom.getElement('empty'),
range.getStartNode());
assertEquals('Selection should start at offset 0', 0, range.getStartOffset());
assertEquals(
'DIV should be selected at end endpoint', goog.dom.getElement('empty'),
range.getEndNode());
assertEquals('Selection should end at offset 0', 0, range.getEndOffset());
assertEquals(
'Container should be DIV', goog.dom.getElement('empty'),
range.getContainer());
assertEquals('Empty text content should be ""', '', range.getText());
assertTrue('Empty range is collapsed', range.isCollapsed());
}
function testCollapse() {
var range =
goog.dom.Range.createFromNodeContents(goog.dom.getElement('test2'));
assertFalse('Div range is not collapsed', range.isCollapsed());
range.collapse();
assertTrue(
'Div range is collapsed after call to empty()', range.isCollapsed());
range = goog.dom.Range.createFromNodeContents(goog.dom.getElement('empty'));
assertTrue('Empty range is collapsed', range.isCollapsed());
range.collapse();
assertTrue('Empty range is still collapsed', range.isCollapsed());
}
// TODO(robbyw): Test iteration over a strange document fragment.
function testIterator() {
goog.testing.dom.assertNodesMatch(
goog.dom.Range.createFromNodeContents(goog.dom.getElement('test2')),
['abc', '#br', '#br', 'def']);
}
function testReversedNodes() {
var node = goog.dom.getElement('test1').firstChild;
var range = goog.dom.Range.createFromNodes(node, 4, node, 0);
assertTrue('Range is reversed', range.isReversed());
node = goog.dom.getElement('test3');
range = goog.dom.Range.createFromNodes(node, 0, node, 1);
assertFalse('Range is not reversed', range.isReversed());
}
function testReversedContents() {
var range =
goog.dom.Range.createFromNodeContents(goog.dom.getElement('test1'), true);
assertTrue('Range is reversed', range.isReversed());
assertEquals('Range should select "Text"', 'Text', range.getText());
assertEquals('Range start offset should be 0', 0, range.getStartOffset());
assertEquals('Range end offset should be 4', 4, range.getEndOffset());
assertEquals('Range anchor offset should be 4', 4, range.getAnchorOffset());
assertEquals('Range focus offset should be 0', 0, range.getFocusOffset());
var range2 = range.clone();
range.collapse(true);
assertTrue('Range is collapsed', range.isCollapsed());
assertFalse('Collapsed range is not reversed', range.isReversed());
assertEquals(
'Post collapse start offset should be 4', 4, range.getStartOffset());
range2.collapse(false);
assertTrue('Range 2 is collapsed', range2.isCollapsed());
assertFalse('Collapsed range 2 is not reversed', range2.isReversed());
assertEquals(
'Post collapse start offset 2 should be 0', 0, range2.getStartOffset());
}
function testRemoveContents() {
var outer = goog.dom.getElement('removeTest');
var range = goog.dom.Range.createFromNodeContents(outer.firstChild);
range.removeContents();
assertEquals('Removed range content should be ""', '', range.getText());
assertTrue('Removed range should be collapsed', range.isCollapsed());
assertEquals('Outer div should have 1 child now', 1, outer.childNodes.length);
assertEquals(
'Inner div should be empty', 0, outer.firstChild.childNodes.length);
}
function testRemovePartialContents() {
var outer = goog.dom.getElement('removePartialTest');
var originalText = goog.dom.getTextContent(outer);
try {
var range = goog.dom.Range.createFromNodes(
outer.firstChild, 2, outer.firstChild, 4);
removeHelper(1, range, outer, 1, '0145');
range = goog.dom.Range.createFromNodes(
outer.firstChild, 0, outer.firstChild, 1);
removeHelper(2, range, outer, 1, '145');
range = goog.dom.Range.createFromNodes(
outer.firstChild, 2, outer.firstChild, 3);
removeHelper(3, range, outer, 1, '14');
var br = goog.dom.createDom(goog.dom.TagName.BR);
outer.appendChild(br);
range = goog.dom.Range.createFromNodes(outer.firstChild, 1, outer, 1);
removeHelper(4, range, outer, 2, '1
');
outer.innerHTML = '
123';
range = goog.dom.Range.createFromNodes(outer, 0, outer.lastChild, 2);
removeHelper(5, range, outer, 1, '3');
outer.innerHTML = '123
456';
range =
goog.dom.Range.createFromNodes(outer.firstChild, 1, outer.lastChild, 2);
removeHelper(6, range, outer, 2, '16');
outer.innerHTML = '123
456';
range =
goog.dom.Range.createFromNodes(outer.firstChild, 0, outer.lastChild, 2);
removeHelper(7, range, outer, 1, '6');
outer.innerHTML = '';
range = goog.dom.Range.createFromNodeContents(outer.firstChild);
removeHelper(8, range, outer, 1, '');
} finally {
// Restore the original text state for repeated runs.
goog.dom.setTextContent(outer, originalText);
}
// TODO(robbyw): Fix the following edge cases:
// * Selecting contents of a node containing multiply empty divs
// * Selecting via createFromNodes(x, 0, x, x.childNodes.length)
// * Consistent handling of nodeContents().remove
}
function removeHelper(
testNumber, range, outer, expectedChildCount, expectedContent) {
range.removeContents();
assertTrue(
testNumber + ': Removed range should now be collapsed',
range.isCollapsed());
assertEquals(
testNumber + ': Removed range content should be ""', '', range.getText());
assertEquals(
testNumber + ': Outer div should contain correct text', expectedContent,
outer.innerHTML.toLowerCase());
assertEquals(
testNumber + ': Outer div should have ' + expectedChildCount +
' children now',
expectedChildCount, outer.childNodes.length);
assertNotNull(
testNumber + ': Empty node should still exist',
goog.dom.getElement('empty'));
}
function testSurroundContents() {
var outer = goog.dom.getElement('surroundTest');
outer.innerHTML = '---Text that
will be surrounded---';
var range = goog.dom.Range.createFromNodes(
outer.firstChild, 3, outer.lastChild,
outer.lastChild.nodeValue.length - 3);
var div = goog.dom.createDom(goog.dom.TagName.DIV, {'style': 'color: red'});
var output = range.surroundContents(div);
assertEquals(
'Outer element should contain new element', outer, output.parentNode);
assertFalse('New element should have no id', !!output.id);
assertEquals('New element should be red', 'red', output.style.color);
assertEquals(
'Outer element should have three children', 3, outer.childNodes.length);
assertEquals(
'New element should have three children', 3, output.childNodes.length);
// TODO(robbyw): Ensure the range stays in a reasonable state.
}
/**
* Given two offsets into the 'foobar' node, make sure that inserting
* nodes at those offsets doesn't change a selection of 'oba'.
* @bug 1480638
*/
function assertSurroundDoesntChangeSelectionWithOffsets(
offset1, offset2, expectedHtml) {
var div = goog.dom.getElement('bug1480638');
goog.dom.setTextContent(div, 'foobar');
var rangeToSelect =
goog.dom.Range.createFromNodes(div.firstChild, 2, div.firstChild, 5);
rangeToSelect.select();
var rangeToSurround = goog.dom.Range.createFromNodes(
div.firstChild, offset1, div.firstChild, offset2);
rangeToSurround.surroundWithNodes(
goog.dom.createDom(goog.dom.TagName.SPAN),
goog.dom.createDom(goog.dom.TagName.SPAN));
// Make sure that the selection didn't change.
assertHTMLEquals(
'Selection must not change when contents are surrounded.', expectedHtml,
goog.dom.Range.createFromWindow().getHtmlFragment());
}
function testSurroundWithNodesDoesntChangeSelection1() {
assertSurroundDoesntChangeSelectionWithOffsets(
3, 4, 'oba');
}
function testSurroundWithNodesDoesntChangeSelection2() {
assertSurroundDoesntChangeSelectionWithOffsets(3, 6, 'oba');
}
function testSurroundWithNodesDoesntChangeSelection3() {
assertSurroundDoesntChangeSelectionWithOffsets(1, 3, 'oba');
}
function testSurroundWithNodesDoesntChangeSelection4() {
assertSurroundDoesntChangeSelectionWithOffsets(1, 6, 'oba');
}
function testInsertNode() {
var outer = goog.dom.getElement('insertTest');
goog.dom.setTextContent(outer, 'ACD');
var range =
goog.dom.Range.createFromNodes(outer.firstChild, 1, outer.firstChild, 2);
range.insertNode(goog.dom.createTextNode('B'), true);
assertEquals(
'Element should have correct innerHTML', 'ABCD', outer.innerHTML);
goog.dom.setTextContent(outer, '12');
range =
goog.dom.Range.createFromNodes(outer.firstChild, 0, outer.firstChild, 1);
var br = range.insertNode(goog.dom.createDom(goog.dom.TagName.BR), false);
assertEquals(
'New element should have correct innerHTML', '1
2',
outer.innerHTML.toLowerCase());
assertEquals('BR should be in outer', outer, br.parentNode);
}
function testReplaceContentsWithNode() {
var outer = goog.dom.getElement('insertTest');
goog.dom.setTextContent(outer, 'AXC');
var range =
goog.dom.Range.createFromNodes(outer.firstChild, 1, outer.firstChild, 2);
range.replaceContentsWithNode(goog.dom.createTextNode('B'));
assertEquals('Element should have correct innerHTML', 'ABC', outer.innerHTML);
goog.dom.setTextContent(outer, 'ABC');
range =
goog.dom.Range.createFromNodes(outer.firstChild, 3, outer.firstChild, 3);
range.replaceContentsWithNode(goog.dom.createTextNode('D'));
assertEquals(
'Element should have correct innerHTML after collapsed replace', 'ABCD',
outer.innerHTML);
outer.innerHTML = 'AXXXC';
range =
goog.dom.Range.createFromNodes(outer.firstChild, 1, outer.lastChild, 1);
range.replaceContentsWithNode(goog.dom.createTextNode('B'));
goog.testing.dom.assertHtmlContentsMatch('ABC', outer);
}
function testSurroundWithNodes() {
var outer = goog.dom.getElement('insertTest');
goog.dom.setTextContent(outer, 'ACE');
var range =
goog.dom.Range.createFromNodes(outer.firstChild, 1, outer.firstChild, 2);
range.surroundWithNodes(
goog.dom.createTextNode('B'), goog.dom.createTextNode('D'));
assertEquals(
'New element should have correct innerHTML', 'ABCDE', outer.innerHTML);
}
function testIsRangeInDocument() {
var outer = goog.dom.getElement('insertTest');
outer.innerHTML = '
ABC';
var range = goog.dom.Range.createCaret(outer.lastChild, 1);
assertEquals(
'Should get correct start element', 'ABC',
range.getStartNode().nodeValue);
assertTrue('Should be considered in document', range.isRangeInDocument());
goog.dom.setTextContent(outer, 'DEF');
assertFalse('Should be marked as out of document', range.isRangeInDocument());
}
function testRemovedNode() {
var node = goog.dom.getElement('removeNodeTest');
var range = goog.dom.browserrange.createRangeFromNodeContents(node);
range.select();
goog.dom.removeNode(node);
var newRange = goog.dom.Range.createFromWindow(window);
// In Chrome 14 and below (<= Webkit 535.1), newRange will be null.
// In Chrome 16 and above (>= Webkit 535.7), newRange will be collapsed
// like on other browsers.
// We didn't bother testing in between.
if (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('535.7')) {
assertNull('Webkit supports rangeCount == 0', newRange);
} else {
assertTrue(
'The other browsers will just have an empty range.',
newRange.isCollapsed());
}
}
function testReversedRange() {
if (goog.userAgent.EDGE_OR_IE) return; // IE doesn't make this distinction.
goog.dom.Range
.createFromNodes(
goog.dom.getElement('test2'), 0, goog.dom.getElement('test1'), 0)
.select();
var range = goog.dom.Range.createFromWindow(window);
assertTrue('Range should be reversed', range.isReversed());
}
function testUnreversedRange() {
goog.dom.Range
.createFromNodes(
goog.dom.getElement('test1'), 0, goog.dom.getElement('test2'), 0)
.select();
var range = goog.dom.Range.createFromWindow(window);
assertFalse('Range should not be reversed', range.isReversed());
}
function testReversedThenUnreversedRange() {
// This tests a workaround for a webkit bug where webkit caches selections
// incorrectly.
goog.dom.Range
.createFromNodes(
goog.dom.getElement('test2'), 0, goog.dom.getElement('test1'), 0)
.select();
goog.dom.Range
.createFromNodes(
goog.dom.getElement('test1'), 0, goog.dom.getElement('test2'), 0)
.select();
var range = goog.dom.Range.createFromWindow(window);
assertFalse('Range should not be reversed', range.isReversed());
}
function testHasAndClearSelection() {
goog.dom.Range.createFromNodeContents(goog.dom.getElement('test1')).select();
assertTrue('Selection should exist', goog.dom.Range.hasSelection());
goog.dom.Range.clearSelection();
assertFalse('Selection should not exist', goog.dom.Range.hasSelection());
}
function assertForward(string, startNode, startOffset, endNode, endOffset) {
var root = goog.dom.getElement('test2');
var originalInnerHtml = root.innerHTML;
assertFalse(
string,
goog.dom.Range.isReversed(startNode, startOffset, endNode, endOffset));
assertTrue(
string,
goog.dom.Range.isReversed(endNode, endOffset, startNode, startOffset));
assertEquals(
'Contents should be unaffected after: ' + string, root.innerHTML,
originalInnerHtml);
}
function testIsReversed() {
var root = goog.dom.getElement('test2');
var text1 = root.firstChild; // Text content: 'abc'.
var br = root.childNodes[1];
var text2 = root.lastChild; // Text content: 'def'.
assertFalse(
'Same element position gives false',
goog.dom.Range.isReversed(root, 0, root, 0));
assertFalse(
'Same text position gives false',
goog.dom.Range.isReversed(text1, 0, text2, 0));
assertForward(
'Element offsets should compare against each other', root, 0, root, 2);
assertForward(
'Text node offsets should compare against each other', text1, 0, text2,
2);
assertForward('Text nodes should compare correctly', text1, 0, text2, 0);
assertForward('Text nodes should compare to later elements', text1, 0, br, 0);
assertForward(
'Text nodes should compare to earlier elements', br, 0, text2, 0);
assertForward('Parent is before element child', root, 0, br, 0);
assertForward('Parent is before text child', root, 0, text1, 0);
assertFalse(
'Equivalent position gives false',
goog.dom.Range.isReversed(root, 0, text1, 0));
assertFalse(
'Equivalent position gives false',
goog.dom.Range.isReversed(root, 1, br, 0));
assertForward('End of element is after children', text1, 0, root, 3);
assertForward('End of element is after children', br, 0, root, 3);
assertForward('End of element is after children', text2, 0, root, 3);
assertForward('End of element is after end of last child', text2, 3, root, 3);
}
function testSelectAroundSpaces() {
// set the selection
var textNode = goog.dom.getElement('textWithSpaces').firstChild;
goog.dom.TextRange.createFromNodes(textNode, 5, textNode, 12).select();
// get the selection and check that it matches what we set it to
var range = goog.dom.Range.createFromWindow();
assertEquals(' world ', range.getText());
assertEquals(5, range.getStartOffset());
assertEquals(12, range.getEndOffset());
assertEquals(textNode, range.getContainer());
// Check the contents again, because there used to be a bug where
// it changed after calling getContainer().
assertEquals(' world ', range.getText());
}
function testSelectInsideSpaces() {
// set the selection
var textNode = goog.dom.getElement('textWithSpaces').firstChild;
goog.dom.TextRange.createFromNodes(textNode, 6, textNode, 11).select();
// get the selection and check that it matches what we set it to
var range = goog.dom.Range.createFromWindow();
assertEquals('world', range.getText());
assertEquals(6, range.getStartOffset());
assertEquals(11, range.getEndOffset());
assertEquals(textNode, range.getContainer());
// Check the contents again, because there used to be a bug where
// it changed after calling getContainer().
assertEquals('world', range.getText());
}
function testRangeBeforeBreak() {
var container = goog.dom.getElement('rangeAroundBreaks');
var text = container.firstChild;
var offset = text.length;
assertEquals(4, offset);
var br = container.childNodes[1];
var caret = goog.dom.Range.createCaret(text, offset);
caret.select();
assertEquals(offset, caret.getStartOffset());
var range = goog.dom.Range.createFromWindow();
assertFalse('Should not contain whole
', range.containsNode(br, false));
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
assertTrue(
'Range over
is adjacent to the immediate range before it',
range.containsNode(br, true));
} else {
assertFalse(
'Should not contain partial
', range.containsNode(br, true));
}
assertEquals(offset, range.getStartOffset());
assertEquals(text, range.getStartNode());
}
function testRangeAfterBreak() {
var container = goog.dom.getElement('rangeAroundBreaks');
var br = container.childNodes[1];
var caret = goog.dom.Range.createCaret(container.lastChild, 0);
caret.select();
assertEquals(0, caret.getStartOffset());
var range = goog.dom.Range.createFromWindow();
assertFalse('Should not contain whole
', range.containsNode(br, false));
var isSafari3 =
goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('528');
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) ||
isSafari3) {
assertTrue(
'Range over
is adjacent to the immediate range after it',
range.containsNode(br, true));
} else {
assertFalse(
'Should not contain partial
', range.containsNode(br, true));
}
if (isSafari3) {
assertEquals(2, range.getStartOffset());
assertEquals(container, range.getStartNode());
} else {
assertEquals(0, range.getStartOffset());
assertEquals(container.lastChild, range.getStartNode());
}
}
function testRangeAtBreakAtStart() {
var container = goog.dom.getElement('breaksAroundNode');
var br = container.firstChild;
var caret = goog.dom.Range.createCaret(container.firstChild, 0);
caret.select();
assertEquals(0, caret.getStartOffset());
var range = goog.dom.Range.createFromWindow();
assertTrue(
'Range over
is adjacent to the immediate range before it',
range.containsNode(br, true));
assertFalse('Should not contain whole
', range.containsNode(br, false));
assertRangeEquals(container, 0, container, 0, range);
}
function testFocusedElementDisappears() {
// This reproduces a failure case specific to Gecko, where an element is
// created, contentEditable is set, is focused, and removed. After that
// happens, calling selection.collapse fails.
// https://bugzilla.mozilla.org/show_bug.cgi?id=773137
var disappearingElement = goog.dom.createDom(goog.dom.TagName.DIV);
document.body.appendChild(disappearingElement);
disappearingElement.contentEditable = true;
disappearingElement.focus();
document.body.removeChild(disappearingElement);
var container = goog.dom.getElement('empty');
var caret = goog.dom.Range.createCaret(container, 0);
// This should not throw.
caret.select();
assertEquals(0, caret.getStartOffset());
}
function assertNodeEquals(expected, actual) {
assertEquals(
'Expected: ' + goog.testing.dom.exposeNode(expected) + '\nActual: ' +
goog.testing.dom.exposeNode(actual),
expected, actual);
}