mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-15 06:02:57 +03:00
fix(text selector): do not match text inside <head> (#2413)
We already skip <script> and <style> tags because they are not the page content. Similar reasoning applies to <head> that has content that is never rendered on the page.
This commit is contained in:
parent
084d5ff48f
commit
8e4a1e7c67
@ -80,9 +80,24 @@ function createMatcher(selector: string): Matcher {
|
||||
return text => text.toLowerCase().includes(selector);
|
||||
}
|
||||
|
||||
// Skips <head>, <script> and <style> elements and all their children.
|
||||
const nodeFilter: NodeFilter = {
|
||||
acceptNode: node => {
|
||||
return node.nodeName === 'HEAD' || node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE' ?
|
||||
NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
||||
}
|
||||
};
|
||||
|
||||
// If we are querying inside a filtered element, nodeFilter is never called, so we need a separate check.
|
||||
function isFilteredNode(root: SelectorRoot, document: Document) {
|
||||
return root.nodeName === 'SCRIPT' || root.nodeName === 'STYLE' || document.head && document.head.contains(root);
|
||||
}
|
||||
|
||||
function queryInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean): Element | undefined {
|
||||
const document = root instanceof Document ? root : root.ownerDocument!;
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT);
|
||||
if (isFilteredNode(root, document))
|
||||
return;
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, nodeFilter);
|
||||
const shadowRoots: ShadowRoot[] = [];
|
||||
if (shadow && (root as Element).shadowRoot)
|
||||
shadowRoots.push((root as Element).shadowRoot!);
|
||||
@ -94,7 +109,7 @@ function queryInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean): E
|
||||
|
||||
const textParent = (node && node.nodeType === Node.TEXT_NODE) ? node.parentElement : null;
|
||||
if (lastTextParent && textParent !== lastTextParent) {
|
||||
if (lastTextParent.nodeName !== 'SCRIPT' && lastTextParent.nodeName !== 'STYLE' && matcher(lastText))
|
||||
if (matcher(lastText))
|
||||
return lastTextParent;
|
||||
lastText = '';
|
||||
}
|
||||
@ -122,7 +137,9 @@ function queryInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean): E
|
||||
|
||||
function queryAllInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean, result: Element[]) {
|
||||
const document = root instanceof Document ? root : root.ownerDocument!;
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT);
|
||||
if (isFilteredNode(root, document))
|
||||
return;
|
||||
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, nodeFilter);
|
||||
const shadowRoots: ShadowRoot[] = [];
|
||||
if (shadow && (root as Element).shadowRoot)
|
||||
shadowRoots.push((root as Element).shadowRoot!);
|
||||
@ -134,7 +151,7 @@ function queryAllInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean,
|
||||
|
||||
const textParent = (node && node.nodeType === Node.TEXT_NODE) ? node.parentElement : null;
|
||||
if (lastTextParent && textParent !== lastTextParent) {
|
||||
if (lastTextParent.nodeName !== 'SCRIPT' && lastTextParent.nodeName !== 'STYLE' && matcher(lastText))
|
||||
if (matcher(lastText))
|
||||
result.push(lastTextParent);
|
||||
lastText = '';
|
||||
}
|
||||
|
@ -551,6 +551,32 @@ describe('text selector', () => {
|
||||
expect(await page.$(`text="with"`)).toBe(null);
|
||||
});
|
||||
|
||||
it('should skip head, script and style', async({page}) => {
|
||||
await page.setContent(`
|
||||
<head>
|
||||
<title>title</title>
|
||||
<script>var script</script>
|
||||
<style>.style {}</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>var script</script>
|
||||
<style>.style {}</style>
|
||||
<div>title script style</div>
|
||||
</body>`);
|
||||
const head = await page.$('head');
|
||||
const title = await page.$('title');
|
||||
const script = await page.$('body script');
|
||||
const style = await page.$('body style');
|
||||
for (const text of ['title', 'script', 'style']) {
|
||||
expect(await page.$eval(`text=${text}`, e => e.nodeName)).toBe('DIV');
|
||||
expect(await page.$$eval(`text=${text}`, els => els.map(e => e.nodeName).join('|'))).toBe('DIV');
|
||||
for (const root of [head, title, script, style]) {
|
||||
expect(await root.$(`text=${text}`)).toBe(null);
|
||||
expect(await root.$$eval(`text=${text}`, els => els.length)).toBe(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should match input[type=button|submit]', async({page}) => {
|
||||
await page.setContent(`<input type="submit" value="hello"><input type="button" value="world">`);
|
||||
expect(await page.$eval(`text=hello`, e => e.outerHTML)).toBe('<input type="submit" value="hello">');
|
||||
|
Loading…
Reference in New Issue
Block a user