diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 69a52e937f..2de827f540 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -95,6 +95,16 @@ await locator.HoverAsync(); await locator.ClickAsync(); ``` +## async method: Locator.allInnerTexts +- returns: <[Array]<[string]>> + +Returns an array of `node.innerText` values for all matching nodes. + +## async method: Locator.allTextContents +- returns: <[Array]<[string]>> + +Returns an array of `node.textContent` values for all matching nodes. + ## async method: Locator.boundingBox - returns: <[null]|[Object]> - `x` <[float]> the x coordinate of the element in pixels. diff --git a/src/client/locator.ts b/src/client/locator.ts index 1d8c7e32c9..3ec73d4b81 100644 --- a/src/client/locator.ts +++ b/src/client/locator.ts @@ -200,6 +200,14 @@ export class Locator implements api.Locator { return this._frame.uncheck(this._selector, { strict: true, ...options }); } + async allInnerTexts(): Promise { + return this._frame.$$eval(this._selector, ee => ee.map(e => (e as HTMLElement).innerText)); + } + + async allTextContents(): Promise { + return this._frame.$$eval(this._selector, ee => ee.map(e => e.textContent || '')); + } + [(util.inspect as any).custom]() { return this.toString(); } diff --git a/tests/page/locator-convenience.spec.ts b/tests/page/locator-convenience.spec.ts index 6ef5d7e64c..db96de60b9 100644 --- a/tests/page/locator-convenience.spec.ts +++ b/tests/page/locator-convenience.spec.ts @@ -47,25 +47,25 @@ it('inputValue should work', async ({ page, server }) => { await page.fill('#input', 'input value'); expect(await page.inputValue('#input')).toBe('input value'); - const handle = page.locator('#input'); - expect(await handle.inputValue()).toBe('input value'); + const locator = page.locator('#input'); + expect(await locator.inputValue()).toBe('input value'); expect(await page.inputValue('#inner').catch(e => e.message)).toContain('Node is not an HTMLInputElement or HTMLTextAreaElement'); - const handle2 = page.locator('#inner'); - expect(await handle2.inputValue().catch(e => e.message)).toContain('Node is not an HTMLInputElement or HTMLTextAreaElement'); + const locator2 = page.locator('#inner'); + expect(await locator2.inputValue().catch(e => e.message)).toContain('Node is not an HTMLInputElement or HTMLTextAreaElement'); }); it('innerHTML should work', async ({ page, server }) => { await page.goto(`${server.PREFIX}/dom.html`); - const handle = page.locator('#outer'); - expect(await handle.innerHTML()).toBe('
Text,\nmore text
'); + const locator = page.locator('#outer'); + expect(await locator.innerHTML()).toBe('
Text,\nmore text
'); expect(await page.innerHTML('#outer')).toBe('
Text,\nmore text
'); }); it('innerText should work', async ({ page, server }) => { await page.goto(`${server.PREFIX}/dom.html`); - const handle = page.locator('#inner'); - expect(await handle.innerText()).toBe('Text, more text'); + const locator = page.locator('#inner'); + expect(await locator.innerText()).toBe('Text, more text'); expect(await page.innerText('#inner')).toBe('Text, more text'); }); @@ -73,15 +73,15 @@ it('innerText should throw', async ({ page, server }) => { await page.setContent(`text`); const error1 = await page.innerText('svg').catch(e => e); expect(error1.message).toContain('Not an HTMLElement'); - const handle = page.locator('svg'); - const error2 = await handle.innerText().catch(e => e); + const locator = page.locator('svg'); + const error2 = await locator.innerText().catch(e => e); expect(error2.message).toContain('Not an HTMLElement'); }); it('textContent should work', async ({ page, server }) => { await page.goto(`${server.PREFIX}/dom.html`); - const handle = page.locator('#inner'); - expect(await handle.textContent()).toBe('Text,\nmore text'); + const locator = page.locator('#inner'); + expect(await locator.textContent()).toBe('Text,\nmore text'); expect(await page.textContent('#inner')).toBe('Text,\nmore text'); }); @@ -196,3 +196,13 @@ it('isChecked should work', async ({page}) => { const error = await page.isChecked('div').catch(e => e); expect(error.message).toContain('Not a checkbox or radio button'); }); + +it('addTextContents should work', async ({page}) => { + await page.setContent(`
A
B
C
`); + expect(await page.locator('div').allTextContents()).toEqual(['A', 'B', 'C']); +}); + +it('addInnerTexts should work', async ({page}) => { + await page.setContent(`
A
B
C
`); + expect(await page.locator('div').allInnerTexts()).toEqual(['A', 'B', 'C']); +}); diff --git a/types/types.d.ts b/types/types.d.ts index ddb421f35f..a575639c5a 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -7047,6 +7047,16 @@ export interface Locator { elementHandle(options?: { timeout?: number; }): Promise>; + /** + * Returns an array of `node.innerText` values for all matching nodes. + */ + allInnerTexts(): Promise>; + + /** + * Returns an array of `node.textContent` values for all matching nodes. + */ + allTextContents(): Promise>; + /** * This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is * calculated relative to the main frame viewport - which is usually the same as the browser window.