mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(locators): implement last,nth (#7870)
This commit is contained in:
parent
b9aad5eb86
commit
cc43f9339f
@ -534,6 +534,11 @@ Returns whether the element is [visible](./actionability.md#visible).
|
|||||||
|
|
||||||
### option: Locator.isVisible.timeout = %%-input-timeout-%%
|
### option: Locator.isVisible.timeout = %%-input-timeout-%%
|
||||||
|
|
||||||
|
## method: Locator.last
|
||||||
|
- returns: <[Locator]>
|
||||||
|
|
||||||
|
Returns locator to the last matching element.
|
||||||
|
|
||||||
## method: Locator.locator
|
## method: Locator.locator
|
||||||
- returns: <[Locator]>
|
- returns: <[Locator]>
|
||||||
|
|
||||||
@ -542,6 +547,14 @@ The method finds an element matching the specified selector in the `Locator`'s s
|
|||||||
|
|
||||||
### param: Locator.locator.selector = %%-find-selector-%%
|
### param: Locator.locator.selector = %%-find-selector-%%
|
||||||
|
|
||||||
|
## method: Locator.nth
|
||||||
|
- returns: <[Locator]>
|
||||||
|
|
||||||
|
Returns locator to the n-th matching element.
|
||||||
|
|
||||||
|
### param: Locator.nth.index
|
||||||
|
- `index` <[int]>
|
||||||
|
|
||||||
## async method: Locator.press
|
## async method: Locator.press
|
||||||
|
|
||||||
Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
|
Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
|
||||||
|
|||||||
@ -99,7 +99,15 @@ export class Locator implements api.Locator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
first(): Locator {
|
first(): Locator {
|
||||||
return new Locator(this._frame, this._selector + ' >> _first=true');
|
return new Locator(this._frame, this._selector + ' >> _nth=first');
|
||||||
|
}
|
||||||
|
|
||||||
|
last(): Locator {
|
||||||
|
return new Locator(this._frame, this._selector + ` >> _nth=last`);
|
||||||
|
}
|
||||||
|
|
||||||
|
nth(index: number): Locator {
|
||||||
|
return new Locator(this._frame, this._selector + ` >> _nth=${index}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async focus(options?: TimeoutOptions): Promise<void> {
|
async focus(options?: TimeoutOptions): Promise<void> {
|
||||||
|
|||||||
@ -75,6 +75,7 @@ export class InjectedScript {
|
|||||||
this._engines.set('css', this._createCSSEngine());
|
this._engines.set('css', this._createCSSEngine());
|
||||||
this._engines.set('_first', { queryAll: () => [] });
|
this._engines.set('_first', { queryAll: () => [] });
|
||||||
this._engines.set('_visible', { queryAll: () => [] });
|
this._engines.set('_visible', { queryAll: () => [] });
|
||||||
|
this._engines.set('_nth', { queryAll: () => [] });
|
||||||
|
|
||||||
for (const { name, engine } of customEngines)
|
for (const { name, engine } of customEngines)
|
||||||
this._engines.set(name, engine);
|
this._engines.set(name, engine);
|
||||||
@ -110,11 +111,30 @@ export class InjectedScript {
|
|||||||
if (index === selector.parts.length)
|
if (index === selector.parts.length)
|
||||||
return roots;
|
return roots;
|
||||||
|
|
||||||
if (selector.parts[index].name === '_first')
|
const part = selector.parts[index];
|
||||||
return roots.slice(0, 1);
|
if (part.name === '_nth') {
|
||||||
|
let filtered: ElementMatch[] = [];
|
||||||
|
if (part.body === 'first') {
|
||||||
|
filtered = roots.slice(0, 1);
|
||||||
|
} else if (part.body === 'last') {
|
||||||
|
if (roots.length)
|
||||||
|
filtered = roots.slice(roots.length - 1);
|
||||||
|
} else {
|
||||||
|
if (typeof selector.capture === 'number')
|
||||||
|
throw new Error(`Can't query n-th element in a request with the capture.`);
|
||||||
|
const nth = +part.body;
|
||||||
|
const set = new Set<Element>();
|
||||||
|
for (const root of roots) {
|
||||||
|
set.add(root.element);
|
||||||
|
if (nth + 1 === set.size)
|
||||||
|
filtered = [root];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._querySelectorRecursively(filtered, selector, index + 1, queryCache);
|
||||||
|
}
|
||||||
|
|
||||||
if (selector.parts[index].name === '_visible') {
|
if (part.name === '_visible') {
|
||||||
const visible = Boolean(selector.parts[index].body);
|
const visible = Boolean(part.body);
|
||||||
return roots.filter(match => visible === isVisible(match.element));
|
return roots.filter(match => visible === isVisible(match.element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export class Selectors {
|
|||||||
'data-testid', 'data-testid:light',
|
'data-testid', 'data-testid:light',
|
||||||
'data-test-id', 'data-test-id:light',
|
'data-test-id', 'data-test-id:light',
|
||||||
'data-test', 'data-test:light',
|
'data-test', 'data-test:light',
|
||||||
'_visible', '_first'
|
'_visible', '_nth'
|
||||||
]);
|
]);
|
||||||
this._engines = new Map();
|
this._engines = new Map();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { test as it, expect } from './pageTest';
|
import { test as it, expect } from './pageTest';
|
||||||
|
|
||||||
it('should respect first()', async ({page}) => {
|
it('should respect first() and last()', async ({page}) => {
|
||||||
await page.setContent(`
|
await page.setContent(`
|
||||||
<section>
|
<section>
|
||||||
<div><p>A</p></div>
|
<div><p>A</p></div>
|
||||||
@ -27,4 +27,23 @@ it('should respect first()', async ({page}) => {
|
|||||||
expect(await page.locator('div >> p').count()).toBe(6);
|
expect(await page.locator('div >> p').count()).toBe(6);
|
||||||
expect(await page.locator('div').locator('p').count()).toBe(6);
|
expect(await page.locator('div').locator('p').count()).toBe(6);
|
||||||
expect(await page.locator('div').first().locator('p').count()).toBe(1);
|
expect(await page.locator('div').first().locator('p').count()).toBe(1);
|
||||||
|
expect(await page.locator('div').last().locator('p').count()).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respect nth()', async ({page}) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<section>
|
||||||
|
<div><p>A</p></div>
|
||||||
|
<div><p>A</p><p>A</p></div>
|
||||||
|
<div><p>A</p><p>A</p><p>A</p></div>
|
||||||
|
</section>`);
|
||||||
|
expect(await page.locator('div >> p').nth(0).count()).toBe(1);
|
||||||
|
expect(await page.locator('div').nth(1).locator('p').count()).toBe(2);
|
||||||
|
expect(await page.locator('div').nth(2).locator('p').count()).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw on capture w/ nth()', async ({page}) => {
|
||||||
|
await page.setContent(`<section><div><p>A</p></div></section>`);
|
||||||
|
const e = await page.locator('*css=div >> p').nth(0).click().catch(e => e);
|
||||||
|
expect(e.message).toContain(`Can't query n-th element`);
|
||||||
});
|
});
|
||||||
|
|||||||
11
types/types.d.ts
vendored
11
types/types.d.ts
vendored
@ -7592,6 +7592,11 @@ export interface Locator {
|
|||||||
timeout?: number;
|
timeout?: number;
|
||||||
}): Promise<boolean>;
|
}): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns locator to the last matching element.
|
||||||
|
*/
|
||||||
|
last(): Locator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method finds an element matching the specified selector in the `Locator`'s subtree. See
|
* The method finds an element matching the specified selector in the `Locator`'s subtree. See
|
||||||
* [Working with selectors](https://playwright.dev/docs/selectors) for more details.
|
* [Working with selectors](https://playwright.dev/docs/selectors) for more details.
|
||||||
@ -7599,6 +7604,12 @@ export interface Locator {
|
|||||||
*/
|
*/
|
||||||
locator(selector: string): Locator;
|
locator(selector: string): Locator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns locator to the n-th matching element.
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
nth(index: number): Locator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Focuses the element, and then uses [keyboard.down(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-down)
|
* Focuses the element, and then uses [keyboard.down(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-down)
|
||||||
* and [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up).
|
* and [keyboard.up(key)](https://playwright.dev/docs/api/class-keyboard#keyboard-up).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user