feat(expect): add text and true matchers (#7873)

This commit is contained in:
Pavel Feldman 2021-07-28 12:07:11 -07:00 committed by GitHub
parent 74cd7584ac
commit 49e9f8c15e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 607 additions and 65 deletions

View File

@ -22,17 +22,14 @@ import { monotonicTime } from '../utils/utils';
import { ElementHandle } from './elementHandle';
import { Frame } from './frame';
import { FilePayload, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
import { TimeoutSettings } from '../utils/timeoutSettings';
export class Locator implements api.Locator {
private _frame: Frame;
private _selector: string;
private _visibleSelector: string;
private _timeoutSettings: TimeoutSettings;
constructor(frame: Frame, selector: string) {
this._frame = frame;
this._timeoutSettings = this._frame.page()._timeoutSettings;
this._selector = selector;
this._visibleSelector = selector + ' >> _visible=true';
}
@ -158,11 +155,11 @@ export class Locator implements api.Locator {
}
async isHidden(options?: TimeoutOptions): Promise<boolean> {
return this._frame.isHidden(this._visibleSelector, { strict: true, ...options });
return this._frame.isHidden(this._selector, { strict: true, ...options });
}
async isVisible(options?: TimeoutOptions): Promise<boolean> {
return this._frame.isVisible(this._visibleSelector, { strict: true, ...options });
return this._frame.isVisible(this._selector, { strict: true, ...options });
}
async press(key: string, options: channels.ElementHandlePressOptions = {}): Promise<void> {

View File

@ -14,11 +14,29 @@
* limitations under the License.
*/
import type { Expect } from './types';
import expectLibrary from 'expect';
import { toBeChecked, toBeDisabled, toBeEditable, toBeEmpty, toBeEnabled, toBeFocused, toBeHidden, toBeVisible } from './matchers/toBeTruthy';
import { toMatchSnapshot } from './matchers/toMatchSnapshot';
import { toMatchText, toHaveText } from './matchers/toMatchText';
import { toContainText, toHaveAttr, toHaveCSS, toHaveData, toHaveId, toHaveText, toHaveValue } from './matchers/toMatchText';
import type { Expect } from './types';
export const expect: Expect = expectLibrary as any;
expectLibrary.setState({ expand: false });
expectLibrary.extend({ toMatchSnapshot, toMatchText, toHaveText });
expectLibrary.extend({
toBeChecked,
toBeDisabled,
toBeEditable,
toBeEmpty,
toBeEnabled,
toBeFocused,
toBeHidden,
toBeVisible,
toContainText,
toHaveAttr,
toHaveCSS,
toHaveData,
toHaveId,
toHaveText,
toHaveValue,
toMatchSnapshot,
});

View File

@ -0,0 +1,151 @@
/**
* Copyright Microsoft Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
matcherHint,
MatcherHintOptions,
printReceived
} from 'jest-matcher-utils';
import { Locator } from '../../..';
import { currentTestInfo } from '../globals';
import type { Expect } from '../types';
import { monotonicTime, pollUntilDeadline } from '../util';
async function toBeTruthyImpl(
this: ReturnType<Expect['getState']>,
matcherName: string,
query: (timeout: number) => Promise<boolean>,
options: { timeout?: number } = {},
) {
const testInfo = currentTestInfo();
if (!testInfo)
throw new Error(`toMatchSnapshot() must be called during the test`);
const matcherOptions: MatcherHintOptions = {
isNot: this.isNot,
promise: this.promise,
};
let received: boolean;
let pass = false;
const timeout = options.timeout === 0 ? 0 : options.timeout || testInfo.timeout;
const deadline = timeout ? monotonicTime() + timeout : 0;
try {
await pollUntilDeadline(async () => {
const remainingTime = deadline ? deadline - monotonicTime() : 0;
received = await query(remainingTime);
pass = !!received;
return pass === !matcherOptions.isNot;
}, deadline, 100);
} catch (e) {
pass = false;
}
const message = () =>
matcherHint(matcherName, undefined, '', matcherOptions) +
'\n\n' +
`Received: ${printReceived(received)}`;
return { message, pass };
}
export async function toBeChecked(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeChecked', async timeout => {
return await locator.isChecked({ timeout });
}, options);
}
export async function toBeEditable(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeEditable', async timeout => {
return await locator.isEditable({ timeout });
}, options);
}
export async function toBeEnabled(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeEnabled', async timeout => {
return await locator.isEnabled({ timeout });
}, options);
}
export async function toBeDisabled(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeDisabled', async timeout => {
return await locator.isDisabled({ timeout });
}, options);
}
export async function toBeEmpty(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeEmpty', async timeout => {
return await locator.evaluate(element => {
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA')
return !(element as HTMLInputElement).value;
return !element.textContent?.trim();
}, { timeout });
}, options);
}
export async function toBeHidden(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeHidden', async timeout => {
return await locator.isHidden({ timeout });
}, options);
}
export async function toBeVisible(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeVisible', async timeout => {
return await locator.isVisible({ timeout });
}, options);
}
export async function toBeFocused(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthyImpl.call(this, 'toBeFocused', async timeout => {
return await locator.evaluate(element => {
return document.activeElement === element;
}, { timeout });
}, options);
}

View File

@ -35,16 +35,15 @@ import { monotonicTime, pollUntilDeadline } from '../util';
async function toMatchTextImpl(
this: ReturnType<Expect['getState']>,
locator: Locator,
matcherName: string,
query: (timeout: number) => Promise<string>,
expected: string | RegExp,
exactMatch: boolean,
options: { timeout?: number, useInnerText?: boolean } = {},
options: { timeout?: number, matchSubstring?: boolean } = {},
) {
const testInfo = currentTestInfo();
if (!testInfo)
throw new Error(`toMatchSnapshot() must be called during the test`);
const matcherName = exactMatch ? 'toHaveText' : 'toMatchText';
const matcherOptions: MatcherHintOptions = {
isNot: this.isNot,
promise: this.promise,
@ -72,18 +71,22 @@ async function toMatchTextImpl(
try {
await pollUntilDeadline(async () => {
received = options?.useInnerText ? await locator.innerText() : await locator.textContent() || '';
if (exactMatch)
pass = expected === received;
const remainingTime = deadline ? deadline - monotonicTime() : 0;
received = await query(remainingTime);
if (options.matchSubstring)
pass = received.includes(expected as string);
else if (typeof expected === 'string')
pass = received === expected;
else
pass = typeof expected === 'string' ? received.includes(expected) : new RegExp(expected).test(received);
pass = expected.test(received);
return pass === !matcherOptions.isNot;
}, deadline, 100);
} catch (e) {
pass = false;
}
const stringSubstring = exactMatch ? 'string' : 'substring';
const stringSubstring = options.matchSubstring ? 'substring' : 'string';
const message = pass
? () =>
typeof expected === 'string'
@ -121,20 +124,88 @@ async function toMatchTextImpl(
return { message, pass };
}
export async function toMatchText(
export async function toHaveText(
this: ReturnType<Expect['getState']>,
locator: Locator,
expected: string | RegExp,
options?: { timeout?: number, useInnerText?: boolean },
) {
return toMatchTextImpl.call(this, locator, expected, false, options);
return toMatchTextImpl.call(this, 'toHaveText', async timeout => {
if (options?.useInnerText)
return await locator.innerText({ timeout });
return await locator.textContent() || '';
}, expected, options);
}
export async function toHaveText(
export async function toContainText(
this: ReturnType<Expect['getState']>,
locator: Locator,
expected: string,
options?: { timeout?: number, useInnerText?: boolean },
) {
return toMatchTextImpl.call(this, locator, expected, true, options);
return toMatchTextImpl.call(this, 'toContainText', async timeout => {
if (options?.useInnerText)
return await locator.innerText({ timeout });
return await locator.textContent() || '';
}, expected, { ...options, matchSubstring: true });
}
export async function toHaveAttr(
this: ReturnType<Expect['getState']>,
locator: Locator,
name: string,
expected: string | RegExp,
options?: { timeout?: number },
) {
return toMatchTextImpl.call(this, 'toHaveAttr', async timeout => {
return await locator.getAttribute(name, { timeout }) || '';
}, expected, options);
}
export async function toHaveData(
this: ReturnType<Expect['getState']>,
locator: Locator,
name: string,
expected: string | RegExp,
options?: { timeout?: number },
) {
return toMatchTextImpl.call(this, 'toHaveData', async timeout => {
return await locator.getAttribute('data-' + name, { timeout }) || '';
}, expected, options);
}
export async function toHaveCSS(
this: ReturnType<Expect['getState']>,
locator: Locator,
name: string,
expected: string | RegExp,
options?: { timeout?: number },
) {
return toMatchTextImpl.call(this, 'toHaveCSS', async timeout => {
return await locator.evaluate(async (element, name) => {
return (window.getComputedStyle(element) as any)[name];
}, name, { timeout });
}, expected, options);
}
export async function toHaveId(
this: ReturnType<Expect['getState']>,
locator: Locator,
expected: string | RegExp,
options?: { timeout?: number },
) {
return toMatchTextImpl.call(this, 'toHaveId', async timeout => {
return await locator.getAttribute('id', { timeout }) || '';
}, expected, options);
}
export async function toHaveValue(
this: ReturnType<Expect['getState']>,
locator: Locator,
expected: string | RegExp,
options?: { timeout?: number },
) {
return toMatchTextImpl.call(this, 'toHaveValue', async timeout => {
return await locator.inputValue({ timeout });
}, expected, options);
}

View File

@ -16,49 +16,55 @@
import { test, expect, stripAscii } from './playwright-test-fixtures';
test('should support toMatchText', async ({ runInlineTest }) => {
test('should support toHaveText w/ regex', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const handle = page.locator('#node');
await expect(handle).toMatchText(/Text/);
const locator = page.locator('#node');
await expect(locator).toHaveText(/Text/);
});
test('fail', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const handle = page.locator('#node');
await expect(handle).toMatchText(/Text 2/, { timeout: 100 });
const locator = page.locator('#node');
await expect(locator).toHaveText(/Text 2/, { timeout: 100 });
});
`,
}, { workers: 1 });
const output = stripAscii(result.output);
expect(output).toContain('Error: expect(received).toMatchText(expected)');
expect(output).toContain('Error: expect(received).toHaveText(expected)');
expect(output).toContain('Expected pattern: /Text 2/');
expect(output).toContain('Received string: "Text content"');
expect(output).toContain('expect(handle).toMatchText');
expect(output).toContain('expect(locator).toHaveText');
expect(result.passed).toBe(1);
expect(result.failed).toBe(1);
expect(result.exitCode).toBe(1);
});
test('should support toHaveText', async ({ runInlineTest }) => {
test('should support toHaveText w/ text', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const handle = page.locator('#node');
await expect(handle).toHaveText('Text content');
const locator = page.locator('#node');
await expect(locator).toHaveText('Text content');
});
test('pass contain', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const locator = page.locator('#node');
await expect(locator).toContainText('Text');
});
test('fail', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const handle = page.locator('#node');
await expect(handle).toHaveText('Text', { timeout: 100 });
const locator = page.locator('#node');
await expect(locator).toHaveText('Text', { timeout: 100 });
});
`,
}, { workers: 1 });
@ -66,23 +72,23 @@ test('should support toHaveText', async ({ runInlineTest }) => {
expect(output).toContain('Error: expect(received).toHaveText(expected)');
expect(output).toContain('Expected string: "Text"');
expect(output).toContain('Received string: "Text content"');
expect(output).toContain('expect(handle).toHaveText');
expect(result.passed).toBe(1);
expect(output).toContain('expect(locator).toHaveText');
expect(result.passed).toBe(2);
expect(result.failed).toBe(1);
expect(result.exitCode).toBe(1);
});
test('should support toMatchText eventually', async ({ runInlineTest }) => {
test('should support toHaveText eventually', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass eventually', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const handle = page.locator('#node');
const locator = page.locator('#node');
await Promise.all([
expect(handle).toMatchText(/Text 2/),
page.waitForTimeout(1000).then(() => handle.evaluate(element => element.textContent = 'Text 2 content')),
expect(locator).toHaveText(/Text 2/),
page.waitForTimeout(1000).then(() => locator.evaluate(element => element.textContent = 'Text 2 content')),
]);
});
`,
@ -92,15 +98,15 @@ test('should support toMatchText eventually', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0);
});
test('should support toMatchText with innerText', async ({ runInlineTest }) => {
test('should support toHaveText with innerText', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const handle = page.locator('#node');
await expect(handle).toHaveText('Text content', { useInnerText: true });
const locator = page.locator('#node');
await expect(locator).toHaveText('Text content', { useInnerText: true });
});
`,
}, { workers: 1 });
@ -108,3 +114,83 @@ test('should support toMatchText with innerText', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0);
});
test('should support toHaveAttr', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const locator = page.locator('#node');
await expect(locator).toHaveAttr('id', 'node');
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});
test('should support toHaveData', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const locator = page.locator('#node');
await expect(locator).toHaveAttr('id', 'node');
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});
test('should support toHaveCSS', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node style="color: rgb(255, 0, 0)">Text content</div>');
const locator = page.locator('#node');
await expect(locator).toHaveCSS('color', 'rgb(255, 0, 0)');
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});
test('should support toHaveId', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const locator = page.locator('#node');
await expect(locator).toHaveId('node');
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});
test('should support toHaveValue', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<input id=node></input>');
const locator = page.locator('#node');
await locator.fill('Text content');
await expect(locator).toHaveValue('Text content');
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});

View File

@ -0,0 +1,146 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test, expect, stripAscii } from './playwright-test-fixtures';
test('should support toBeChecked', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<input type=checkbox checked></input>');
const locator = page.locator('input');
await expect(locator).toBeChecked();
});
test('pass not', async ({ page }) => {
await page.setContent('<input type=checkbox></input>');
const locator = page.locator('input');
await expect(locator).not.toBeChecked();
});
test('fail', async ({ page }) => {
await page.setContent('<input type=checkbox></input>');
const locator = page.locator('input');
await expect(locator).toBeChecked({ timeout: 100 });
});
`,
}, { workers: 1 });
const output = stripAscii(result.output);
expect(output).toContain('Error: expect(received).toBeChecked()');
expect(output).toContain('expect(locator).toBeChecked');
expect(result.passed).toBe(2);
expect(result.failed).toBe(1);
expect(result.exitCode).toBe(1);
});
test('should support toBeEditable, toBeEnabled, toBeDisabled, toBeEmpty', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('editable', async ({ page }) => {
await page.setContent('<input></input>');
const locator = page.locator('input');
await expect(locator).toBeEditable();
});
test('enabled', async ({ page }) => {
await page.setContent('<button>Text</button>');
const locator = page.locator('button');
await expect(locator).toBeEnabled();
});
test('disabled', async ({ page }) => {
await page.setContent('<button disabled>Text</button>');
const locator = page.locator('button');
await expect(locator).toBeDisabled();
});
test('empty input', async ({ page }) => {
await page.setContent('<input></inpput>');
const locator = page.locator('input');
await expect(locator).toBeEmpty();
});
test('non-empty input', async ({ page }) => {
await page.setContent('<input value=text></input>');
const locator = page.locator('input');
await expect(locator).not.toBeEmpty();
});
test('empty DOM', async ({ page }) => {
await page.setContent('<div style="width: 50; height: 50px"></div>');
const locator = page.locator('div');
await expect(locator).toBeEmpty();
});
`,
}, { workers: 1 });
expect(result.passed).toBe(6);
expect(result.exitCode).toBe(0);
});
test('should support toBeVisible, toBeHidden', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('visible', async ({ page }) => {
await page.setContent('<input></input>');
const locator = page.locator('input');
await expect(locator).toBeVisible();
});
test('not visible', async ({ page }) => {
await page.setContent('<button style="display: none"></button>');
const locator = page.locator('button');
await expect(locator).not.toBeVisible();
});
test('hidden', async ({ page }) => {
await page.setContent('<button style="display: none"></button>');
const locator = page.locator('button');
await expect(locator).toBeHidden();
});
test('not hidden', async ({ page }) => {
await page.setContent('<input></input>');
const locator = page.locator('input');
await expect(locator).not.toBeHidden();
});
`,
}, { workers: 1 });
expect(result.passed).toBe(4);
expect(result.exitCode).toBe(0);
});
test('should support toBeFocused', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('focused', async ({ page }) => {
await page.setContent('<input></input>');
const locator = page.locator('input');
await locator.focus();
await expect(locator).toBeFocused({ timeout: 1000 });
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});

73
types/testExpect.d.ts vendored
View File

@ -72,13 +72,78 @@ declare global {
/**
* Asserts element's exact text content.
*/
toHaveText(expected: string, options?: { timeout?: number, useInnerText?: boolean }): Promise<R>;
toHaveText(expected: string | RegExp, options?: { timeout?: number, useInnerText?: boolean }): Promise<R>;
/**
/**
* Asserts element's text content matches given pattern or contains given substring.
*/
toMatchText(expected: string | RegExp, options?: { timeout?: number, useInnerText?: boolean }): Promise<R>;
}
toContainText(expected: string, options?: { timeout?: number, useInnerText?: boolean }): Promise<R>;
/**
* Asserts element's attributes `name` matches expected value.
*/
toHaveAttr(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>;
/**
* Asserts element's data attribute data-`name` matches expected value.
*/
toHaveData(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>;
/**
* Asserts element's computed CSS property `name` matches expected value.
*/
toHaveCSS(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>;
/**
* Asserts element's `id` attribute matches expected value.
*/
toHaveId(expected: string | RegExp, options?: { timeout?: number }): Promise<R>;
/**
* Asserts input element's value.
*/
toHaveValue(expected: string | RegExp, options?: { timeout?: number }): Promise<R>;
/**
* Asserts input is checked.
*/
toBeChecked(options?: { timeout?: number }): Promise<R>;
/**
* Asserts input is editable.
*/
toBeEditable(options?: { timeout?: number }): Promise<R>;
/**
* Asserts input is enabled.
*/
toBeEnabled(options?: { timeout?: number }): Promise<R>;
/**
* Asserts input is disabled.
*/
toBeDisabled(options?: { timeout?: number }): Promise<R>;
/**
* Asserts given DOM node or input has no text content or no input value.
*/
toBeEmpty(options?: { timeout?: number }): Promise<R>;
/**
* Asserts given DOM node is hidden or detached from DOM.
*/
toBeHidden(options?: { timeout?: number }): Promise<R>;
/**
* Asserts given DOM node visible on the screen.
*/
toBeVisible(options?: { timeout?: number }): Promise<R>;
/**
* Asserts given DOM is a focused (active) in document.
*/
toBeFocused(options?: { timeout?: number }): Promise<R>;
}
}
}

33
types/types.d.ts vendored
View File

@ -6968,18 +6968,6 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
*
*/
export interface Locator {
/**
* Resolves given locator to the first matching DOM element. If no elements matching the query are visible, waits for them
* up to a given timeout. If multiple elements match the selector, throws.
* @param options
*/
elementHandle(options?: {
timeout?: number;
}): Promise<null|ElementHandle<SVGElement | HTMLElement>>;
/**
* Resolves given locator to all matching DOM elements.
*/
elementHandles(): Promise<null|ElementHandle<SVGElement | HTMLElement>[]>;
/**
* Returns the return value of `pageFunction`.
*
@ -6999,8 +6987,12 @@ export interface Locator {
* @param arg Optional argument to pass to `pageFunction`.
* @param options
*/
evaluate<R, Arg>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, void, R>): Promise<R>;
evaluate<R, Arg>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, Arg, R>, arg: Arg, options?: {
timeout?: number;
}): Promise<R>;
evaluate<R>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, void, R>, options?: {
timeout?: number;
}): Promise<R>;
/**
* The method finds all elements matching the specified locator and passes an array of matched elements as a first argument
* to `pageFunction`. Returns the result of `pageFunction` invocation.
@ -7020,6 +7012,14 @@ export interface Locator {
*/
evaluateAll<R, Arg>(pageFunction: PageFunctionOn<(SVGElement | HTMLElement)[], Arg, R>, arg: Arg): Promise<R>;
evaluateAll<R>(pageFunction: PageFunctionOn<(SVGElement | HTMLElement)[], void, R>): Promise<R>;
/**
* Resolves given locator to the first matching DOM element. If no elements matching the query are visible, waits for them
* up to a given timeout. If multiple elements match the selector, throws.
* @param options
*/
elementHandle(options?: {
timeout?: number;
}): Promise<null|ElementHandle<SVGElement | HTMLElement>>;
/**
* 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.
@ -7317,6 +7317,11 @@ export interface Locator {
timeout?: number;
}): Promise<void>;
/**
* Resolves given locator to all matching DOM elements.
*/
elementHandles(): Promise<Array<ElementHandle>>;
/**
* Returns the return value of `pageFunction` as a [JSHandle].
*

View File

@ -141,14 +141,17 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
}
export interface Locator {
evaluate<R, Arg>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, Arg, R>, arg: Arg, options?: {
timeout?: number;
}): Promise<R>;
evaluate<R>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, void, R>, options?: {
timeout?: number;
}): Promise<R>;
evaluateAll<R, Arg>(pageFunction: PageFunctionOn<(SVGElement | HTMLElement)[], Arg, R>, arg: Arg): Promise<R>;
evaluateAll<R>(pageFunction: PageFunctionOn<(SVGElement | HTMLElement)[], void, R>): Promise<R>;
elementHandle(options?: {
timeout?: number;
}): Promise<null|ElementHandle<SVGElement | HTMLElement>>;
elementHandles(): Promise<null|ElementHandle<SVGElement | HTMLElement>[]>;
evaluate<R, Arg>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunctionOn<SVGElement | HTMLElement, void, R>): Promise<R>;
evaluateAll<R, Arg>(pageFunction: PageFunctionOn<(SVGElement | HTMLElement)[], Arg, R>, arg: Arg): Promise<R>;
evaluateAll<R>(pageFunction: PageFunctionOn<(SVGElement | HTMLElement)[], void, R>): Promise<R>;
}
export interface BrowserType<Unused = {}> {