Files
browser/src/browser/tests/range.html
2026-02-13 16:28:53 +08:00

957 lines
28 KiB
HTML

<!DOCTYPE html>
<script src="testing.js"></script>
<body>
<div id="test-content">
<p id="p1">First paragraph</p>
<p id="p2">Second paragraph</p>
<span id="s1">Span content</span>
</div>
<script id=basic>
{
const range = document.createRange();
testing.expectEqual('object', typeof range);
testing.expectEqual(true, range instanceof Range);
testing.expectEqual(true, range instanceof AbstractRange);
}
</script>
<script id=initial_state>
{
const range = document.createRange();
// New range should be collapsed at document position
testing.expectEqual(document, range.startContainer);
testing.expectEqual(0, range.startOffset);
testing.expectEqual(document, range.endContainer);
testing.expectEqual(0, range.endOffset);
testing.expectEqual(true, range.collapsed);
}
</script>
<script id=setStart_setEnd>
{
const range = document.createRange();
const p1 = $('#p1');
const p2 = $('#p2');
range.setStart(p1, 0);
testing.expectEqual(p1, range.startContainer);
testing.expectEqual(0, range.startOffset);
// After setStart, if new start is after original end, range collapses
// Since original end was at (document, 0) and p1 is inside document,
// the range auto-collapses to the new start
testing.expectEqual(p1, range.endContainer);
testing.expectEqual(0, range.endOffset);
testing.expectEqual(true, range.collapsed);
range.setEnd(p2, 0);
testing.expectEqual(p2, range.endContainer);
testing.expectEqual(0, range.endOffset);
testing.expectEqual(false, range.collapsed);
}
</script>
<script id=collapse>
{
const range = document.createRange();
const p1 = $('#p1');
const p2 = $('#p2');
range.setStart(p1, 0);
range.setEnd(p2, 1);
testing.expectEqual(false, range.collapsed);
// Collapse to start
range.collapse(true);
testing.expectEqual(p1, range.startContainer);
testing.expectEqual(p1, range.endContainer);
testing.expectEqual(0, range.startOffset);
testing.expectEqual(0, range.endOffset);
testing.expectEqual(true, range.collapsed);
// Reset and collapse to end
range.setStart(p1, 0);
range.setEnd(p2, 1);
range.collapse(false);
testing.expectEqual(p2, range.startContainer);
testing.expectEqual(p2, range.endContainer);
testing.expectEqual(1, range.startOffset);
testing.expectEqual(1, range.endOffset);
testing.expectEqual(true, range.collapsed);
}
</script>
<script id=selectNode>
{
const range = document.createRange();
const p1 = $('#p1');
const parent = p1.parentNode;
range.selectNode(p1);
testing.expectEqual(parent, range.startContainer);
testing.expectEqual(parent, range.endContainer);
testing.expectEqual(false, range.collapsed);
// Start should be before p1, end should be after p1
const startOffset = range.startOffset;
const endOffset = range.endOffset;
testing.expectEqual(startOffset + 1, endOffset);
testing.expectEqual(p1, parent.childNodes[startOffset]);
}
</script>
<script id=selectNodeContents>
{
const range = document.createRange();
const p1 = $('#p1');
range.selectNodeContents(p1);
testing.expectEqual(p1, range.startContainer);
testing.expectEqual(p1, range.endContainer);
testing.expectEqual(0, range.startOffset);
testing.expectEqual(p1.childNodes.length, range.endOffset);
testing.expectEqual(false, range.collapsed);
}
</script>
<script id=setStartBefore_setStartAfter>
{
const range = document.createRange();
const p1 = $('#p1');
const p2 = $('#p2');
const parent = p1.parentNode;
range.setStartBefore(p1);
testing.expectEqual(parent, range.startContainer);
const beforeOffset = range.startOffset;
testing.expectEqual(p1, parent.childNodes[beforeOffset]);
range.setStartAfter(p1);
testing.expectEqual(parent, range.startContainer);
const afterOffset = range.startOffset;
testing.expectEqual(afterOffset, beforeOffset + 1);
}
</script>
<script id=setEndBefore_setEndAfter>
{
const range = document.createRange();
const p2 = $('#p2');
const parent = p2.parentNode;
range.setEndBefore(p2);
testing.expectEqual(parent, range.endContainer);
const beforeOffset = range.endOffset;
testing.expectEqual(p2, parent.childNodes[beforeOffset]);
range.setEndAfter(p2);
testing.expectEqual(parent, range.endContainer);
const afterOffset = range.endOffset;
testing.expectEqual(afterOffset, beforeOffset + 1);
}
</script>
<script id=cloneRange>
{
const range = document.createRange();
const p1 = $('#p1');
const p2 = $('#p2');
range.setStart(p1, 0);
range.setEnd(p2, 1);
const clone = range.cloneRange();
testing.expectTrue(clone !== range);
testing.expectEqual(range.startContainer, clone.startContainer);
testing.expectEqual(range.startOffset, clone.startOffset);
testing.expectEqual(range.endContainer, clone.endContainer);
testing.expectEqual(range.endOffset, clone.endOffset);
testing.expectEqual(range.collapsed, clone.collapsed);
// Modifying clone shouldn't affect original
clone.collapse(true);
testing.expectEqual(true, clone.collapsed);
testing.expectEqual(false, range.collapsed);
}
</script>
<script id=toString_collapsed>
{
const range = document.createRange();
const p1 = $('#p1');
range.setStart(p1, 0);
range.setEnd(p1, 0);
testing.expectEqual('', range.toString());
}
</script>
<script id=insertNode>
{
const range = document.createRange();
const p1 = $('#p1');
const newSpan = document.createElement('span');
newSpan.textContent = 'INSERTED';
// Select p1's contents
range.selectNodeContents(p1);
// Collapse to start
range.collapse(true);
// Insert node
range.insertNode(newSpan);
// Check that span is first child of p1
testing.expectEqual(newSpan, p1.firstChild);
testing.expectEqual('INSERTED', newSpan.textContent);
}
</script>
<script id=insertNode_splitText>
{
const div = document.createElement('div');
div.textContent = 'Hello World';
const range = document.createRange();
const textNode = div.firstChild;
// Set range to middle of text (after "Hello ")
range.setStart(textNode, 6);
range.collapse(true);
// Insert a span in the middle
const span = document.createElement('span');
span.textContent = 'MIDDLE';
range.insertNode(span);
// Should have 3 children: "Hello ", span, "World"
testing.expectEqual(3, div.childNodes.length);
testing.expectEqual('Hello ', div.childNodes[0].textContent);
testing.expectEqual(span, div.childNodes[1]);
testing.expectEqual('MIDDLE', div.childNodes[1].textContent);
testing.expectEqual('World', div.childNodes[2].textContent);
// Full text should be correct
testing.expectEqual('Hello MIDDLEWorld', div.textContent);
}
</script>
<script id=deleteContents>
{
const div = document.createElement('div');
div.innerHTML = '<p>Hello</p><span>World</span>';
const range = document.createRange();
range.selectNodeContents(div);
testing.expectEqual(2, div.childNodes.length);
range.deleteContents();
testing.expectEqual(0, div.childNodes.length);
testing.expectEqual(true, range.collapsed);
}
</script>
<script id=cloneContents>
{
const div = document.createElement('div');
div.innerHTML = '<p>First</p><span>Second</span>';
const range = document.createRange();
range.selectNodeContents(div);
const fragment = range.cloneContents();
// Original should be unchanged
testing.expectEqual(2, div.childNodes.length);
// Fragment should have copies
testing.expectEqual(2, fragment.childNodes.length);
testing.expectEqual('P', fragment.childNodes[0].tagName);
testing.expectEqual('First', fragment.childNodes[0].textContent);
testing.expectEqual('SPAN', fragment.childNodes[1].tagName);
testing.expectEqual('Second', fragment.childNodes[1].textContent);
}
</script>
<script id=extractContents>
{
const div = document.createElement('div');
div.innerHTML = '<p>First</p><span>Second</span>';
const range = document.createRange();
range.selectNodeContents(div);
const fragment = range.extractContents();
// Original should be empty
testing.expectEqual(0, div.childNodes.length);
// Fragment should have extracted content
testing.expectEqual(2, fragment.childNodes.length);
testing.expectEqual('P', fragment.childNodes[0].tagName);
testing.expectEqual('SPAN', fragment.childNodes[1].tagName);
}
</script>
<script id=surroundContents>
{
const div = document.createElement('div');
div.innerHTML = '<p>Content</p>';
const range = document.createRange();
range.selectNodeContents(div);
const wrapper = document.createElement('section');
wrapper.className = 'wrapper';
range.surroundContents(wrapper);
// Div should now contain only the wrapper
testing.expectEqual(1, div.childNodes.length);
testing.expectEqual(wrapper, div.firstChild);
testing.expectEqual('wrapper', wrapper.className);
// Wrapper should contain original content
testing.expectEqual(1, wrapper.childNodes.length);
testing.expectEqual('P', wrapper.firstChild.tagName);
testing.expectEqual('Content', wrapper.firstChild.textContent);
}
</script>
<script id=createContextualFragment_basic>
{
const div = document.createElement('div');
const range = document.createRange();
range.selectNodeContents(div);
const fragment = range.createContextualFragment('<p>Hello</p><span>World</span>');
// Fragment should contain the parsed elements
testing.expectEqual(2, fragment.childNodes.length);
testing.expectEqual('P', fragment.childNodes[0].tagName);
testing.expectEqual('Hello', fragment.childNodes[0].textContent);
testing.expectEqual('SPAN', fragment.childNodes[1].tagName);
testing.expectEqual('World', fragment.childNodes[1].textContent);
// Original div should be unchanged
testing.expectEqual(0, div.childNodes.length);
}
</script>
<script id=createContextualFragment_empty>
{
const div = document.createElement('div');
const range = document.createRange();
range.selectNodeContents(div);
const fragment = range.createContextualFragment('');
testing.expectEqual(0, fragment.childNodes.length);
}
</script>
<script id=createContextualFragment_textContext>
{
const div = document.createElement('div');
div.textContent = 'Some text';
const range = document.createRange();
const textNode = div.firstChild;
range.setStart(textNode, 5);
range.collapse(true);
// Even though range is in text node, should use parent (div) as context
const fragment = range.createContextualFragment('<b>Bold</b>');
testing.expectEqual(1, fragment.childNodes.length);
testing.expectEqual('B', fragment.childNodes[0].tagName);
testing.expectEqual('Bold', fragment.childNodes[0].textContent);
}
</script>
<script id=offset_validation_setStart>
{
const range = document.createRange();
const p1 = $('#p1');
// Test setStart with offset beyond node length
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.setStart(p1, 999);
});
// Test with negative offset (wraps to large u32)
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.setStart(p1.firstChild, -1);
});
}
</script>
<script id=offset_validation_setEnd>
{
const range = document.createRange();
const p1 = $('#p1');
// Test setEnd with offset beyond node length
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.setEnd(p1, 999);
});
// Test with text node
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.setEnd(p1.firstChild, 9999);
});
}
</script>
<script id=comparePoint_basic>
{
// Create fresh elements to avoid DOM pollution from other tests
const div = document.createElement('div');
const p1 = document.createElement('p');
const p2 = document.createElement('p');
p1.textContent = 'First paragraph text';
p2.textContent = 'Second paragraph text';
div.appendChild(p1);
div.appendChild(p2);
const range = document.createRange();
range.setStart(p1.firstChild, 0);
range.setEnd(p2.firstChild, 5);
// Point before range
testing.expectEqual(-1, range.comparePoint(div, 0));
// Point at start boundary
testing.expectEqual(0, range.comparePoint(p1.firstChild, 0));
// Point inside range (in p1)
testing.expectEqual(0, range.comparePoint(p1.firstChild, 3));
// Point inside range (in p2)
testing.expectEqual(0, range.comparePoint(p2.firstChild, 2));
// Point at end boundary
testing.expectEqual(0, range.comparePoint(p2.firstChild, 5));
// Point after range
testing.expectEqual(1, range.comparePoint(p2.firstChild, 10));
}
</script>
<script id=comparePoint_validation>
{
// Create fresh element
const p1 = document.createElement('p');
p1.textContent = 'Test content';
const range = document.createRange();
range.setStart(p1, 0);
range.setEnd(p1, 1);
// Test comparePoint with invalid offset
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.comparePoint(p1, 20);
});
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.comparePoint(p1.firstChild, -1);
});
}
</script>
<script id=different_document_collapse>
{
// Create fresh element in current document
const p1 = document.createElement('p');
p1.textContent = 'Local content';
const range = document.createRange();
// Create a foreign document
const foreignDoc = document.implementation.createHTMLDocument('');
const foreignP = foreignDoc.createElement('p');
foreignP.textContent = 'Foreign';
foreignDoc.body.appendChild(foreignP);
// Set up range in current document
range.setStart(p1, 0);
range.setEnd(p1, 1);
testing.expectEqual(false, range.collapsed);
// Setting start to foreign document should collapse to that point
range.setStart(foreignP, 0);
testing.expectEqual(true, range.collapsed);
testing.expectEqual(foreignP, range.startContainer);
testing.expectEqual(foreignP, range.endContainer);
}
</script>
<script id=detached_node_collapse>
{
// Create fresh element
const p1 = document.createElement('p');
p1.textContent = 'Attached content';
const range = document.createRange();
// Create a detached element
const detached = document.createElement('div');
detached.textContent = 'Detached';
// Set up range in document
range.setStart(p1, 0);
range.setEnd(p1, 1);
testing.expectEqual(false, range.collapsed);
// Setting end to detached node should collapse
range.setEnd(detached.firstChild, 0);
testing.expectEqual(true, range.collapsed);
testing.expectEqual(detached.firstChild, range.startContainer);
testing.expectEqual(detached.firstChild, range.endContainer);
}
</script>
<script id=isPointInRange_basic>
{
// Create fresh elements
const div = document.createElement('div');
const p1 = document.createElement('p');
const p2 = document.createElement('p');
p1.textContent = 'First paragraph';
p2.textContent = 'Second paragraph';
div.appendChild(p1);
div.appendChild(p2);
const range = document.createRange();
range.setStart(p1.firstChild, 5);
range.setEnd(p2.firstChild, 6);
// Point before range
testing.expectEqual(false, range.isPointInRange(p1.firstChild, 0));
// Point at start boundary
testing.expectEqual(true, range.isPointInRange(p1.firstChild, 5));
// Point inside range
testing.expectEqual(true, range.isPointInRange(p1.firstChild, 7));
testing.expectEqual(true, range.isPointInRange(p2.firstChild, 3));
// Point at end boundary
testing.expectEqual(true, range.isPointInRange(p2.firstChild, 6));
// Point after range
testing.expectEqual(false, range.isPointInRange(p2.firstChild, 10));
}
</script>
<script id=isPointInRange_different_root>
{
// Create element in current document
const p1 = document.createElement('p');
p1.textContent = 'Local content';
const range = document.createRange();
range.setStart(p1, 0);
range.setEnd(p1, 1);
// Create element in different document
const foreignDoc = document.implementation.createHTMLDocument('');
const foreignP = foreignDoc.createElement('p');
foreignP.textContent = 'Foreign';
// Point in different root should return false (not throw)
testing.expectEqual(false, range.isPointInRange(foreignP, 0));
}
</script>
<script id=isPointInRange_validation>
{
const p1 = document.createElement('p');
p1.textContent = 'Test content';
const range = document.createRange();
range.setStart(p1, 0);
range.setEnd(p1, 1);
// Invalid offset should throw IndexSizeError
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.isPointInRange(p1, 999);
});
testing.expectError('IndexSizeError: Index or size is negative or greater than the allowed amount', () => {
range.isPointInRange(p1.firstChild, 9999);
});
}
</script>
<script id=intersectsNode_basic>
{
// Create fresh elements
const div = document.createElement('div');
const p1 = document.createElement('p');
const p2 = document.createElement('p');
const p3 = document.createElement('p');
p1.textContent = 'First';
p2.textContent = 'Second';
p3.textContent = 'Third';
div.appendChild(p1);
div.appendChild(p2);
div.appendChild(p3);
const range = document.createRange();
range.setStart(p1.firstChild, 2);
range.setEnd(p2.firstChild, 3);
// Node that intersects (p1 contains the start)
testing.expectEqual(true, range.intersectsNode(p1));
// Node that intersects (p2 contains the end)
testing.expectEqual(true, range.intersectsNode(p2));
// Node that doesn't intersect (p3 is after the range)
testing.expectEqual(false, range.intersectsNode(p3));
// Container intersects
testing.expectEqual(true, range.intersectsNode(div));
}
</script>
<script id=intersectsNode_detached>
{
const div = document.createElement('div');
const p1 = document.createElement('p');
p1.textContent = 'Content';
div.appendChild(p1);
const range = document.createRange();
range.setStart(p1, 0);
range.setEnd(p1, 1);
// The root node (div) should return true when it has no parent
// (Note: div is detached, so it's in the same tree as the range)
testing.expectEqual(true, range.intersectsNode(div));
}
</script>
<script id=intersectsNode_different_root>
{
const p1 = document.createElement('p');
p1.textContent = 'Local';
const range = document.createRange();
range.setStart(p1, 0);
range.setEnd(p1, 1);
// Node in different document should return false
const foreignDoc = document.implementation.createHTMLDocument('');
const foreignP = foreignDoc.createElement('p');
testing.expectEqual(false, range.intersectsNode(foreignP));
}
</script>
<script id=commonAncestorContainer_same_node>
{
const p = document.createElement('p');
p.textContent = 'Content';
const range = document.createRange();
range.setStart(p.firstChild, 0);
range.setEnd(p.firstChild, 3);
// Both boundaries in same text node, so that's the common ancestor
testing.expectEqual(p.firstChild, range.commonAncestorContainer);
}
</script>
<script id=commonAncestorContainer_siblings>
{
const div = document.createElement('div');
const p1 = document.createElement('p');
const p2 = document.createElement('p');
p1.textContent = 'First';
p2.textContent = 'Second';
div.appendChild(p1);
div.appendChild(p2);
const range = document.createRange();
range.setStart(p1.firstChild, 0);
range.setEnd(p2.firstChild, 3);
// Start and end in different siblings, common ancestor is the parent div
testing.expectEqual(div, range.commonAncestorContainer);
}
</script>
<script id=commonAncestorContainer_nested>
{
const div = document.createElement('div');
const section = document.createElement('section');
const p = document.createElement('p');
const span = document.createElement('span');
p.textContent = 'Text';
span.textContent = 'Span';
div.appendChild(section);
section.appendChild(p);
div.appendChild(span);
const range = document.createRange();
range.setStart(p.firstChild, 0);
range.setEnd(span.firstChild, 2);
// Common ancestor of deeply nested p and sibling span is div
testing.expectEqual(div, range.commonAncestorContainer);
}
</script>
<script id=compareBoundaryPoints_constants>
{
// Test that the constants are defined
testing.expectEqual(0, Range.START_TO_START);
testing.expectEqual(1, Range.START_TO_END);
testing.expectEqual(2, Range.END_TO_END);
testing.expectEqual(3, Range.END_TO_START);
}
</script>
<script id=compareBoundaryPoints_basic>
{
const div = document.createElement('div');
const p1 = document.createElement('p');
const p2 = document.createElement('p');
p1.textContent = 'First paragraph';
p2.textContent = 'Second paragraph';
div.appendChild(p1);
div.appendChild(p2);
const range1 = document.createRange();
range1.setStart(p1.firstChild, 0);
range1.setEnd(p1.firstChild, 5);
const range2 = document.createRange();
range2.setStart(p1.firstChild, 3);
range2.setEnd(p2.firstChild, 5);
// range1 start is before range2 start
testing.expectEqual(-1, range1.compareBoundaryPoints(Range.START_TO_START, range2));
// range1 end is after range2 start
testing.expectEqual(1, range1.compareBoundaryPoints(Range.START_TO_END, range2));
// range1 start is before range2 end
testing.expectEqual(-1, range1.compareBoundaryPoints(Range.END_TO_START, range2));
// range1 end is before range2 end
testing.expectEqual(-1, range1.compareBoundaryPoints(Range.END_TO_END, range2));
}
</script>
<script id=compareBoundaryPoints_same_range>
{
const p = document.createElement('p');
p.textContent = 'Content';
const range = document.createRange();
range.setStart(p.firstChild, 2);
range.setEnd(p.firstChild, 5);
// Comparing a range to itself should return 0
testing.expectEqual(0, range.compareBoundaryPoints(Range.START_TO_START, range));
testing.expectEqual(0, range.compareBoundaryPoints(Range.END_TO_END, range));
// End is after start
testing.expectEqual(1, range.compareBoundaryPoints(Range.START_TO_END, range));
// Start is before end
testing.expectEqual(-1, range.compareBoundaryPoints(Range.END_TO_START, range));
}
</script>
<script id=compareBoundaryPoints_invalid_how>
{
const p = document.createElement('p');
p.textContent = 'Test';
const range1 = document.createRange();
const range2 = document.createRange();
range1.setStart(p, 0);
range2.setStart(p, 0);
// Invalid how parameter should throw NotSupportedError
testing.expectError('NotSupportedError: Not Supported', () => {
range1.compareBoundaryPoints(4, range2);
});
testing.expectError('NotSupportedError: Not Supported', () => {
range1.compareBoundaryPoints(99, range2);
});
}
</script>
<script id=compareBoundaryPoints_different_root>
{
const p1 = document.createElement('p');
p1.textContent = 'Local';
const range1 = document.createRange();
range1.setStart(p1, 0);
range1.setEnd(p1, 1);
// Create range in different document
const foreignDoc = document.implementation.createHTMLDocument('');
const foreignP = foreignDoc.createElement('p');
foreignP.textContent = 'Foreign';
const range2 = foreignDoc.createRange();
range2.setStart(foreignP, 0);
range2.setEnd(foreignP, 1);
// Comparing ranges in different documents should throw WrongDocumentError
testing.expectError('WrongDocumentError: wrong_document_error', () => {
range1.compareBoundaryPoints(Range.START_TO_START, range2);
});
}
</script>
<script id=deleteContents_crossNode>
{
// Test deleteContents across multiple sibling text nodes
const p = document.createElement('p');
p.appendChild(document.createTextNode('AAAA'));
p.appendChild(document.createTextNode('BBBB'));
p.appendChild(document.createTextNode('CCCC'));
testing.expectEqual(3, p.childNodes.length);
testing.expectEqual('AAAABBBBCCCC', p.textContent);
const range = document.createRange();
// Start at position 2 in first text node ("AA|AA")
range.setStart(p.childNodes[0], 2);
// End at position 2 in third text node ("CC|CC")
range.setEnd(p.childNodes[2], 2);
range.deleteContents();
// Should have truncated first node to "AA" and third node to "CC"
// Middle node should be removed
testing.expectEqual(2, p.childNodes.length);
testing.expectEqual('AA', p.childNodes[0].textContent);
testing.expectEqual('CC', p.childNodes[1].textContent);
testing.expectEqual('AACC', p.textContent);
}
</script>
<script id=deleteContents_crossNode_partial>
{
// Test deleteContents where start node is completely preserved
const p = document.createElement('p');
p.appendChild(document.createTextNode('KEEP'));
p.appendChild(document.createTextNode('DELETE'));
p.appendChild(document.createTextNode('PARTIAL'));
const range = document.createRange();
// Start at end of first text node
range.setStart(p.childNodes[0], 4);
// End in middle of third text node
range.setEnd(p.childNodes[2], 4);
range.deleteContents();
testing.expectEqual(2, p.childNodes.length);
testing.expectEqual('KEEP', p.childNodes[0].textContent);
testing.expectEqual('IAL', p.childNodes[1].textContent);
testing.expectEqual('KEEPIAL', p.textContent);
}
</script>
<script id=extractContents_crossNode>
{
// Test extractContents across multiple sibling text nodes
const p = document.createElement('p');
p.appendChild(document.createTextNode('AAAA'));
p.appendChild(document.createTextNode('BBBB'));
p.appendChild(document.createTextNode('CCCC'));
const range = document.createRange();
range.setStart(p.childNodes[0], 2);
range.setEnd(p.childNodes[2], 2);
const fragment = range.extractContents();
// Original should be truncated
testing.expectEqual(2, p.childNodes.length);
testing.expectEqual('AA', p.childNodes[0].textContent);
testing.expectEqual('CC', p.childNodes[1].textContent);
// Fragment should contain extracted content
testing.expectEqual(3, fragment.childNodes.length);
testing.expectEqual('AA', fragment.childNodes[0].textContent);
testing.expectEqual('BBBB', fragment.childNodes[1].textContent);
testing.expectEqual('CC', fragment.childNodes[2].textContent);
}
</script>
<script id=cloneContents_crossNode>
{
// Test cloneContents across multiple sibling text nodes
const p = document.createElement('p');
p.appendChild(document.createTextNode('AAAA'));
p.appendChild(document.createTextNode('BBBB'));
p.appendChild(document.createTextNode('CCCC'));
const range = document.createRange();
range.setStart(p.childNodes[0], 2);
range.setEnd(p.childNodes[2], 2);
const fragment = range.cloneContents();
// Original should be unchanged
testing.expectEqual(3, p.childNodes.length);
testing.expectEqual('AAAA', p.childNodes[0].textContent);
testing.expectEqual('BBBB', p.childNodes[1].textContent);
testing.expectEqual('CCCC', p.childNodes[2].textContent);
// Fragment should contain cloned content
testing.expectEqual(3, fragment.childNodes.length);
testing.expectEqual('AA', fragment.childNodes[0].textContent);
testing.expectEqual('BBBB', fragment.childNodes[1].textContent);
testing.expectEqual('CC', fragment.childNodes[2].textContent);
}
</script>
<script id=deleteContents_crossNode_withElements>
{
// Test deleteContents with mixed text and element nodes
const div = document.createElement('div');
div.appendChild(document.createTextNode('Start'));
const span = document.createElement('span');
span.textContent = 'Middle';
div.appendChild(span);
div.appendChild(document.createTextNode('End'));
testing.expectEqual(3, div.childNodes.length);
const range = document.createRange();
// Start in middle of first text node
range.setStart(div.childNodes[0], 2);
// End in middle of last text node
range.setEnd(div.childNodes[2], 1);
range.deleteContents();
// Should keep "St" from start, remove span, keep "nd" from end
testing.expectEqual(2, div.childNodes.length);
testing.expectEqual('St', div.childNodes[0].textContent);
testing.expectEqual('nd', div.childNodes[1].textContent);
testing.expectEqual('Stnd', div.textContent);
}
</script>