mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-03 09:48:38 +02:00
643 lines
27 KiB
HTML
643 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Text Fragment Chrome-only API Test</title>
|
|
<meta charset="UTF-8">
|
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script src="/tests/SimpleTest/GleanTest.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
</head>
|
|
<body>
|
|
<div id="abc">abc def ghi abc def ghi jkl abc. def <span>ghi</span></div>
|
|
<div id="foo">foo</div>
|
|
<p id="block">p<span id="inlinespan">sp<span id="nestedinlinespan">a</span>n</span>p</p><span id="afterblockboundary">afterblockboundary</span>
|
|
<div id="image"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAEUlEQVR42mNk+M+AARiHsiAAcCIKAYwFoQ8AAAAASUVORK5CYII="></div>
|
|
<p>placeholder with block boundary</p>
|
|
<p id="surroundedByBlockBoundaries">abc def ghi</p>
|
|
<p>placeholder with block boundary</p>
|
|
<ul>
|
|
<li>First element</li>
|
|
<li>Second element</li>
|
|
</ul>
|
|
<ul>
|
|
<li>First element, but different</li>
|
|
<li id="secondListElement">Second Element</li>
|
|
</ul>
|
|
<p id="textWithEmoji"><span>😍😍</span> abc def 😍😍</p>
|
|
<p id="textWithDifferentEmoji"><span>😍😏😍</span> abc def 😍😏</p>
|
|
<!-- TODO: Add test case with `visibility: hidden` as soon as it's supported (bug 1950707) -->
|
|
<p id="textWithInvisibleContent">Text with <span style="display: none">display:none </span>content</p>
|
|
<p id="wordBoundaryAtInlineBoundary">WordEnd <span>should not be part of text directive</span></p>
|
|
<p id="matchAtEndOfBlock">EndOfBlock</p>
|
|
<p id="prevMatchAtEndOfBlock">EndOfBlock</p>
|
|
<div id="emptyBlockAtBeginning"><div></div>AfterEmptyBlock</div>
|
|
<div id="emptyBlockAtEnd">BeforeEmptyBlock<div></div></div>
|
|
<div id="rangeBasedWithMultipleEndMatchesBegin">Begin and begin or begin with begin</div>
|
|
<div id="rangeBasedWithMultipleEndMatchesEnd">end or end and end at end</div>
|
|
<div>HereStartsATableAndThisIsHereToMakeSureTheTestResultsAreIndependentOfSurroundingContent</div>
|
|
<table>
|
|
<tr>
|
|
<td id="firstRow">first row</td></tr> <tr><td id="secondRow">second row</td>
|
|
</tr>
|
|
</table>
|
|
<div>HereEndsATableAndThisIsHereToMakeSureTheTestResultsAreIndependentOfSurroundingContent</div>
|
|
|
|
<span id="longWord">1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</span>
|
|
<p id="loremIpsum">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source.</p>
|
|
<p>
|
|
<span id="beforeEmptyTextNode">before empty text node</span>
|
|
<!-- empty text node will be inserted right here -->
|
|
<span id="afterEmptyTextNode">after empty text node</span>
|
|
</p>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const empty = document.createTextNode("");
|
|
const afterEmptyTextNode = document.getElementById("afterEmptyTextNode");
|
|
afterEmptyTextNode.parentNode.insertBefore(empty, afterEmptyTextNode);
|
|
});
|
|
|
|
SimpleTest.waitForExplicitFinish();
|
|
function rAF() {
|
|
return new Promise(resolve => {
|
|
requestAnimationFrame(resolve);
|
|
});
|
|
}
|
|
/**
|
|
* Helper function which is used by assertions below.
|
|
* Returns true if the two ranges have the exact same start and end points.
|
|
*/
|
|
function rangeBoundariesAreEqual(range1, range2) {
|
|
const startContainerIsEqual = range1.startContainer.textContent === range2.startContainer.textContent;
|
|
const endContainerIsEqual = range1.endContainer.textContent === range2.endContainer.textContent;
|
|
|
|
const startOffsetIsEqual = range1.startOffset == range2.startOffset;
|
|
const endOffsetIsEqual = range1.endOffset == range2.endOffset;
|
|
return (
|
|
startContainerIsEqual
|
|
&& endContainerIsEqual
|
|
&& startOffsetIsEqual
|
|
&& endOffsetIsEqual
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test for the `rangeBoundariesAreEqual()` helper function.
|
|
*/
|
|
function testRangeBoundariesAreEqual() {
|
|
const range1 = document.createRange();
|
|
range1.setStart(abc.firstChild, 0);
|
|
range1.setEnd(abc.firstChild, 3);
|
|
const range2 = document.createRange();
|
|
range2.setStart(abc.firstChild, 0);
|
|
range2.setEnd(abc.firstChild, 3);
|
|
|
|
ok(
|
|
rangeBoundariesAreEqual(range1, range2),
|
|
"Ranges should have the same boundary points when containers are text nodes"
|
|
);
|
|
|
|
const range3 = document.createRange();
|
|
range3.selectNode(abc);
|
|
const range4 = document.createRange();
|
|
range4.selectNode(abc);
|
|
ok(
|
|
rangeBoundariesAreEqual(range3, range4),
|
|
"Ranges should have the same boundary points when containers are nodes"
|
|
);
|
|
}
|
|
|
|
async function basicTests() {
|
|
const createDirectiveTimeStart = await GleanTest.domTextfragment.createDirective.testGetValue() ?? {count: 0};
|
|
let lastCreateTimeCount = createDirectiveTimeStart.count;
|
|
for(let testCase of [
|
|
{
|
|
name: "Text directive is first string in the document",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 0,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 3,
|
|
content: "abc",
|
|
textDirective: "text=abc"
|
|
},
|
|
{
|
|
name: "Text directive is the second word in the document",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 4,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 7,
|
|
content: "def",
|
|
textDirective: "text=def"
|
|
},
|
|
{
|
|
name: "Text directive spans two words",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 0,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 7,
|
|
content: "abc def",
|
|
textDirective: "text=abc%20def"
|
|
},
|
|
{
|
|
name: "Text directive is second occurrence of content",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 12,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 15,
|
|
content: "abc",
|
|
textDirective: "text=ghi-,abc"
|
|
},
|
|
{
|
|
name: "Text directive is second occurrence, suffix is shorter",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 20,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 23,
|
|
content: "ghi",
|
|
textDirective: "text=ghi,-jkl"
|
|
},
|
|
{
|
|
name: "Shortest text directive crosses block boundary",
|
|
startContainer: abc.lastChild.firstChild,
|
|
startOffset: 0,
|
|
endContainer: abc.lastChild.firstChild,
|
|
endOffset: 3,
|
|
content: "ghi",
|
|
textDirective: "text=ghi,-foo"
|
|
},
|
|
{
|
|
name: "Text directive crosses block boundary",
|
|
startContainer: block.firstChild,
|
|
startOffset: 0,
|
|
endContainer: afterblockboundary.firstChild,
|
|
endOffset: 18,
|
|
content: "pspanpafterblockboundary",
|
|
textDirective: "text=pspanp,afterblockboundary",
|
|
},
|
|
{
|
|
name: "Text directive crosses several block boundaries",
|
|
startContainer: foo.firstChild,
|
|
startOffset: 0,
|
|
endContainer: afterblockboundary.firstChild,
|
|
endOffset: 18,
|
|
content: "foo\n pspanpafterblockboundary",
|
|
textDirective: "text=foo,afterblockboundary",
|
|
},
|
|
{
|
|
name: "Text directive needs to check before previous block boundary",
|
|
startContainer: secondListElement.firstChild,
|
|
startOffset: 0,
|
|
endContainer: secondListElement.firstChild,
|
|
endOffset: 6,
|
|
content: "Second",
|
|
textDirective: "text=different-,Second"
|
|
},
|
|
{
|
|
name: "Text directive contains emoji as start",
|
|
startContainer: textWithEmoji.firstChild.firstChild,
|
|
startOffset: 0,
|
|
endContainer: textWithEmoji.firstChild.firstChild,
|
|
endOffset: 2,
|
|
content: "😍",
|
|
textDirective: "text=%F0%9F%98%8D",
|
|
},
|
|
{
|
|
name: "Text directive contains emoji as start and prefix",
|
|
startContainer: textWithEmoji.firstChild.firstChild,
|
|
startOffset: 2,
|
|
endContainer: textWithEmoji.firstChild.firstChild,
|
|
endOffset: 4,
|
|
content: "😍",
|
|
textDirective: "text=%F0%9F%98%8D-,%F0%9F%98%8D",
|
|
},
|
|
{
|
|
name: "Text directive contains emoji as prefix",
|
|
startContainer: textWithEmoji.firstChild.nextSibling,
|
|
startOffset: 1,
|
|
endContainer: textWithEmoji.firstChild.nextSibling,
|
|
endOffset: 4,
|
|
content: "abc",
|
|
textDirective: "text=%F0%9F%98%8D-,abc",
|
|
},
|
|
{
|
|
name: "Text directive contains slightly different emoji as prefix",
|
|
startContainer: textWithDifferentEmoji.firstChild.nextSibling,
|
|
startOffset: 1,
|
|
endContainer: textWithDifferentEmoji.firstChild.nextSibling,
|
|
endOffset: 4,
|
|
content: "abc",
|
|
textDirective: "text=%F0%9F%98%8F%F0%9F%98%8D-,abc",
|
|
},
|
|
{
|
|
name: "Text directive needs to compare emojis for suffix",
|
|
startContainer: textWithDifferentEmoji.firstChild.nextSibling,
|
|
startOffset: 5,
|
|
endContainer: textWithDifferentEmoji.firstChild.nextSibling,
|
|
endOffset: 8,
|
|
content: "def",
|
|
textDirective: "text=def,-%F0%9F%98%8D%F0%9F%98%8F"
|
|
},
|
|
{
|
|
name: "Text directive contains search-invisible content",
|
|
startContainer: textWithInvisibleContent.firstChild,
|
|
startOffset: 0,
|
|
endContainer: textWithInvisibleContent.lastChild,
|
|
endOffset: textWithInvisibleContent.lastChild.length,
|
|
content: "Text with display:none content",
|
|
textDirective: "text=Text%20with%20content"
|
|
},
|
|
{
|
|
name: "Range-based text directive needs prefix",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 12,
|
|
endContainer: foo.firstChild,
|
|
endOffset: 3,
|
|
content: "abc def ghi jkl abc. def ghi\n foo",
|
|
textDirective: "text=ghi-,abc,foo"
|
|
},
|
|
{
|
|
name: "Range-based text directive, end term is at inline boundary",
|
|
startContainer: textWithInvisibleContent.firstChild,
|
|
startOffset: 0,
|
|
endContainer: wordBoundaryAtInlineBoundary.firstChild,
|
|
endOffset: 7,
|
|
content: "Text with display:none content\n WordEnd",
|
|
textDirective: "text=Text,WordEnd"
|
|
},
|
|
{
|
|
name: "Finding text directive matches must correctly deal with block boundaries",
|
|
startContainer: prevMatchAtEndOfBlock.firstChild,
|
|
startOffset: 0,
|
|
endContainer: prevMatchAtEndOfBlock.firstChild,
|
|
endOffset: 10,
|
|
content: "EndOfBlock",
|
|
textDirective: "text=EndOfBlock-,EndOfBlock"
|
|
},
|
|
{
|
|
name: "Finding text directive across table rows",
|
|
startContainer: firstRow.firstChild,
|
|
startOffset: 0,
|
|
endContainer: secondRow.firstChild,
|
|
endOffset: secondRow.firstChild.length,
|
|
content: "first row second row",
|
|
textDirective: "text=first%20row,second%20row",
|
|
},
|
|
{
|
|
name: "Finding prefix in the correct order",
|
|
startContainer: loremIpsum.firstChild,
|
|
startOffset: 308,
|
|
endContainer: loremIpsum.firstChild,
|
|
endOffset: 313,
|
|
content: "Lorem",
|
|
textDirective: "text=a-,Lorem"
|
|
},
|
|
{
|
|
name: "Range-based text directive with multiple end matches 1 (first match)",
|
|
startContainer: rangeBasedWithMultipleEndMatchesBegin.firstChild,
|
|
startOffset: 0,
|
|
endContainer: rangeBasedWithMultipleEndMatchesEnd.firstChild,
|
|
endOffset: 3,
|
|
content: "Begin and begin or begin with begin\n end",
|
|
textDirective: "text=Begin,end"
|
|
},
|
|
{
|
|
name: "Range-based text directive with multiple end matches 2 (use end because it's shorter)",
|
|
startContainer: rangeBasedWithMultipleEndMatchesBegin.firstChild,
|
|
startOffset: 0,
|
|
endContainer: rangeBasedWithMultipleEndMatchesEnd.firstChild,
|
|
endOffset: 10,
|
|
content: "Begin and begin or begin with begin\n end or end",
|
|
textDirective: "text=Begin,or%20end"
|
|
},
|
|
{
|
|
name: "Range-based text directive with multiple end matches 3 (use suffix because it's shorter)",
|
|
startContainer: rangeBasedWithMultipleEndMatchesBegin.firstChild,
|
|
startOffset: 0,
|
|
endContainer: rangeBasedWithMultipleEndMatchesEnd.firstChild,
|
|
endOffset: 18,
|
|
content: "Begin and begin or begin with begin\n end or end and end",
|
|
textDirective: "text=Begin,end,-at"
|
|
},
|
|
{
|
|
name: "Range-based text directive with multiple start matches 1 (use start because it's shorter)",
|
|
startContainer: rangeBasedWithMultipleEndMatchesBegin.firstChild,
|
|
startOffset: 10,
|
|
endContainer: rangeBasedWithMultipleEndMatchesEnd.firstChild,
|
|
endOffset: 18,
|
|
content: "begin or begin with begin\n end or end and end",
|
|
textDirective: "text=begin%20or,end,-at"
|
|
},
|
|
{
|
|
name: "Range-based text directive with multiple start matches 2 (use prefix because it's shorter)",
|
|
startContainer: rangeBasedWithMultipleEndMatchesBegin.firstChild,
|
|
startOffset: 19,
|
|
endContainer: rangeBasedWithMultipleEndMatchesEnd.firstChild,
|
|
endOffset: 18,
|
|
content: "begin with begin\n end or end and end",
|
|
textDirective: "text=or-,begin,end,-at"
|
|
},
|
|
{
|
|
name: "Empty text nodes need to be handled correctly when finding word boundary looking left",
|
|
startContainer: afterEmptyTextNode.firstChild,
|
|
startOffset: 0,
|
|
endContainer: afterEmptyTextNode.firstChild,
|
|
endOffset: 5,
|
|
content: "after",
|
|
textDirective: "text=after"
|
|
},
|
|
{
|
|
name: "Empty text nodes need to be handled correctly when finding word boundary looking right",
|
|
startContainer: beforeEmptyTextNode.firstChild,
|
|
startOffset: 18,
|
|
endContainer: beforeEmptyTextNode.firstChild,
|
|
endOffset: 22,
|
|
content: "node",
|
|
textDirective: "text=node"
|
|
},
|
|
]) {
|
|
const range = document.createRange();
|
|
range.setStart(testCase.startContainer, testCase.startOffset);
|
|
range.setEnd(testCase.endContainer, testCase.endOffset);
|
|
is(
|
|
range.toString(), testCase.content,
|
|
`${testCase.name}: Precondition - Range has expected value`
|
|
);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
|
|
const textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(
|
|
textDirective, testCase.textDirective,
|
|
`${testCase.name}: text directive has expected value '${testCase.textDirective}'`
|
|
);
|
|
// load the page with the given text directive
|
|
location.hash = `#:~:${textDirective}`;
|
|
await rAF();
|
|
|
|
// access the range from the loaded text directive and compare the boundary points
|
|
ranges = SpecialPowers.wrap(document).fragmentDirective.getTextDirectiveRanges();
|
|
is(
|
|
ranges.length, 1,
|
|
`${testCase.name}: There is one text fragment range on the document`
|
|
);
|
|
|
|
is(
|
|
ranges[0].toString(), range.toString(),
|
|
`${testCase.name}: Ranges have the same content`
|
|
);
|
|
ok(
|
|
rangeBoundariesAreEqual(range, ranges[0]),
|
|
`${testCase.name}: Ranges have the same boundary points`
|
|
);
|
|
|
|
// finally, remove all text directives to clean up for the next test.
|
|
SpecialPowers.wrap(document).fragmentDirective.removeAllTextDirectives();
|
|
|
|
const createDirectiveTime = await GleanTest.domTextfragment.createDirective.testGetValue() ?? {count: 0};
|
|
is(createDirectiveTime.count, lastCreateTimeCount + 1, `${testCase.name}: Glean should have recorded the time it took to create the text directive`);
|
|
lastCreateTimeCount = createDirectiveTime.count;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calling the API with an empty / collapsed range should
|
|
* return an empty string, not an error.
|
|
*/
|
|
async function testEmptyRange() {
|
|
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
let textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(
|
|
textDirective, null,
|
|
"Empty input range: Produces empty text directive"
|
|
);
|
|
const range = document.createRange();
|
|
range.selectNode(abc);
|
|
range.collapse(true);
|
|
selection.addRange(range);
|
|
textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(
|
|
textDirective, null,
|
|
"Collapsed input range: Produces empty text directive"
|
|
);
|
|
range.selectNode(image);
|
|
textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(
|
|
textDirective, null,
|
|
"Input range contains image only: Produces empty text directive"
|
|
);
|
|
|
|
}
|
|
|
|
async function testExpandRangeToWordBoundaries() {
|
|
const createDirectiveTimeStart = await GleanTest.domTextfragment.createDirective.testGetValue() ?? {count: 0};
|
|
let lastCreateTimeCount = createDirectiveTimeStart.count;
|
|
|
|
for (testCase of [
|
|
{
|
|
name: "Expanding single-word range to word boundaries (input is inside word)",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 5,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 6,
|
|
outputStartContainer: abc.firstChild,
|
|
outputStartOffset: 4,
|
|
outputEndContainer: abc.firstChild,
|
|
outputEndOffset: 7,
|
|
content: "e",
|
|
outputContent: "def",
|
|
textDirective: "text=def"
|
|
},
|
|
{
|
|
name: "Expanding single-word range to word boundaries (input is start of word)",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 4,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 5,
|
|
outputStartContainer: abc.firstChild,
|
|
outputStartOffset: 4,
|
|
outputEndContainer: abc.firstChild,
|
|
outputEndOffset: 7,
|
|
content: "d",
|
|
outputContent: "def",
|
|
textDirective: "text=def"
|
|
},
|
|
{
|
|
name: "Expanding single-word range to word boundaries (input is end of word)",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 6,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 7,
|
|
outputStartContainer: abc.firstChild,
|
|
outputStartOffset: 4,
|
|
outputEndContainer: abc.firstChild,
|
|
outputEndOffset: 7,
|
|
content: "f",
|
|
outputContent: "def",
|
|
textDirective: "text=def"
|
|
},
|
|
{
|
|
name: "Expanding multi-word range to word boundaries",
|
|
startContainer: abc.firstChild,
|
|
startOffset: 5,
|
|
endContainer: abc.firstChild,
|
|
endOffset: 9,
|
|
outputStartContainer: abc.firstChild,
|
|
outputStartOffset: 4,
|
|
outputEndContainer: abc.firstChild,
|
|
outputEndOffset: 11,
|
|
content: "ef g",
|
|
outputContent: "def ghi",
|
|
textDirective: "text=def%20ghi"
|
|
},
|
|
{
|
|
name: "Expanding inline-boundary word range to word boundaries",
|
|
startContainer: nestedinlinespan.firstChild,
|
|
startOffset: 0,
|
|
endContainer: nestedinlinespan.firstChild,
|
|
endOffset: 1,
|
|
outputStartContainer: block.firstChild,
|
|
outputStartOffset: 0,
|
|
outputEndContainer: block.lastChild,
|
|
outputEndOffset: 1,
|
|
content: "a",
|
|
outputContent: "pspanp",
|
|
textDirective: "text=pspanp"
|
|
},
|
|
]) {
|
|
|
|
const range = document.createRange();
|
|
range.setStart(testCase.startContainer, testCase.startOffset);
|
|
range.setEnd(testCase.endContainer, testCase.endOffset);
|
|
is(
|
|
range.toString().trim(), testCase.content,
|
|
`${testCase.name}: Precondition - Range has expected value`
|
|
);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
const textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(
|
|
textDirective, testCase.textDirective,
|
|
`${testCase.name}: text directive has expected value '${textDirective}'`
|
|
);
|
|
// load the page with the given text directive
|
|
location.hash = `#:~:${textDirective}`;
|
|
await rAF();
|
|
|
|
// access the range from the loaded text directive and compare the boundary points
|
|
ranges = SpecialPowers.wrap(document).fragmentDirective.getTextDirectiveRanges();
|
|
const expectedRange = document.createRange();
|
|
expectedRange.setStart(testCase.outputStartContainer, testCase.outputStartOffset);
|
|
expectedRange.setEnd(testCase.outputEndContainer, testCase.outputEndOffset);
|
|
is(
|
|
expectedRange.toString(), ranges[0].toString(),
|
|
`${testCase.name}: Ranges have the same content '${ranges[0].toString()}'`
|
|
);
|
|
ok(
|
|
rangeBoundariesAreEqual(expectedRange, ranges[0]),
|
|
`${testCase.name}: Ranges have the same boundary points`
|
|
);
|
|
|
|
// finally, remove all text directives to clean up for the next test.
|
|
SpecialPowers.wrap(document).fragmentDirective.removeAllTextDirectives();
|
|
location.hash = "";
|
|
|
|
const createDirectiveTime = await GleanTest.domTextfragment.createDirective.testGetValue() ?? {count: 0};
|
|
is(createDirectiveTime.count, lastCreateTimeCount + 1, `${testCase.name}: Glean should have recorded the time it took to create the text directive`);
|
|
lastCreateTimeCount = createDirectiveTime.count;
|
|
|
|
}
|
|
}
|
|
|
|
async function testNonUniqueTestSurroundedByBlockBoundaries() {
|
|
const createDirectiveTimeStart = await GleanTest.domTextfragment.createDirective.testGetValue() ?? {count: 0};
|
|
|
|
const testCase = "Creating a text directive which cannot be found"
|
|
const range = document.createRange();
|
|
range.setStart(surroundedByBlockBoundaries.firstChild, 4);
|
|
range.setEnd(surroundedByBlockBoundaries.firstChild, 7);
|
|
is(range.toString(), "def", `${testCase}: Range has expected value`);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
const textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(textDirective, null, `${testCase}: It's not possible to create a unique text directive`);
|
|
|
|
const createDirectiveTime = await GleanTest.domTextfragment.createDirective.testGetValue() ?? {count: 0};
|
|
is(createDirectiveTime.count, createDirectiveTimeStart.count + 1, `${testCase}: Glean should have recorded the time it took to create the text directive`);
|
|
}
|
|
|
|
async function testRangeBasedWithEmptySurroundingBlocks() {
|
|
const testCase = "Empty blocks at beginning or end of range must be ignored";
|
|
const range = document.createRange();
|
|
range.setStart(emptyBlockAtBeginning, 0);
|
|
range.setEnd(emptyBlockAtEnd.nextSibling ,0);
|
|
const content = "AfterEmptyBlock\n BeforeEmptyBlock";
|
|
const textDirectiveString = "text=AfterEmptyBlock,BeforeEmptyBlock";
|
|
is(range.toString(), content, `${testCase}: Range has expected value`);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
const textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(textDirective, textDirectiveString, `${testCase}: text directive has expected value '${textDirectiveString}'`);
|
|
location.hash = `:~:${textDirective}`;
|
|
await rAF();
|
|
ranges = SpecialPowers.wrap(document).fragmentDirective.getTextDirectiveRanges();
|
|
is(ranges.length, 1, `${testCase}: There is one text fragment range on the document`);
|
|
is(ranges[0].toString(), range.toString(), `${testCase}: Ranges have the same content`);
|
|
}
|
|
async function testRangeBasedWithNonBreakableWord() {
|
|
const testCase = "Range-based text directive with non-breakable word";
|
|
const range = document.createRange();
|
|
range.setStart(longWord.firstChild, 0);
|
|
range.setEnd(longWord.firstChild, longWord.firstChild.length);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
const textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(textDirective, null, `${testCase}: This test case should not produce a text directive`);
|
|
}
|
|
|
|
async function testMultipleRanges() {
|
|
let testCase = "Two single-word text directives";
|
|
const range1 = document.createRange();
|
|
range1.setStart(abc.firstChild, 0);
|
|
range1.setEnd(abc.firstChild, 3);
|
|
const range2 = document.createRange();
|
|
range2.setStart(foo.firstChild, 0);
|
|
range2.setEnd(foo.firstChild, 3);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range1);
|
|
selection.addRange(range2);
|
|
const textDirective = await SpecialPowers.wrap(document).fragmentDirective.createTextDirectiveForSelection();
|
|
is(textDirective, "text=abc&text=foo", `${testCase}: The text fragment has the expected value`);
|
|
}
|
|
|
|
async function runTests() {
|
|
try {
|
|
await SpecialPowers.pushPrefEnv({"set": [
|
|
["dom.text_fragments.enabled", true],
|
|
["dom.text_fragments.create_text_fragment.enabled", true],
|
|
]});
|
|
testRangeBoundariesAreEqual();
|
|
await basicTests();
|
|
await testEmptyRange();
|
|
await testExpandRangeToWordBoundaries();
|
|
await testNonUniqueTestSurroundedByBlockBoundaries();
|
|
await testRangeBasedWithEmptySurroundingBlocks();
|
|
await testRangeBasedWithNonBreakableWord();
|
|
await testMultipleRanges();
|
|
|
|
}
|
|
finally {
|
|
SimpleTest.finish();
|
|
}
|
|
}
|
|
|
|
document.body.onload = runTests;
|
|
</script>
|
|
</body>
|
|
</html>
|