Skip to content

Commit 92bbdbe

Browse files
aslushnikovdgozman
andauthored
cherry-pick(release-1.9): make quoted selector match by text nodes (#5603) (#5608)
Cherry-pick 0102e08 Fixes #5583 Co-authored-by: Dmitry Gozman <[email protected]>
1 parent 1196ac6 commit 92bbdbe

File tree

4 files changed

+31
-15
lines changed

4 files changed

+31
-15
lines changed

docs/src/selectors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Text selector has a few variations:
148148
page.click("text=Log in")
149149
```
150150

151-
- `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>`.
151+
- `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>`.
152152

153153
Quoted body follows the usual escaping rules, e.g. use `\"` to escape double quote in a double-quoted string: `text="foo\"bar"`.
154154

src/server/injected/injectedScript.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -780,8 +780,8 @@ function createTextMatcher(selector: string): { matcher: Matcher, strict: boolea
780780
const matcher = (text: string) => {
781781
text = text.trim().replace(/\s+/g, ' ');
782782
if (!strict)
783-
return text.toLowerCase().includes(selector);
784-
return text === selector;
783+
text = text.toLowerCase();
784+
return text.includes(selector);
785785
};
786786
return { matcher, strict };
787787
}

src/server/injected/selectorEvaluator.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,15 @@ const hasTextEngine: SelectorEngine = {
462462
},
463463
};
464464

465-
function textMatcher(text: string, substring: boolean): (s: string) => boolean {
465+
function textMatcher(text: string, caseInsensitive: boolean): (s: string) => boolean {
466466
text = text.trim().replace(/\s+/g, ' ');
467-
text = text.toLowerCase();
467+
if (caseInsensitive)
468+
text = text.toLowerCase();
468469
return (s: string) => {
469470
s = s.trim().replace(/\s+/g, ' ');
470-
s = s.toLowerCase();
471-
return substring ? s.includes(text) : s === text;
471+
if (caseInsensitive)
472+
s = s.toLowerCase();
473+
return s.includes(text);
472474
};
473475
}
474476

test/selectors-text.spec.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,19 @@ it('should work', async ({page}) => {
103103
expect((await page.$$(`text="Sign in"`)).length).toBe(1);
104104
expect(await page.$eval(`text=lo wo`, e => e.outerHTML)).toBe('<span>Hello\n \nworld</span>');
105105
expect(await page.$eval(`text="Hello world"`, e => e.outerHTML)).toBe('<span>Hello\n \nworld</span>');
106-
expect(await page.$(`text="lo wo"`)).toBe(null);
106+
expect(await page.$eval(`text="lo wo"`, e => e.outerHTML)).toBe('<span>Hello\n \nworld</span>');
107107
expect((await page.$$(`text=lo \nwo`)).length).toBe(1);
108-
expect((await page.$$(`text="lo wo"`)).length).toBe(0);
108+
expect((await page.$$(`text="lo \nwo"`)).length).toBe(1);
109109
});
110110

111111
it('should work with :text', async ({page}) => {
112112
await page.setContent(`<div>yo</div><div>ya</div><div>\nHELLO \n world </div>`);
113113
expect(await page.$eval(`:text("ya")`, e => e.outerHTML)).toBe('<div>ya</div>');
114114
expect(await page.$eval(`:text-is("ya")`, e => e.outerHTML)).toBe('<div>ya</div>');
115115
expect(await page.$eval(`:text("y")`, e => e.outerHTML)).toBe('<div>yo</div>');
116-
expect(await page.$(`:text-is("y")`)).toBe(null);
116+
expect(await page.$(`:text-is("Y")`)).toBe(null);
117117
expect(await page.$eval(`:text("hello world")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
118-
expect(await page.$eval(`:text-is("hello world")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
118+
expect(await page.$eval(`:text-is("HELLO world")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
119119
expect(await page.$eval(`:text("lo wo")`, e => e.outerHTML)).toBe('<div>\nHELLO \n world </div>');
120120
expect(await page.$(`:text-is("lo wo")`)).toBe(null);
121121
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}) => {
145145
expect(await page.$(`text=hello world`)).toBe(null);
146146

147147
expect(await page.$eval(`:text-is("Hello, world!")`, e => e.id)).toBe('target1');
148-
expect(await page.$(`:text-is("Hello")`)).toBe(null);
148+
expect(await page.$eval(`:text-is("Hello")`, e => e.id)).toBe('target1');
149149
expect(await page.$eval(`:text-is("world")`, e => e.id)).toBe('target2');
150150
expect(await page.$$eval(`:text-is("world")`, els => els.length)).toBe(1);
151151
expect(await page.$eval(`text="Hello, world!"`, e => e.id)).toBe('target1');
152-
expect(await page.$(`text="Hello"`)).toBe(null);
152+
expect(await page.$eval(`text="Hello"`, e => e.id)).toBe('target1');
153153
expect(await page.$eval(`text="world"`, e => e.id)).toBe('target2');
154154
expect(await page.$$eval(`text="world"`, els => els.length)).toBe(1);
155155

@@ -162,6 +162,20 @@ it('should work across nodes', async ({page}) => {
162162
expect(await page.$$eval(`text=/world/`, els => els.length)).toBe(1);
163163
});
164164

165+
it('should work with text nodes in quoted mode', async ({page}) => {
166+
await page.setContent(`<div id=target1>Hello<span id=target2>wo rld </span> Hi again </div>`);
167+
expect(await page.$eval(`text="Hello"`, e => e.id)).toBe('target1');
168+
expect(await page.$eval(`text="Hi again"`, e => e.id)).toBe('target1');
169+
expect(await page.$eval(`text="wo rld"`, e => e.id)).toBe('target2');
170+
expect(await page.$eval(`text="Hellowo rld Hi again"`, e => e.id)).toBe('target1');
171+
expect(await page.$eval(`text="Hellowo"`, e => e.id)).toBe('target1');
172+
expect(await page.$eval(`text="Hellowo rld"`, e => e.id)).toBe('target1');
173+
expect(await page.$eval(`text="wo rld Hi ag"`, e => e.id)).toBe('target1');
174+
expect(await page.$eval(`text="again"`, e => e.id)).toBe('target1');
175+
expect(await page.$(`text="hi again"`)).toBe(null);
176+
expect(await page.$eval(`text=hi again`, e => e.id)).toBe('target1');
177+
});
178+
165179
it('should clear caches', async ({page}) => {
166180
await page.setContent(`<div id=target1>text</div><div id=target2>text</div>`);
167181
const div = await page.$('#target1');
@@ -277,10 +291,10 @@ it('should be case sensitive if quotes are specified', async ({page}) => {
277291
expect(await page.$(`text="yA"`)).toBe(null);
278292
});
279293

280-
it('should search for a substring without quotes', async ({page}) => {
294+
it('should search for a substring', async ({page}) => {
281295
await page.setContent(`<div>textwithsubstring</div>`);
282296
expect(await page.$eval(`text=with`, e => e.outerHTML)).toBe('<div>textwithsubstring</div>');
283-
expect(await page.$(`text="with"`)).toBe(null);
297+
expect(await page.$eval(`text="with"`, e => e.outerHTML)).toBe('<div>textwithsubstring</div>');
284298
});
285299

286300
it('should skip head, script and style', async ({page}) => {

0 commit comments

Comments
 (0)