Commit 88d98fda authored by Nick Burris's avatar Nick Burris Committed by Commit Bot

Add more scroll to text WPTs

Adds more web platform tests for scroll to text:

- Combination of non-text directive and text directive
- Scroll second text directive into view if first doesn't match
- Matching shadow DOM
- Don't scroll to hidden text
- Text that requires a horizontal scroll into view
- Also fixed non-ASCII test case (and changed to Japanese example)

Change-Id: Id7e21a185897267dc565b19adc7bfdfb5fe70974
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1903624
Commit-Queue: Nick Burris <nburris@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714381}
parent 19f6b5e6
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<script> <script>
function isInView(element) { function isInView(element) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
return rect.top >= 0 && rect.top <= window.innerHeight; return rect.top >= 0 && rect.top <= window.innerHeight
&& rect.left >= 0 && rect.left <= window.innerWidth;
} }
function checkScroll() { function checkScroll() {
...@@ -22,6 +23,12 @@ function checkScroll() { ...@@ -22,6 +23,12 @@ function checkScroll() {
position = 'cross-node-context'; position = 'cross-node-context';
else if (isInView(document.getElementById('text-directive-parameters'))) else if (isInView(document.getElementById('text-directive-parameters')))
position = 'text-directive-parameters'; position = 'text-directive-parameters';
else if (isInView(document.getElementById('shadow-parent')))
position = 'shadow-parent';
else if (isInView(document.getElementById('hidden')))
position = 'hidden';
else if (isInView(document.getElementById('horizontal-scroll')) && window.scrollX > 0)
position = 'horizontal-scroll';
bc.postMessage({ scrollPosition: position, href: window.location.href }); bc.postMessage({ scrollPosition: position, href: window.location.href });
bc.close(); bc.close();
...@@ -29,40 +36,38 @@ function checkScroll() { ...@@ -29,40 +36,38 @@ function checkScroll() {
} }
</script> </script>
<style> <style>
body { .scroll-section {
height: 6200px; /* 1000px margin on top and bottom so only one section can be in view. */
margin: 1000px 0px;
} }
#element { #hidden {
position: absolute; visibility: hidden;
top: 2000px;
} }
#text { #horizontal-scroll {
position: absolute; margin-left: 2000px;
top: 3000px;
} }
#more-text { #display-none {
position: absolute; display: none;
top: 4000px;
}
#cross-node-context {
position: absolute;
top: 5000px;
}
#text-directive-parameters {
position: absolute;
top: 6000px;
} }
</style> </style>
<body onload="window.requestAnimationFrame(checkScroll)"> <body onload="window.requestAnimationFrame(checkScroll)">
<div id="element">Element</div> <div id="element" class="scroll-section">Element</div>
<p id="text">This is a test page !$'()*+./:;=?@_~ &,- &#x2665;</p> <p id="text" class="scroll-section">This is a test page !$'()*+./:;=?@_~ &,- &#x30cd;&#x30b3;</p>
<p id="more-text">More test page text</p> <p id="more-text" class="scroll-section">More test page text</p>
<div id="cross-node-context"> <div id="cross-node-context" class="scroll-section">
<div> <div>
<p>prefix</p> <p>prefix</p>
<p>test page</p> <p>test page</p>
</div> </div>
<div><p>suffix</p></div> <div><p>suffix</p></div>
</div> </div>
<p id="text-directive-parameters">this,is,test,page</p> <p id="text-directive-parameters" class="scroll-section">this,is,test,page</p>
<div id="shadow-parent" class="scroll-section"></div>
<script>
let shadow = document.getElementById("shadow-parent").attachShadow({mode: 'open'});
shadow.innerHTML = '<p>shadow text</p>';
</script>
<p id="hidden" class="scroll-section">hidden text</p>
<p id="horizontal-scroll" class="scroll-section">horizontally scrolled text</p>
<p id="display-none" class="scroll-section">display none</p>
</body> </body>
...@@ -13,162 +13,207 @@ let test_cases = [ ...@@ -13,162 +13,207 @@ let test_cases = [
{ {
fragment: '#', fragment: '#',
expect_position: 'top', expect_position: 'top',
description: 'Empty hash' description: 'Empty hash should scroll to top'
}, },
{ {
fragment: '#:~:text=this,is,test,page', fragment: '#:~:text=this,is,test,page',
expect_position: 'top', expect_position: 'top',
description: 'Text directive with invalid syntax, context terms without "-"' description: 'Text directive with invalid syntax (context terms without "-") should not parse as a text directive'
}, },
{ {
fragment: '#element:~:directive', fragment: '#element:~:directive',
expect_position: 'element', expect_position: 'element',
description: 'Generic fragment directive with existing element fragment' description: 'Generic fragment directive with existing element fragment should scroll to element'
},
{
fragment: '#:~:TEXT=test',
expect_position: 'top',
description: 'Uppercase TEXT directive should not parse as a text directive'
}, },
// Test exact text matching, with all combinations of context terms // Test exact text matching, with all combinations of context terms
{ {
fragment: '#:~:text=test', fragment: '#:~:text=test',
expect_position: 'text', expect_position: 'text',
description: 'Exact text with no context' description: 'Exact text with no context should match text'
}, },
{ {
fragment: '#:~:text=this is a-,test', fragment: '#:~:text=this is a-,test',
expect_position: 'text', expect_position: 'text',
description: 'Exact text with prefix' description: 'Exact text with prefix should match text'
}, },
{ {
fragment: '#:~:text=test,-page', fragment: '#:~:text=test,-page',
expect_position: 'text', expect_position: 'text',
description: 'Exact text with suffix' description: 'Exact text with suffix should match text'
}, },
{ {
fragment: '#:~:text=this is a-,test,-page', fragment: '#:~:text=this is a-,test,-page',
expect_position: 'text', expect_position: 'text',
description: 'Exact text with prefix and suffix' description: 'Exact text with prefix and suffix should match text'
}, },
// Test text range matching, with all combinations of context terms // Test text range matching, with all combinations of context terms
{ {
fragment: '#:~:text=this,page', fragment: '#:~:text=this,page',
expect_position: 'text', expect_position: 'text',
description: 'Text range with no context' description: 'Text range with no context should match text'
}, },
{ {
fragment: '#:~:text=this-,is,test', fragment: '#:~:text=this-,is,test',
expect_position: 'text', expect_position: 'text',
description: 'Text range with prefix' description: 'Text range with prefix should match text'
}, },
{ {
fragment: '#:~:text=this,test,-page', fragment: '#:~:text=this,test,-page',
expect_position: 'text', expect_position: 'text',
description: 'Text range with suffix' description: 'Text range with suffix should match text'
}, },
{ {
fragment: '#:~:text=this-,is,test,-page', fragment: '#:~:text=this-,is,test,-page',
expect_position: 'text', expect_position: 'text',
description: 'Text range with prefix and suffix' description: 'Text range with prefix and suffix should match text'
}, },
// Test partially non-matching text ranges // Test partially non-matching text ranges
{ {
fragment: '#:~:text=this,none', fragment: '#:~:text=this,none',
expect_position: 'top', expect_position: 'top',
description: 'Text range with non-matching endText' description: 'Text range with non-matching endText should not match'
}, },
{ {
fragment: '#:~:text=none,page', fragment: '#:~:text=none,page',
expect_position: 'top', expect_position: 'top',
description: 'Text range with non-matching startText' description: 'Text range with non-matching startText should not match'
}, },
// Test non-matching context terms // Test non-matching context terms
{ {
fragment: '#:~:text=this-,is,page,-none', fragment: '#:~:text=this-,is,page,-none',
expect_position: 'top', expect_position: 'top',
description: 'Text range with prefix and nonmatching suffix' description: 'Text range with prefix and nonmatching suffix should not match'
}, },
{ {
fragment: '#:~:text=none-,this,test,-page', fragment: '#:~:text=none-,this,test,-page',
expect_position: 'top', expect_position: 'top',
description: 'Text range with nonmatching prefix and matching suffix' description: 'Text range with nonmatching prefix and matching suffix should not match'
}, },
// Test percent encoded characters // Test percent encoded characters
{ {
fragment: '#:~:text=this%20is%20a%20test%20page', fragment: '#:~:text=this%20is%20a%20test%20page',
expect_position: 'text', expect_position: 'text',
description: 'Exact text with percent encoded spaces' description: 'Exact text with percent encoded spaces should match text'
}, },
{ {
fragment: '#:~:text=test%20pag', fragment: '#:~:text=test%20pag',
expect_position: 'top', expect_position: 'top',
description: 'Non-whole-word exact text with spaces' description: 'Non-whole-word exact text with spaces should not match'
}, },
{ {
fragment: '#:~:text=%26%2C%2D', fragment: '#:~:text=%26%2C%2D',
expect_position: 'text', expect_position: 'text',
description: 'Fragment directive with percent encoded syntactical characters "&,-"' description: 'Fragment directive with percent encoded syntactical characters "&,-" should match text'
}, },
{ {
fragment: '#:~:text=%2665', fragment: '#:~:text=%E3%83%8D%E3%82%B3',
expect_position: 'text', expect_position: 'text',
description: 'Fragment directive with percent encoded non-ASCII unicode character' description: 'Fragment directive with percent encoded non-ASCII unicode character should match text'
}, },
{ {
fragment: '#:~:text=!$\'()*+./:;=?@_~', fragment: '#:~:text=!$\'()*+./:;=?@_~',
expect_position: 'text', expect_position: 'text',
description: 'Fragment directive with all TextMatchChars' description: 'Fragment directive with all TextMatchChars should match text'
}, },
// Test multiple text directives // Test multiple text directives
{ {
fragment: '#:~:text=this&text=test,page', fragment: '#:~:text=this&text=test,page',
expect_position: 'text', expect_position: 'text',
description: 'Multiple matching exact texts' description: 'Multiple matching exact texts should match text'
}, },
{ {
fragment: '#:~:text=tes&text=age', fragment: '#:~:text=tes&text=age',
expect_position: 'top', expect_position: 'top',
description: 'Multiple non-whole-word exact texts' description: 'Multiple non-whole-word exact texts should not match'
},
{
fragment: '#:~:text=none&text=test%20page',
expect_position: 'text',
description: 'A non-matching text directive followed by a matching text directive should match and scroll into view the second text directive'
},
{
fragment: '#:~:text=test%20page&directive',
expect_position: 'text',
description: 'Text directive followed by non-text directive should match text'
},
{
fragment: '#:~:text=test&directive&text=page',
expect_position: 'text',
description: 'Multiple text directives and a non-text directive should match text'
}, },
// Test text directive behavior when there's an element fragment identifier // Test text directive behavior when there's an element fragment identifier
{ {
fragment: '#element:~:text=test', fragment: '#element:~:text=test',
expect_position: 'text', expect_position: 'text',
description: 'Text directive with existing element fragment' description: 'Text directive with existing element fragment should match and scroll into view text'
}, },
{ {
fragment: '#pagestate:~:text=test', fragment: '#pagestate:~:text=test',
expect_position: 'text', expect_position: 'text',
description: 'Text directive with nonexistent element fragment' description: 'Text directive with nonexistent element fragment should match and scroll into view text'
}, },
{ {
fragment: '#element:~:text=nomatch', fragment: '#element:~:text=nomatch',
expect_position: 'element', expect_position: 'element',
description: 'Non-matching text directive with existing element fragment' description: 'Non-matching text directive with existing element fragment should scroll to element'
}, },
{ {
fragment: '#pagestate:~:text=nomatch', fragment: '#pagestate:~:text=nomatch',
expect_position: 'top', expect_position: 'top',
description: 'Non-matching text directive with nonexistent element fragment' description: 'Non-matching text directive with nonexistent element fragment should not match and not scroll'
}, },
// Test ambiguous text matches disambiguated by context terms // Test ambiguous text matches disambiguated by context terms
{ {
fragment: '#:~:text=more-,test%20page', fragment: '#:~:text=more-,test%20page',
expect_position: 'more-text', expect_position: 'more-text',
description: 'Multiple match text directive disambiguated by prefix' description: 'Multiple match text directive disambiguated by prefix should match the prefixed text'
}, },
{ {
fragment: '#:~:text=test%20page,-text', fragment: '#:~:text=test%20page,-text',
expect_position: 'more-text', expect_position: 'more-text',
description: 'Multiple match text directive disambiguated by suffix' description: 'Multiple match text directive disambiguated by suffix should match the suffixed text'
}, },
{ {
fragment: '#:~:text=more-,test%20page,-text', fragment: '#:~:text=more-,test%20page,-text',
expect_position: 'more-text', expect_position: 'more-text',
description: 'Multiple match text directive disambiguated by prefix and suffix' description: 'Multiple match text directive disambiguated by prefix and suffix should match the text with the given context'
}, },
// Test context terms separated by node boundaries // Test context terms separated by node boundaries
{ {
fragment: '#:~:text=prefix-,test%20page,-suffix', fragment: '#:~:text=prefix-,test%20page,-suffix',
expect_position: 'cross-node-context', expect_position: 'cross-node-context',
description: 'Text directive with context terms separated by node boundaries' description: 'Text directive should match when context terms are separated by node boundaries'
},
// Test text directive within shadow DOM
{
fragment: '#:~:text=shadow%20text',
expect_position: 'shadow-parent',
description: 'Text directive should match text within shadow DOM'
},
// Test text directive within hidden and display none elements. These cases should not scroll into
// view, but still "match" in that they should be highlighted or otherwise visibly indicated
// if they were to become visible.
{
fragment: '#:~:text=hidden%20text',
expect_position: 'top',
description: 'Text directive should not scroll to hidden text'
}, },
{
fragment: '#:~:text=display%20none',
expect_position: 'top',
description: 'Text directive should not scroll to display none text'
},
// Test horizontal scroll into view
{
fragment: '#:~:text=horizontally%20scrolled%20text',
expect_position: 'horizontal-scroll',
description: 'Text directive should horizontally scroll into view'
}
]; ];
for (const test_case of test_cases) { for (const test_case of test_cases) {
...@@ -185,7 +230,7 @@ for (const test_case of test_cases) { ...@@ -185,7 +230,7 @@ for (const test_case of test_cases) {
assert_equals(data.href.indexOf(':~:'), -1, 'Expected fragment directive to be stripped from the URL.'); assert_equals(data.href.indexOf(':~:'), -1, 'Expected fragment directive to be stripped from the URL.');
assert_equals(data.scrollPosition, test_case.expect_position, assert_equals(data.scrollPosition, test_case.expect_position,
`Expected ${test_case.fragment} (${test_case.description}) to scroll to ${test_case.expect_position}.`); `Expected ${test_case.fragment} (${test_case.description}) to scroll to ${test_case.expect_position}.`);
}), `Test navigation with fragment: ${test_case.description}`); }), `Test navigation with fragment: ${test_case.description}.`);
} }
promise_test(t => new Promise(resolve => { promise_test(t => new Promise(resolve => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment