Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions docs/src/api/class-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -1803,6 +1803,13 @@ var banana = await page.GetByRole(AriaRole.Listitem).Last(1);
### option: Locator.locator.hasNotText = %%-locator-option-has-not-text-%%
* since: v1.33

## async method: Locator.normalize
* since: v1.59
- returns: <[Locator]>

Returns a new locator that uses best practices for referencing the matched element, prioritizing test ids,
aria roles, and other user-facing attributes over CSS selectors. This is useful for converting implementation-detail selectors into more resilient, human-readable locators.

## method: Locator.nth
* since: v1.14
- returns: <[Locator]>
Expand Down Expand Up @@ -2562,12 +2569,6 @@ If you need to assert text on the page, prefer [`method: LocatorAssertions.toHav
### option: Locator.textContent.timeout = %%-input-timeout-js-%%
* since: v1.14

## async method: Locator.toCode
* since: v1.59
- returns: <[string]>

Returns a code string for a locator that uses best practices for referencing the matched element, prioritizing test ids, aria roles, and other user-facing attributes over CSS selectors.

## method: Locator.toString
* since: v1.57
* langs: js
Expand Down
13 changes: 7 additions & 6 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14213,6 +14213,13 @@ export interface Locator {
hasText?: string|RegExp;
}): Locator;

/**
* Returns a new locator that uses best practices for referencing the matched element, prioritizing test ids, aria
* roles, and other user-facing attributes over CSS selectors. This is useful for converting implementation-detail
* selectors into more resilient, human-readable locators.
*/
normalize(): Promise<Locator>;

/**
* Returns locator to the n-th matching element. It's zero based, `nth(0)` selects the first element.
*
Expand Down Expand Up @@ -14802,12 +14809,6 @@ export interface Locator {
timeout?: number;
}): Promise<null|string>;

/**
* Returns a code string for a locator that uses best practices for referencing the matched element, prioritizing test
* ids, aria roles, and other user-facing attributes over CSS selectors.
*/
toCode(): Promise<string>;

/**
* Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the
* text.
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/client/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,9 @@ export class Locator implements api.Locator {
return await this._frame._queryCount(this._selector, _options);
}

async toCode(): Promise<string> {
async normalize(): Promise<Locator> {
const { resolvedSelector } = await this._frame._channel.resolveSelector({ selector: this._selector });
return new Locator(this._frame, resolvedSelector).toString();
return new Locator(this._frame, resolvedSelector);
}

async getAttribute(name: string, options?: TimeoutOptions): Promise<string | null> {
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/tools/backend/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,8 @@ export class Tab extends EventEmitter<TabEventsInterface> {
let locator = this.page.locator(`aria-ref=${param.ref}`);
if (param.element)
locator = locator.describe(param.element);
const resolved = await locator.toCode();
return { locator, resolved };
const resolved = await locator.normalize();
return { locator, resolved: resolved.toString() };
} catch (e) {
throw new Error(`Ref ${param.ref} not found in the current page snapshot. Try capturing new snapshot.`);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/tools/backend/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const verifyElement = defineTabTool({
for (const frame of tab.page.frames()) {
const locator = frame.getByRole(params.role as Parameters<playwright.Frame['getByRole']>[0], { name: params.accessibleName });
if (await locator.count() > 0) {
const resolved = await locator.toCode();
const resolved = await locator.normalize();
response.addCode(`await expect(page.${resolved}).toBeVisible();`);
response.addTextResult('Done');
return;
Expand All @@ -63,7 +63,7 @@ const verifyText = defineTabTool({
for (const frame of tab.page.frames()) {
const locator = frame.getByText(params.text).filter({ visible: true });
if (await locator.count() > 0) {
const resolved = await locator.toCode();
const resolved = await locator.normalize();
response.addCode(`await expect(page.${resolved}).toBeVisible();`);
response.addTextResult('Done');
return;
Expand Down
13 changes: 7 additions & 6 deletions packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14213,6 +14213,13 @@ export interface Locator {
hasText?: string|RegExp;
}): Locator;

/**
* Returns a new locator that uses best practices for referencing the matched element, prioritizing test ids, aria
* roles, and other user-facing attributes over CSS selectors. This is useful for converting implementation-detail
* selectors into more resilient, human-readable locators.
*/
normalize(): Promise<Locator>;

/**
* Returns locator to the n-th matching element. It's zero based, `nth(0)` selects the first element.
*
Expand Down Expand Up @@ -14802,12 +14809,6 @@ export interface Locator {
timeout?: number;
}): Promise<null|string>;

/**
* Returns a code string for a locator that uses best practices for referencing the matched element, prioritizing test
* ids, aria roles, and other user-facing attributes over CSS selectors.
*/
toCode(): Promise<string>;

/**
* Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the
* text.
Expand Down
14 changes: 7 additions & 7 deletions tests/page/page-aria-snapshot-ai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,20 @@ it('should stitch all frame snapshots', async ({ page, server }) => {
expect(href3).toBe(server.PREFIX + '/frames/frame.html');

{
const resolved = await page.locator('aria-ref=e1').toCode();
expect(resolved).toBe(`locator('body')`);
const resolved = await page.locator('aria-ref=e1').normalize();
expect(resolved.toString()).toBe(`locator('body')`);
}
{
const resolved = await page.locator('aria-ref=f4e2').toCode();
expect(resolved).toBe(`locator('iframe[name="2frames"]').contentFrame().locator('iframe[name="dos"]').contentFrame().getByText('Hi, I\\'m frame')`);
const resolved = await page.locator('aria-ref=f4e2').normalize();
expect(resolved.toString()).toBe(`locator('iframe[name="2frames"]').contentFrame().locator('iframe[name="dos"]').contentFrame().getByText('Hi, I\\'m frame')`);
}
{
// Should tolerate .describe().
const resolved = await page.locator('aria-ref=f3e2').describe('foo bar').toCode();
expect(resolved).toBe(`locator('iframe[name=\"2frames\"]').contentFrame().locator('iframe[name=\"uno\"]').contentFrame().getByText('Hi, I\\'m frame')`);
const resolved = await page.locator('aria-ref=f3e2').describe('foo bar').normalize();
expect(resolved.toString()).toBe(`locator('iframe[name=\"2frames\"]').contentFrame().locator('iframe[name=\"uno\"]').contentFrame().getByText('Hi, I\\'m frame')`);
}
{
const error = await page.locator('aria-ref=e1000').toCode().catch(e => e);
const error = await page.locator('aria-ref=e1000').normalize().catch(e => e);
expect(error.message).toContain(`No element matching aria-ref=e1000`);
}
});
Expand Down
Loading