mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(text selector): make quoted selector match by text nodes (#5603)
This change turns quoted match to be case-sensitive (as before), but not strictly full-string for the whole element's text. This is a fix for a case where element contains text nodes and child elements: ```html <div>text1<span>child node</span>text2</div> ``` We now match this div by `text="text1"` and `text="text2"`.
This commit is contained in:
parent
8906ba332c
commit
0102e080f6
@ -148,7 +148,7 @@ Text selector has a few variations:
|
||||
page.click("text=Log in")
|
||||
```
|
||||
|
||||
- `text="Log in"` - text body can be escaped with single or double quotes for full-string case-sensitive match. For example `text="Log"` does not match `<button>Log in</button>` but instead matches `<span>Log</span>`.
|
||||
- `text="Log in"` - text body can be escaped with single or double quotes for case-sensitive match. For example `text="Log"` does not match `<button>log in</button>` but instead matches `<span>Log in</span>`.
|
||||
|
||||
Quoted body follows the usual escaping rules, e.g. use `\"` to escape double quote in a double-quoted string: `text="foo\"bar"`.
|
||||
|
||||
|
@ -780,8 +780,8 @@ function createTextMatcher(selector: string): { matcher: Matcher, strict: boolea
|
||||
const matcher = (text: string) => {
|
||||
text = text.trim().replace(/\s+/g, ' ');
|
||||
if (!strict)
|
||||
return text.toLowerCase().includes(selector);
|
||||
return text === selector;
|
||||
text = text.toLowerCase();
|
||||
return text.includes(selector);
|
||||
};
|
||||
return { matcher, strict };
|
||||
}
|
||||
|
@ -462,13 +462,15 @@ const hasTextEngine: SelectorEngine = {
|
||||
},
|
||||
};
|
||||
|
||||
function textMatcher(text: string, substring: boolean): (s: string) => boolean {
|
||||
function textMatcher(text: string, caseInsensitive: boolean): (s: string) => boolean {
|
||||
text = text.trim().replace(/\s+/g, ' ');
|
||||
text = text.toLowerCase();
|
||||
if (caseInsensitive)
|
||||
text = text.toLowerCase();
|
||||
return (s: string) => {
|
||||
s = s.trim().replace(/\s+/g, ' ');
|
||||
s = s.toLowerCase();
|
||||
return substring ? s.includes(text) : s === text;
|
||||
if (caseInsensitive)
|
||||
s = s.toLowerCase();
|
||||
return s.includes(text);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -103,9 +103,9 @@ it('should work', async ({page}) => {
|
||||
expect((await page.$$(`text="Sign in"`)).length).toBe(1);
|
||||
expect(await page.$eval(`text=lo wo`, e => e.outerHTML)).toBe('<span>Hello\n \nworld</span>');
|
||||
expect(await page.$eval(`text="Hello world"`, e => e.outerHTML)).toBe('<span>Hello\n \nworld</span>');
|
||||
expect(await page.$(`text="lo wo"`)).toBe(null);
|
||||
expect(await page.$eval(`text="lo wo"`, e => e.outerHTML)).toBe('<span>Hello\n \nworld</span>');
|
||||
expect((await page.$$(`text=lo \nwo`)).length).toBe(1);
|
||||
expect((await page.$$(`text="lo wo"`)).length).toBe(0);
|
||||
expect((await page.$$(`text="lo \nwo"`)).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with :text', async ({page}) => {
|
||||
@ -113,9 +113,9 @@ it('should work with :text', async ({page}) => {
|
||||
expect(await page.$eval(`:text("ya")`, e => e.outerHTML)).toBe('<div>ya</div>');
|
||||
expect(await page.$eval(`:text-is("ya")`, e => e.outerHTML)).toBe('<div>ya</div>');
|
||||
expect(await page.$eval(`:text("y")`, e => e.outerHTML)).toBe('<div>yo</div>');
|
||||
expect(await page.$(`:text-is("y")`)).toBe(null);
|
||||
expect(await page.$(`:text-is("Y")`)).toBe(null);
|
||||
expect(await page.$eval(`:text("hello world")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
|
||||
expect(await page.$eval(`:text-is("hello world")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
|
||||
expect(await page.$eval(`:text-is("HELLO world")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
|
||||
expect(await page.$eval(`:text("lo wo")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
|
||||
expect(await page.$(`:text-is("lo wo")`)).toBe(null);
|
||||
expect(await page.$eval(`:text-matches("^[ay]+$")`, e => e.outerHTML)).toBe('<div>ya</div>');
|
||||
@ -145,11 +145,11 @@ it('should work across nodes', async ({page}) => {
|
||||
expect(await page.$(`text=hello world`)).toBe(null);
|
||||
|
||||
expect(await page.$eval(`:text-is("Hello, world!")`, e => e.id)).toBe('target1');
|
||||
expect(await page.$(`:text-is("Hello")`)).toBe(null);
|
||||
expect(await page.$eval(`:text-is("Hello")`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`:text-is("world")`, e => e.id)).toBe('target2');
|
||||
expect(await page.$$eval(`:text-is("world")`, els => els.length)).toBe(1);
|
||||
expect(await page.$eval(`text="Hello, world!"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$(`text="Hello"`)).toBe(null);
|
||||
expect(await page.$eval(`text="Hello"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="world"`, e => e.id)).toBe('target2');
|
||||
expect(await page.$$eval(`text="world"`, els => els.length)).toBe(1);
|
||||
|
||||
@ -162,6 +162,20 @@ it('should work across nodes', async ({page}) => {
|
||||
expect(await page.$$eval(`text=/world/`, els => els.length)).toBe(1);
|
||||
});
|
||||
|
||||
it('should work with text nodes in quoted mode', async ({page}) => {
|
||||
await page.setContent(`<div id=target1>Hello<span id=target2>wo rld </span> Hi again </div>`);
|
||||
expect(await page.$eval(`text="Hello"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="Hi again"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="wo rld"`, e => e.id)).toBe('target2');
|
||||
expect(await page.$eval(`text="Hellowo rld Hi again"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="Hellowo"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="Hellowo rld"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="wo rld Hi ag"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$eval(`text="again"`, e => e.id)).toBe('target1');
|
||||
expect(await page.$(`text="hi again"`)).toBe(null);
|
||||
expect(await page.$eval(`text=hi again`, e => e.id)).toBe('target1');
|
||||
});
|
||||
|
||||
it('should clear caches', async ({page}) => {
|
||||
await page.setContent(`<div id=target1>text</div><div id=target2>text</div>`);
|
||||
const div = await page.$('#target1');
|
||||
@ -277,10 +291,10 @@ it('should be case sensitive if quotes are specified', async ({page}) => {
|
||||
expect(await page.$(`text="yA"`)).toBe(null);
|
||||
});
|
||||
|
||||
it('should search for a substring without quotes', async ({page}) => {
|
||||
it('should search for a substring', async ({page}) => {
|
||||
await page.setContent(`<div>textwithsubstring</div>`);
|
||||
expect(await page.$eval(`text=with`, e => e.outerHTML)).toBe('<div>textwithsubstring</div>');
|
||||
expect(await page.$(`text="with"`)).toBe(null);
|
||||
expect(await page.$eval(`text="with"`, e => e.outerHTML)).toBe('<div>textwithsubstring</div>');
|
||||
});
|
||||
|
||||
it('should skip head, script and style', async ({page}) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user