Skip to content

Commit c8e9b05

Browse files
authored
feat(selectors): disable proximity selectors (#4659)
These are not ready for prime time yet.
1 parent 84ff20f commit c8e9b05

File tree

6 files changed

+10
-72
lines changed

6 files changed

+10
-72
lines changed

docs-src/api-footer.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ Playwright also supports the following CSS extensions:
8888
* `:text("string")` - Matches elements that contain specific text node. Learn more about [text selector](./selectors.md#css-extension-text).
8989
* `:visible` - Matches only visible elements. Learn more about [visible selector](./selectors.md#css-extension-visible).
9090
* `:light(selector)` - Matches in the light DOM only as opposite to piercing open shadow roots. Learn more about [shadow piercing](./selectors.md#shadow-piercing).
91+
<!--
9192
* `:right-of(selector)`, `:left-of(selector)`, `:above(selector)`, `:below(selector)`, `:near(selector)`, `:within(selector)` - Match elements based on their relative position to another element. Learn more about [proximity selectors](./selectors.md#css-extension-proximity).
93+
-->
9294

9395
For convenience, selectors in the wrong format are heuristically converted to the right format:
9496
- selector starting with `//` or `..` is assumed to be `xpath=selector`;

docs/api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5460,7 +5460,9 @@ Playwright also supports the following CSS extensions:
54605460
* `:text("string")` - Matches elements that contain specific text node. Learn more about [text selector](./selectors.md#css-extension-text).
54615461
* `:visible` - Matches only visible elements. Learn more about [visible selector](./selectors.md#css-extension-visible).
54625462
* `:light(selector)` - Matches in the light DOM only as opposite to piercing open shadow roots. Learn more about [shadow piercing](./selectors.md#shadow-piercing).
5463+
<!--
54635464
* `:right-of(selector)`, `:left-of(selector)`, `:above(selector)`, `:below(selector)`, `:near(selector)`, `:within(selector)` - Match elements based on their relative position to another element. Learn more about [proximity selectors](./selectors.md#css-extension-proximity).
5465+
-->
54645466

54655467
For convenience, selectors in the wrong format are heuristically converted to the right format:
54665468
- selector starting with `//` or `..` is assumed to be `xpath=selector`;

docs/selectors.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ await page.click('button:text("Sign in")');
213213
await page.click(':light(.article > .header)');
214214
```
215215

216+
<!--
216217
#### CSS extension: proximity
217218
218219
Playwright provides a few proximity selectors based on the page layout. These can be combined with regular CSS for better results, for example `input:right-of(:text("Password"))` matches an input field that is to the right of text "Password".
@@ -233,6 +234,7 @@ await page.fill('input:right-of(:text("Username"))');
233234
// Click a button near the promo card.
234235
await page.click('button:near(.promo-card)');
235236
```
237+
-->
236238

237239
### xpath
238240

src/server/common/selectorParser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function selectorsV2Enabled() {
3535
}
3636

3737
export function selectorsV2EngineNames() {
38-
return ['not', 'is', 'where', 'has', 'scope', 'light', 'visible', 'text-matches', 'text-is', 'above', 'below', 'right-of', 'left-of', 'near', 'within'];
38+
return ['not', 'is', 'where', 'has', 'scope', 'light', 'visible', 'text-matches', 'text-is'];
3939
}
4040

4141
export function parseSelector(selector: string, customNames: Set<string>): ParsedSelector {

src/server/injected/selectorEvaluator.ts

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@ export class SelectorEvaluatorImpl implements SelectorEvaluator {
6060
this._engines.set('xpath', xpathEngine);
6161
for (const attr of ['id', 'data-testid', 'data-test-id', 'data-test'])
6262
this._engines.set(attr, createAttributeEngine(attr));
63-
this._engines.set('right-of', createProximityEngine('right-of', boxRightOf));
64-
this._engines.set('left-of', createProximityEngine('left-of', boxLeftOf));
65-
this._engines.set('above', createProximityEngine('above', boxAbove));
66-
this._engines.set('below', createProximityEngine('below', boxBelow));
67-
this._engines.set('near', createProximityEngine('near', boxNear));
68-
this._engines.set('within', createProximityEngine('within', boxWithin));
6963
}
7064

7165
// This is the only function we should use for querying, because it does
@@ -448,70 +442,6 @@ function createAttributeEngine(attr: string): SelectorEngine {
448442
};
449443
}
450444

451-
function areCloseRanges(from1: number, to1: number, from2: number, to2: number, threshold: number) {
452-
return to1 >= from2 - threshold && to2 >= from1 - threshold;
453-
}
454-
455-
function boxSize(box: DOMRect) {
456-
return Math.sqrt(box.width * box.height);
457-
}
458-
459-
function boxesProximityThreshold(box1: DOMRect, box2: DOMRect) {
460-
return (boxSize(box1) + boxSize(box2)) / 2;
461-
}
462-
463-
function boxRightOf(box1: DOMRect, box2: DOMRect): boolean {
464-
// To the right, but not too far, and vertically intersects.
465-
const distance = box1.left - box2.right;
466-
return distance >= 0 && distance <= boxesProximityThreshold(box1, box2) &&
467-
areCloseRanges(box1.top, box1.bottom, box2.top, box2.bottom, 0);
468-
}
469-
470-
function boxLeftOf(box1: DOMRect, box2: DOMRect): boolean {
471-
// To the left, but not too far, and vertically intersects.
472-
const distance = box2.left - box1.right;
473-
return distance >= 0 && distance <= boxesProximityThreshold(box1, box2) &&
474-
areCloseRanges(box1.top, box1.bottom, box2.top, box2.bottom, 0);
475-
}
476-
477-
function boxAbove(box1: DOMRect, box2: DOMRect): boolean {
478-
// Above, but not too far, and horizontally intersects.
479-
const distance = box2.top - box1.bottom;
480-
return distance >= 0 && distance <= boxesProximityThreshold(box1, box2) &&
481-
areCloseRanges(box1.left, box1.right, box2.left, box2.right, 0);
482-
}
483-
484-
function boxBelow(box1: DOMRect, box2: DOMRect): boolean {
485-
// Below, but not too far, and horizontally intersects.
486-
const distance = box1.top - box2.bottom;
487-
return distance >= 0 && distance <= boxesProximityThreshold(box1, box2) &&
488-
areCloseRanges(box1.left, box1.right, box2.left, box2.right, 0);
489-
}
490-
491-
function boxWithin(box1: DOMRect, box2: DOMRect): boolean {
492-
return box1.left >= box2.left && box1.right <= box2.right && box1.top >= box2.top && box1.bottom <= box2.bottom;
493-
}
494-
495-
function boxNear(box1: DOMRect, box2: DOMRect): boolean {
496-
const intersects = !(box1.left >= box2.right || box2.left >= box1.right || box1.top >= box2.bottom || box2.top >= box1.bottom);
497-
if (intersects)
498-
return false;
499-
const threshold = boxesProximityThreshold(box1, box2);
500-
return areCloseRanges(box1.left, box1.right, box2.left, box2.right, threshold) &&
501-
areCloseRanges(box1.top, box1.bottom, box2.top, box2.bottom, threshold);
502-
}
503-
504-
function createProximityEngine(name: string, predicate: (box1: DOMRect, box2: DOMRect) => boolean): SelectorEngine {
505-
return {
506-
matches(element: Element, args: (string | number | Selector)[], context: QueryContext, evaluator: SelectorEvaluator): boolean {
507-
if (!args.length)
508-
throw new Error(`"${name}" engine expects a selector list`);
509-
const box = element.getBoundingClientRect();
510-
return evaluator.query(context, args).some(e => e !== element && predicate(box, e.getBoundingClientRect()));
511-
},
512-
};
513-
}
514-
515445
export function parentElementOrShadowHost(element: Element): Element | undefined {
516446
if (element.parentElement)
517447
return element.parentElement;

test/selectors-misc.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ it('should work with :visible', async ({page}) => {
5252
expect(await page.$eval('div:visible', div => div.id)).toBe('target2');
5353
});
5454

55-
it('should work with proximity selectors', async ({page}) => {
55+
it('should work with proximity selectors', test => {
56+
test.skip('Not ready yet');
57+
}, async ({page}) => {
5658
if (!selectorsV2Enabled())
5759
return; // Selectors v1 do not support this.
5860

0 commit comments

Comments
 (0)