Skip to content

Commit 29568e8

Browse files
committed
fix(selectors): text engine after capture matches scope (#4749)
1 parent 5d69549 commit 29568e8

File tree

4 files changed

+16
-5
lines changed

4 files changed

+16
-5
lines changed

src/server/common/selectorParser.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function parseSelector(selector: string, customNames: Set<string>): Parse
5454
};
5555
}
5656

57-
const chain = (from: number, to: number): CSSComplexSelector => {
57+
const chain = (from: number, to: number, turnFirstTextIntoScope: boolean): CSSComplexSelector => {
5858
const result: CSSComplexSelector = { simples: [] };
5959
for (const part of v1.parts.slice(from, to)) {
6060
let name = part.name;
@@ -76,6 +76,8 @@ export function parseSelector(selector: string, customNames: Set<string>): Parse
7676
}
7777
} else if (name === 'text') {
7878
let simple = textSelectorToSimple(part.body);
79+
if (turnFirstTextIntoScope)
80+
simple.functions.push({ name: 'is', args: [ simpleToComplex(callWith('scope', [])), simpleToComplex({ css: '*', functions: [] }) ]});
7981
if (result.simples.length)
8082
result.simples[result.simples.length - 1].combinator = '>=';
8183
if (wrapInLight)
@@ -87,14 +89,16 @@ export function parseSelector(selector: string, customNames: Set<string>): Parse
8789
simple = callWith('light', [simpleToComplex(simple)]);
8890
result.simples.push({ selector: simple, combinator: '' });
8991
}
92+
if (name !== 'text')
93+
turnFirstTextIntoScope = false;
9094
}
9195
return result;
9296
};
9397

9498
const capture = v1.capture === undefined ? v1.parts.length - 1 : v1.capture;
95-
const result = chain(0, capture + 1);
99+
const result = chain(0, capture + 1, false);
96100
if (capture + 1 < v1.parts.length) {
97-
const has = chain(capture + 1, v1.parts.length);
101+
const has = chain(capture + 1, v1.parts.length, true);
98102
const last = result.simples[result.simples.length - 1];
99103
last.selector.functions.push({ name: 'has', args: [has] });
100104
}

src/server/injected/selectorEvaluator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ export class SelectorEvaluatorImpl implements SelectorEvaluator {
120120

121121
private _matchesSimple(element: Element, simple: CSSSimpleSelector, context: QueryContext): boolean {
122122
return this._cached<boolean>(this._cacheMatchesSimple, element, [simple, context], () => {
123-
const isScopeClause = simple.functions.some(f => f.name === 'scope');
124-
if (!isScopeClause && element === context.scope)
123+
const isPossiblyScopeClause = simple.functions.some(f => f.name === 'scope' || f.name === 'is');
124+
if (!isPossiblyScopeClause && element === context.scope)
125125
return false;
126126
if (simple.css && !this._matchesCSS(element, simple.css))
127127
return false;

test/selectors-css.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ it('should work with :is', async ({page, server}) => {
348348
expect(await page.$$eval(`css=:is(div, span)`, els => els.length)).toBe(7);
349349
expect(await page.$$eval(`css=section:is(section) div:is(section div)`, els => els.length)).toBe(3);
350350
expect(await page.$$eval(`css=:is(div, span) > *`, els => els.length)).toBe(6);
351+
expect(await page.$$eval(`css=#root1:has(:is(#root1))`, els => els.length)).toBe(0);
352+
expect(await page.$$eval(`css=#root1:has(:is(:scope, #root1))`, els => els.length)).toBe(1);
351353
});
352354

353355
it('should work with :has', async ({page, server}) => {

test/selectors-text.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,8 @@ it('should match root after >>', async ({page, server}) => {
227227
const element2 = await page.$('text=test >> text=test');
228228
expect(element2).toBeTruthy();
229229
});
230+
231+
it('should match root after >> with *', async ({ page }) => {
232+
await page.setContent(`<button> hello world </button> <button> hellow <span> world </span> </button>`);
233+
expect(await page.$$eval('*css=button >> text=hello >> text=world', els => els.length)).toBe(2);
234+
});

0 commit comments

Comments
 (0)