mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(api): introduce getByTestId (#17645)
This commit is contained in:
parent
57337e8df8
commit
d8f67eb75d
@ -932,6 +932,16 @@ Attribute name to get the value for.
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: Frame.getByTestId
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
||||
%%-template-locator-get-by-test-id-%%
|
||||
|
||||
### param: Frame.getByTestId.testId = %%-locator-get-by-test-id-test-id-%%
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: Frame.getByText
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
@ -137,6 +137,16 @@ in that iframe.
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: FrameLocator.getByTestId
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
||||
%%-template-locator-get-by-test-id-%%
|
||||
|
||||
### param: FrameLocator.getByTestId.testId = %%-locator-get-by-test-id-test-id-%%
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: FrameLocator.getByText
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
@ -657,6 +657,16 @@ Attribute name to get the value for.
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: Locator.getByTestId
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
||||
%%-template-locator-get-by-test-id-%%
|
||||
|
||||
### param: Locator.getByTestId.testId = %%-locator-get-by-test-id-test-id-%%
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: Locator.getByText
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
@ -2206,6 +2206,16 @@ Attribute name to get the value for.
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: Page.getByTestId
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
||||
%%-template-locator-get-by-test-id-%%
|
||||
|
||||
### param: Page.getByTestId.testId = %%-locator-get-by-test-id-test-id-%%
|
||||
* since: v1.27
|
||||
|
||||
|
||||
## method: Page.getByText
|
||||
* since: v1.27
|
||||
- returns: <[Locator]>
|
||||
|
@ -214,3 +214,14 @@ Script that evaluates to a selector engine instance. The script is evaluated in
|
||||
Whether to run this selector engine in isolated JavaScript environment. This environment has access to the same DOM, but
|
||||
not any JavaScript objects from the frame's scripts. Defaults to `false`. Note that running as a content script is not
|
||||
guaranteed when this engine is used together with other registered engines.
|
||||
|
||||
## method: Selectors.setTestIdAttribute
|
||||
* since: v1.27
|
||||
|
||||
Defines custom attribute name to be used in [`method: Page.getByTestId`]. `data-testid` is used by default.
|
||||
|
||||
### param: Selectors.setTestIdAttribute.attributeName
|
||||
* since: v1.27
|
||||
- `attributeName` <[string]>
|
||||
|
||||
Test id attribute name.
|
||||
|
@ -1058,18 +1058,30 @@ When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`,
|
||||
- %%-screenshot-option-mask-%%
|
||||
- %%-input-timeout-%%
|
||||
|
||||
## locator-get-by-test-id-test-id
|
||||
* since: v1.27
|
||||
- `testId` <[string]>
|
||||
|
||||
Id to locate the element by.
|
||||
|
||||
## locator-get-by-text-text
|
||||
* since: v1.27
|
||||
- `text` <[string]|[RegExp]>
|
||||
|
||||
Text to locate the element for.
|
||||
|
||||
## locator-get-by-text-exact
|
||||
* since: v1.27
|
||||
- `exact` <[boolean]>
|
||||
|
||||
Whether to find an exact match: case-sensitive and whole-string. Default to false.
|
||||
|
||||
## locator-get-by-role-role
|
||||
* since: v1.27
|
||||
- `role` <[string]>
|
||||
|
||||
Required aria role.
|
||||
|
||||
## locator-get-by-role-option-checked
|
||||
* since: v1.27
|
||||
- `checked` <[boolean]>
|
||||
@ -1160,6 +1172,10 @@ Locator is resolved to the element immediately before performing an action, so a
|
||||
|
||||
[Learn more about locators](../locators.md).
|
||||
|
||||
## template-locator-get-by-test-id
|
||||
|
||||
Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use [`method: Selectors.setTestIdAttribute`] to configure a different test id attribute if necessary.
|
||||
|
||||
## template-locator-get-by-text
|
||||
|
||||
Allows locating elements that contain given text.
|
||||
|
@ -202,6 +202,11 @@ Learn more about [automatic screenshots](../test-configuration.md#automatic-scre
|
||||
## property: TestOptions.storageState = %%-js-python-context-option-storage-state-%%
|
||||
* since: v1.10
|
||||
|
||||
## property: TestOptions.testIdAttribute
|
||||
* since: v1.27
|
||||
|
||||
Custom attribute to be used in [`method: Page.getByTestId`]. `data-testid` is used by default.
|
||||
|
||||
## property: TestOptions.timezoneId = %%-context-option-timezoneid-%%
|
||||
* since: v1.10
|
||||
|
||||
|
@ -303,6 +303,10 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
|
||||
return this.locator(selector, options);
|
||||
}
|
||||
|
||||
getByTestId(testId: string): Locator {
|
||||
return this.locator(Locator.getByTestIdSelector(testId));
|
||||
}
|
||||
|
||||
getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
||||
return this.locator(Locator.getByTextSelector(text, options));
|
||||
}
|
||||
|
@ -46,6 +46,15 @@ export class Locator implements api.Locator {
|
||||
_frame: Frame;
|
||||
_selector: string;
|
||||
|
||||
static _testIdAttributeName = 'data-testid';
|
||||
static _setTestIdAttribute(attributeName: string) {
|
||||
Locator._testIdAttributeName = attributeName;
|
||||
}
|
||||
|
||||
static getByTestIdSelector(testId: string): string {
|
||||
return `css=[${Locator._testIdAttributeName}=${testId}]`;
|
||||
}
|
||||
|
||||
static getByTextSelector(text: string | RegExp, options?: { exact?: boolean }): string {
|
||||
if (!isString(text))
|
||||
return `text=${text}`;
|
||||
@ -176,6 +185,10 @@ export class Locator implements api.Locator {
|
||||
return this.locator(selector, options);
|
||||
}
|
||||
|
||||
getByTestId(testId: string): Locator {
|
||||
return this.locator(Locator.getByTestIdSelector(testId));
|
||||
}
|
||||
|
||||
getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
||||
return this.locator(Locator.getByTextSelector(text, options));
|
||||
}
|
||||
@ -362,6 +375,10 @@ export class FrameLocator implements api.FrameLocator {
|
||||
return this.locator(selector, options);
|
||||
}
|
||||
|
||||
getByTestId(testId: string): Locator {
|
||||
return this.locator(Locator.getByTestIdSelector(testId));
|
||||
}
|
||||
|
||||
getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
||||
return this.locator(Locator.getByTextSelector(text, options));
|
||||
}
|
||||
|
@ -568,6 +568,10 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||
return this.mainFrame().locator(selector, options);
|
||||
}
|
||||
|
||||
getByTestId(testId: string): Locator {
|
||||
return this.mainFrame().getByTestId(testId);
|
||||
}
|
||||
|
||||
getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
||||
return this.mainFrame().getByText(text, options);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import type * as channels from '@protocol/channels';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import type { SelectorEngine } from './types';
|
||||
import type * as api from '../../types/types';
|
||||
import { Locator } from './locator';
|
||||
|
||||
export class Selectors implements api.Selectors {
|
||||
private _channels = new Set<SelectorsOwner>();
|
||||
@ -32,6 +33,10 @@ export class Selectors implements api.Selectors {
|
||||
this._registrations.push(params);
|
||||
}
|
||||
|
||||
setTestIdAttribute(attributeName: string) {
|
||||
Locator._setTestIdAttribute(attributeName);
|
||||
}
|
||||
|
||||
_addChannel(channel: SelectorsOwner) {
|
||||
this._channels.add(channel);
|
||||
for (const params of this._registrations) {
|
||||
|
68
packages/playwright-core/types/types.d.ts
vendored
68
packages/playwright-core/types/types.d.ts
vendored
@ -2492,7 +2492,7 @@ export interface Page {
|
||||
* [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
|
||||
* can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
|
||||
* recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
|
||||
* @param role
|
||||
* @param role Required aria role.
|
||||
* @param options
|
||||
*/
|
||||
getByRole(role: string, options?: {
|
||||
@ -2557,12 +2557,23 @@ export interface Page {
|
||||
selected?: boolean;
|
||||
}): Locator;
|
||||
|
||||
/**
|
||||
* Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
|
||||
* [selectors.setTestIdAttribute(attributeName)](https://playwright.dev/docs/api/class-selectors#selectors-set-test-id-attribute)
|
||||
* to configure a different test id attribute if necessary.
|
||||
* @param testId Id to locate the element by.
|
||||
*/
|
||||
getByTestId(testId: string): Locator;
|
||||
|
||||
/**
|
||||
* Allows locating elements that contain given text.
|
||||
* @param text
|
||||
* @param text Text to locate the element for.
|
||||
* @param options
|
||||
*/
|
||||
getByText(text: string|RegExp, options?: {
|
||||
/**
|
||||
* Whether to find an exact match: case-sensitive and whole-string. Default to false.
|
||||
*/
|
||||
exact?: boolean;
|
||||
}): Locator;
|
||||
|
||||
@ -5522,7 +5533,7 @@ export interface Frame {
|
||||
* [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
|
||||
* can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
|
||||
* recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
|
||||
* @param role
|
||||
* @param role Required aria role.
|
||||
* @param options
|
||||
*/
|
||||
getByRole(role: string, options?: {
|
||||
@ -5587,12 +5598,23 @@ export interface Frame {
|
||||
selected?: boolean;
|
||||
}): Locator;
|
||||
|
||||
/**
|
||||
* Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
|
||||
* [selectors.setTestIdAttribute(attributeName)](https://playwright.dev/docs/api/class-selectors#selectors-set-test-id-attribute)
|
||||
* to configure a different test id attribute if necessary.
|
||||
* @param testId Id to locate the element by.
|
||||
*/
|
||||
getByTestId(testId: string): Locator;
|
||||
|
||||
/**
|
||||
* Allows locating elements that contain given text.
|
||||
* @param text
|
||||
* @param text Text to locate the element for.
|
||||
* @param options
|
||||
*/
|
||||
getByText(text: string|RegExp, options?: {
|
||||
/**
|
||||
* Whether to find an exact match: case-sensitive and whole-string. Default to false.
|
||||
*/
|
||||
exact?: boolean;
|
||||
}): Locator;
|
||||
|
||||
@ -9899,7 +9921,7 @@ export interface Locator {
|
||||
* [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
|
||||
* can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
|
||||
* recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
|
||||
* @param role
|
||||
* @param role Required aria role.
|
||||
* @param options
|
||||
*/
|
||||
getByRole(role: string, options?: {
|
||||
@ -9964,12 +9986,23 @@ export interface Locator {
|
||||
selected?: boolean;
|
||||
}): Locator;
|
||||
|
||||
/**
|
||||
* Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
|
||||
* [selectors.setTestIdAttribute(attributeName)](https://playwright.dev/docs/api/class-selectors#selectors-set-test-id-attribute)
|
||||
* to configure a different test id attribute if necessary.
|
||||
* @param testId Id to locate the element by.
|
||||
*/
|
||||
getByTestId(testId: string): Locator;
|
||||
|
||||
/**
|
||||
* Allows locating elements that contain given text.
|
||||
* @param text
|
||||
* @param text Text to locate the element for.
|
||||
* @param options
|
||||
*/
|
||||
getByText(text: string|RegExp, options?: {
|
||||
/**
|
||||
* Whether to find an exact match: case-sensitive and whole-string. Default to false.
|
||||
*/
|
||||
exact?: boolean;
|
||||
}): Locator;
|
||||
|
||||
@ -15097,7 +15130,7 @@ export interface FrameLocator {
|
||||
* [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
|
||||
* can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
|
||||
* recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
|
||||
* @param role
|
||||
* @param role Required aria role.
|
||||
* @param options
|
||||
*/
|
||||
getByRole(role: string, options?: {
|
||||
@ -15162,12 +15195,23 @@ export interface FrameLocator {
|
||||
selected?: boolean;
|
||||
}): Locator;
|
||||
|
||||
/**
|
||||
* Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
|
||||
* [selectors.setTestIdAttribute(attributeName)](https://playwright.dev/docs/api/class-selectors#selectors-set-test-id-attribute)
|
||||
* to configure a different test id attribute if necessary.
|
||||
* @param testId Id to locate the element by.
|
||||
*/
|
||||
getByTestId(testId: string): Locator;
|
||||
|
||||
/**
|
||||
* Allows locating elements that contain given text.
|
||||
* @param text
|
||||
* @param text Text to locate the element for.
|
||||
* @param options
|
||||
*/
|
||||
getByText(text: string|RegExp, options?: {
|
||||
/**
|
||||
* Whether to find an exact match: case-sensitive and whole-string. Default to false.
|
||||
*/
|
||||
exact?: boolean;
|
||||
}): Locator;
|
||||
|
||||
@ -16252,6 +16296,14 @@ export interface Selectors {
|
||||
*/
|
||||
contentScript?: boolean;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Defines custom attribute name to be used in
|
||||
* [page.getByTestId(testId)](https://playwright.dev/docs/api/class-page#page-get-by-test-id). `data-testid` is used by
|
||||
* default.
|
||||
* @param attributeName Test id attribute name.
|
||||
*/
|
||||
setTestIdAttribute(attributeName: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,6 +143,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||
userAgent: [({ contextOptions }, use) => use(contextOptions.userAgent), { option: true }],
|
||||
viewport: [({ contextOptions }, use) => use(contextOptions.viewport === undefined ? { width: 1280, height: 720 } : contextOptions.viewport), { option: true }],
|
||||
actionTimeout: [0, { option: true }],
|
||||
testIdAttribute: ['data-testid', { option: true }],
|
||||
navigationTimeout: [0, { option: true }],
|
||||
baseURL: [async ({ }, use) => {
|
||||
await use(process.env.PLAYWRIGHT_TEST_BASE_URL);
|
||||
@ -225,7 +226,9 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||
|
||||
_snapshotSuffix: [process.platform, { scope: 'worker' }],
|
||||
|
||||
_setupContextOptionsAndArtifacts: [async ({ playwright, _snapshotSuffix, _combinedContextOptions, _browserOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => {
|
||||
_setupContextOptionsAndArtifacts: [async ({ playwright, _snapshotSuffix, _combinedContextOptions, _browserOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout, testIdAttribute }, use, testInfo) => {
|
||||
if (testIdAttribute)
|
||||
playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute);
|
||||
testInfo.snapshotSuffix = _snapshotSuffix;
|
||||
if (debugMode())
|
||||
testInfo.setTimeout(0);
|
||||
|
6
packages/playwright-test/types/test.d.ts
vendored
6
packages/playwright-test/types/test.d.ts
vendored
@ -2969,6 +2969,12 @@ export interface PlaywrightTestOptions {
|
||||
* - `'block'`: Playwright will block all registration of Service Workers.
|
||||
*/
|
||||
serviceWorkers: ServiceWorkerPolicy | undefined;
|
||||
/**
|
||||
* Custom attribute to be used in
|
||||
* [page.getByTestId(testId)](https://playwright.dev/docs/api/class-page#page-get-by-test-id). `data-testid` is used by
|
||||
* default.
|
||||
*/
|
||||
testIdAttribute: string | undefined;
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,7 +30,7 @@ async function routeIframe(page: Page) {
|
||||
body: `
|
||||
<html>
|
||||
<div>
|
||||
<button>Hello iframe</button>
|
||||
<button data-testid="buttonId">Hello iframe</button>
|
||||
<iframe src="iframe-2.html"></iframe>
|
||||
</div>
|
||||
<span>1</span>
|
||||
@ -243,6 +243,8 @@ it('role and text coverage', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const button1 = page.frameLocator('iframe').getByRole('button');
|
||||
const button2 = page.frameLocator('iframe').getByText('Hello');
|
||||
const button3 = page.frameLocator('iframe').getByTestId('buttonId');
|
||||
await expect(button1).toHaveText('Hello iframe');
|
||||
await expect(button2).toHaveText('Hello iframe');
|
||||
await expect(button3).toHaveText('Hello iframe');
|
||||
});
|
||||
|
@ -413,3 +413,10 @@ it('css on the handle should be relative', async ({ page }) => {
|
||||
expect(await div.$eval(`.find-me`, e => e.id)).toBe('target2');
|
||||
expect(await page.$eval(`div >> .find-me`, e => e.id)).toBe('target2');
|
||||
});
|
||||
|
||||
it('getByTestId should work', async ({ page }) => {
|
||||
await page.setContent('<div><div data-testid="Hello">Hello world</div></div>');
|
||||
await expect(page.getByTestId('Hello')).toHaveText('Hello world');
|
||||
await expect(page.mainFrame().getByTestId('Hello')).toHaveText('Hello world');
|
||||
await expect(page.get('div').getByTestId('Hello')).toHaveText('Hello world');
|
||||
});
|
||||
|
@ -184,3 +184,25 @@ test('should override contextOptions', async ({ runInlineTest }) => {
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should respect testIdAttribute', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
use: {
|
||||
testIdAttribute: 'data-pw',
|
||||
}
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async ({ page }) => {
|
||||
await page.setContent('<div data-pw="myid">Hi</div>');
|
||||
await expect(page.getByTestId('myid')).toHaveCount(1);
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
1
utils/generate_types/overrides-test.d.ts
vendored
1
utils/generate_types/overrides-test.d.ts
vendored
@ -237,6 +237,7 @@ export interface PlaywrightTestOptions {
|
||||
actionTimeout: number | undefined;
|
||||
navigationTimeout: number | undefined;
|
||||
serviceWorkers: ServiceWorkerPolicy | undefined;
|
||||
testIdAttribute: string | undefined;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user