feat(text selector): concat sibling text nodes when calculating text (#1969)

Text that is split into multiple text nodes now matches.
This commit is contained in:
Dmitry Gozman 2020-04-24 20:49:29 -07:00 committed by GitHub
parent b60c006c63
commit 7ecf252dd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 16 deletions

View file

@ -86,21 +86,33 @@ function queryInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean): E
const shadowRoots: ShadowRoot[] = [];
if (shadow && (root as Element).shadowRoot)
shadowRoots.push((root as Element).shadowRoot!);
while (walker.nextNode()) {
const node = walker.currentNode;
if (node.nodeType === Node.ELEMENT_NODE) {
let lastTextParent: Element | null = null;
let lastText = '';
while (true) {
const node = walker.nextNode();
const textParent = (node && node.nodeType === Node.TEXT_NODE) ? node.parentElement : null;
if (lastTextParent && textParent !== lastTextParent) {
if (lastTextParent.nodeName !== 'SCRIPT' && lastTextParent.nodeName !== 'STYLE' && matcher(lastText))
return lastTextParent;
lastText = '';
}
lastTextParent = textParent;
if (!node)
break;
if (node.nodeType === Node.TEXT_NODE) {
lastText += node.nodeValue;
} else {
const element = node as Element;
if ((element instanceof HTMLInputElement) && (element.type === 'submit' || element.type === 'button') && matcher(element.value))
return element;
if (shadow && element.shadowRoot)
shadowRoots.push(element.shadowRoot);
} else {
const element = node.parentElement;
const text = node.nodeValue;
if (element && element.nodeName !== 'SCRIPT' && element.nodeName !== 'STYLE' && text && matcher(text))
return element;
}
}
for (const shadowRoot of shadowRoots) {
const element = queryInternal(shadowRoot, matcher, shadow);
if (element)
@ -114,21 +126,33 @@ function queryAllInternal(root: SelectorRoot, matcher: Matcher, shadow: boolean,
const shadowRoots: ShadowRoot[] = [];
if (shadow && (root as Element).shadowRoot)
shadowRoots.push((root as Element).shadowRoot!);
while (walker.nextNode()) {
const node = walker.currentNode;
if (node.nodeType === Node.ELEMENT_NODE) {
let lastTextParent: Element | null = null;
let lastText = '';
while (true) {
const node = walker.nextNode();
const textParent = (node && node.nodeType === Node.TEXT_NODE) ? node.parentElement : null;
if (lastTextParent && textParent !== lastTextParent) {
if (lastTextParent.nodeName !== 'SCRIPT' && lastTextParent.nodeName !== 'STYLE' && matcher(lastText))
result.push(lastTextParent);
lastText = '';
}
lastTextParent = textParent;
if (!node)
break;
if (node.nodeType === Node.TEXT_NODE) {
lastText += node.nodeValue;
} else {
const element = node as Element;
if ((element instanceof HTMLInputElement) && (element.type === 'submit' || element.type === 'button') && matcher(element.value))
result.push(element);
if (shadow && element.shadowRoot)
shadowRoots.push(element.shadowRoot);
} else {
const element = node.parentElement;
const text = node.nodeValue;
if (element && element.nodeName !== 'SCRIPT' && element.nodeName !== 'STYLE' && text && matcher(text))
result.push(element);
}
}
for (const shadowRoot of shadowRoots)
queryAllInternal(shadowRoot, matcher, shadow, result);
}

View file

@ -540,6 +540,26 @@ describe('text selector', () => {
await page.setContent(`<div> ' </div><div> " </div>`);
expect(await page.$eval(`text="`, e => e.outerHTML)).toBe('<div> " </div>');
expect(await page.$eval(`text='`, e => e.outerHTML)).toBe('<div> \' </div>');
await page.setContent(`<div>a<br>b</div><div>a</div>`);
expect(await page.$eval(`text=a`, e => e.outerHTML)).toBe('<div>a<br>b</div>');
expect(await page.$eval(`text=b`, e => e.outerHTML)).toBe('<div>a<br>b</div>');
expect(await page.$(`text=ab`)).toBe(null);
expect(await page.$$eval(`text=a`, els => els.length)).toBe(2);
expect(await page.$$eval(`text=b`, els => els.length)).toBe(1);
expect(await page.$$eval(`text=ab`, els => els.length)).toBe(0);
await page.setContent(`<div></div><span></span>`);
await page.$eval('div', div => {
div.appendChild(document.createTextNode('hello'));
div.appendChild(document.createTextNode('world'));
});
await page.$eval('span', span => {
span.appendChild(document.createTextNode('hello'));
span.appendChild(document.createTextNode('world'));
});
expect(await page.$eval(`text=lowo`, e => e.outerHTML)).toBe('<div>helloworld</div>');
expect(await page.$$eval(`text=lowo`, els => els.map(e => e.outerHTML).join(''))).toBe('<div>helloworld</div><span>helloworld</span>');
});
it('create', async ({page}) => {