From 2f4acbb001f5e057de243fedc075b37bcc7719a7 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 18 Sep 2024 20:15:01 -0700 Subject: [PATCH] chore: use contentFrame() as a canonical locator representation (#32697) --- docs/src/api/class-framelocator.md | 46 ++++++++-------- .../src/utils/isomorphic/locatorGenerators.ts | 28 ++++------ .../src/utils/isomorphic/locatorParser.ts | 2 + packages/playwright-core/types/types.d.ts | 15 ++++-- tests/library/locator-generator.spec.ts | 52 +++++++++---------- tests/library/trace-viewer.spec.ts | 14 ++--- tests/page/locator-frame.spec.ts | 2 +- tests/page/locator-query.spec.ts | 4 +- 8 files changed, 82 insertions(+), 81 deletions(-) diff --git a/docs/src/api/class-framelocator.md b/docs/src/api/class-framelocator.md index 9851a35f96d34..7b6fb8f1bc298 100644 --- a/docs/src/api/class-framelocator.md +++ b/docs/src/api/class-framelocator.md @@ -1,30 +1,30 @@ # class: FrameLocator * since: v1.17 -FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the `iframe` and locate elements in that iframe. FrameLocator can be created with either [`method: Page.frameLocator`] or [`method: Locator.frameLocator`] method. +FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the `iframe` and locate elements in that iframe. FrameLocator can be created with either [`method: Locator.contentFrame`], [`method: Page.frameLocator`] or [`method: Locator.frameLocator`] method. ```js -const locator = page.frameLocator('#my-frame').getByText('Submit'); +const locator = page.locator('#my-frame').contentFrame().getByText('Submit'); await locator.click(); ``` ```java -Locator locator = page.frameLocator("#my-frame").getByText("Submit"); +Locator locator = page.locator("#my-frame").contentFrame().getByText("Submit"); locator.click(); ``` ```python async -locator = page.frame_locator("#my-frame").get_by_text("Submit") +locator = page.locator("#my-frame").content_frame.get_by_text("Submit") await locator.click() ``` ```python sync -locator = page.frame_locator("my-frame").get_by_text("Submit") +locator = page.locator("my-frame").content_frame.get_by_text("Submit") locator.click() ``` ```csharp -var locator = page.FrameLocator("#my-frame").GetByText("Submit"); +var locator = page.Locator("#my-frame").ContentFrame.GetByText("Submit"); await locator.ClickAsync(); ``` @@ -34,42 +34,42 @@ Frame locators are strict. This means that all operations on frame locators will ```js // Throws if there are several frames in DOM: -await page.frameLocator('.result-frame').getByRole('button').click(); +await page.locator('.result-frame').contentFrame().getByRole('button').click(); // Works because we explicitly tell locator to pick the first frame: -await page.frameLocator('.result-frame').first().getByRole('button').click(); +await page.locator('.result-frame').contentFrame().first().getByRole('button').click(); ``` ```python async # Throws if there are several frames in DOM: -await page.frame_locator('.result-frame').get_by_role('button').click() +await page.locator('.result-frame').content_frame.get_by_role('button').click() # Works because we explicitly tell locator to pick the first frame: -await page.frame_locator('.result-frame').first.get_by_role('button').click() +await page.locator('.result-frame').first.content_frame.get_by_role('button').click() ``` ```python sync # Throws if there are several frames in DOM: -page.frame_locator('.result-frame').get_by_role('button').click() +page.locator('.result-frame').content_frame.get_by_role('button').click() # Works because we explicitly tell locator to pick the first frame: -page.frame_locator('.result-frame').first.get_by_role('button').click() +page.locator('.result-frame').first.content_frame.get_by_role('button').click() ``` ```java // Throws if there are several frames in DOM: -page.frame_locator(".result-frame").getByRole(AriaRole.BUTTON).click(); +page.locator(".result-frame").contentFrame().getByRole(AriaRole.BUTTON).click(); // Works because we explicitly tell locator to pick the first frame: -page.frame_locator(".result-frame").first().getByRole(AriaRole.BUTTON).click(); +page.locator(".result-frame").first().contentFrame().getByRole(AriaRole.BUTTON).click(); ``` ```csharp // Throws if there are several frames in DOM: -await page.FrameLocator(".result-frame").GetByRole(AriaRole.Button).ClickAsync(); +await page.Locator(".result-frame").ContentFrame.GetByRole(AriaRole.Button).ClickAsync(); // Works because we explicitly tell locator to pick the first frame: -await page.FrameLocator(".result-frame").First.getByRole(AriaRole.Button).ClickAsync(); +await page.Locator(".result-frame").First.ContentFrame.getByRole(AriaRole.Button).ClickAsync(); ``` **Converting Locator to FrameLocator** @@ -82,6 +82,7 @@ If you have a [FrameLocator] object it can be converted to [Locator] pointing to ## method: FrameLocator.first +* deprecated: Use [`method: Locator.first`] followed by [`method: Locator.contentFrame`] instead. * since: v1.17 - returns: <[FrameLocator]> @@ -171,6 +172,7 @@ in that iframe. ### option: FrameLocator.getByTitle.exact = %%-locator-get-by-text-exact-%% ## method: FrameLocator.last +* deprecated: Use [`method: Locator.last`] followed by [`method: Locator.contentFrame`] instead. * since: v1.17 - returns: <[FrameLocator]> @@ -195,6 +197,7 @@ Returns locator to the last matching frame. * since: v1.33 ## method: FrameLocator.nth +* deprecated: Use [`method: Locator.nth`] followed by [`method: Locator.contentFrame`] instead. * since: v1.17 - returns: <[FrameLocator]> @@ -217,37 +220,36 @@ For a reverse operation, use [`method: Locator.contentFrame`]. **Usage** ```js -const frameLocator = page.frameLocator('iframe[name="embedded"]'); +const frameLocator = page.locator('iframe[name="embedded"]').contentFrame(); // ... const locator = frameLocator.owner(); await expect(locator).toBeVisible(); ``` ```java -FrameLocator frameLocator = page.frameLocator("iframe[name=\"embedded\"]"); +FrameLocator frameLocator = page.locator("iframe[name=\"embedded\"]").contentFrame(); // ... Locator locator = frameLocator.owner(); assertThat(locator).isVisible(); ``` ```python async -frame_locator = page.frame_locator("iframe[name=\"embedded\"]") +frame_locator = page.locator("iframe[name=\"embedded\"]").content_frame # ... locator = frame_locator.owner await expect(locator).to_be_visible() ``` ```python sync -frame_locator = page.frame_locator("iframe[name=\"embedded\"]") +frame_locator = page.locator("iframe[name=\"embedded\"]").content_frame # ... locator = frame_locator.owner expect(locator).to_be_visible() ``` ```csharp -var frameLocator = Page.FrameLocator("iframe[name=\"embedded\"]"); +var frameLocator = Page.Locator("iframe[name=\"embedded\"]").ContentFrame; // ... var locator = frameLocator.Owner; await Expect(locator).ToBeVisibleAsync(); ``` - diff --git a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts index e51fa0b6239ac..56252d02d3dd1 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts @@ -50,16 +50,6 @@ export function asLocators(lang: Language, selector: string, isFrameLocator: boo function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFrameLocator: boolean = false, maxOutputSize = 20): string[] { const parts = [...parsed.parts]; - // frameLocator('iframe').first is actually "iframe >> nth=0 >> internal:control=enter-frame" - // To make it easier to parse, we turn it into "iframe >> internal:control=enter-frame >> nth=0" - for (let index = 0; index < parts.length - 1; index++) { - if (parts[index].name === 'nth' && parts[index + 1].name === 'internal:control' && (parts[index + 1].body as string) === 'enter-frame') { - // Swap nth and enter-frame. - const [nth] = parts.splice(index, 1); - parts.splice(index + 1, 0, nth); - } - } - const tokens: string[][] = []; let nextBase: LocatorBase = isFrameLocator ? 'frame-locator' : 'page'; for (let index = 0; index < parts.length; index++) { @@ -167,15 +157,15 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram continue; } } + if (part.name === 'internal:control' && (part.body as string) === 'enter-frame') { + tokens.push([factory.generateLocator(base, 'frame', '')]); + nextBase = 'frame-locator'; + continue; + } let locatorType: LocatorType = 'default'; const nextPart = parts[index + 1]; - if (nextPart && nextPart.name === 'internal:control' && (nextPart.body as string) === 'enter-frame') { - locatorType = 'frame'; - nextBase = 'frame-locator'; - index++; - } const selectorPart = stringifySelector({ parts: [part] }); const locatorPart = factory.generateLocator(base, locatorType, selectorPart); @@ -264,7 +254,7 @@ export class JavaScriptLocatorFactory implements LocatorFactory { return `locator(${this.quote(body as string)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`; return `locator(${this.quote(body as string)})`; case 'frame': - return `frameLocator(${this.quote(body as string)})`; + return `contentFrame()`; case 'nth': return `nth(${body})`; case 'first': @@ -356,7 +346,7 @@ export class PythonLocatorFactory implements LocatorFactory { return `locator(${this.quote(body as string)}, has_not_text=${this.toHasText(options.hasNotText)})`; return `locator(${this.quote(body as string)})`; case 'frame': - return `frame_locator(${this.quote(body as string)})`; + return `content_frame`; case 'nth': return `nth(${body})`; case 'first': @@ -461,7 +451,7 @@ export class JavaLocatorFactory implements LocatorFactory { return `locator(${this.quote(body as string)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`; return `locator(${this.quote(body as string)})`; case 'frame': - return `frameLocator(${this.quote(body as string)})`; + return `contentFrame()`; case 'nth': return `nth(${body})`; case 'first': @@ -556,7 +546,7 @@ export class CSharpLocatorFactory implements LocatorFactory { return `Locator(${this.quote(body as string)}, new() { ${this.toHasNotText(options.hasNotText)} })`; return `Locator(${this.quote(body as string)})`; case 'frame': - return `FrameLocator(${this.quote(body as string)})`; + return `ContentFrame`; case 'nth': return `Nth(${body})`; case 'first': diff --git a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts index 2ad5fd29c65fa..e0c10b53fd20b 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorParser.ts @@ -75,6 +75,7 @@ function parseLocator(locator: string, testIdAttributeName: string): { selector: .replace(/has_text/g, 'hastext') .replace(/has_not/g, 'hasnot') .replace(/frame_locator/g, 'framelocator') + .replace(/content_frame/g, 'contentframe') .replace(/[{}\s]/g, '') .replace(/new\(\)/g, '') .replace(/new[\w]+\.[\w]+options\(\)/g, '') @@ -154,6 +155,7 @@ function transform(template: string, params: TemplateParams, testIdAttributeName template = template .replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => ',' + group1.toLowerCase() + '=' + group2.toLowerCase()) .replace(/framelocator\(([^)]+)\)/g, '$1.internal:control=enter-frame') + .replace(/contentframe(\(\))?/g, 'internal:control=enter-frame') .replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2') .replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, 'locator($1).internal:has-not-text=$2') .replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2') diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index ad102f9271f72..03e4c311d2c35 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -18240,11 +18240,12 @@ export interface FileChooser { /** * FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the * `iframe` and locate elements in that iframe. FrameLocator can be created with either + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame), * [page.frameLocator(selector)](https://playwright.dev/docs/api/class-page#page-frame-locator) or * [locator.frameLocator(selector)](https://playwright.dev/docs/api/class-locator#locator-frame-locator) method. * * ```js - * const locator = page.frameLocator('#my-frame').getByText('Submit'); + * const locator = page.locator('#my-frame').contentFrame().getByText('Submit'); * await locator.click(); * ``` * @@ -18255,10 +18256,10 @@ export interface FileChooser { * * ```js * // Throws if there are several frames in DOM: - * await page.frameLocator('.result-frame').getByRole('button').click(); + * await page.locator('.result-frame').contentFrame().getByRole('button').click(); * * // Works because we explicitly tell locator to pick the first frame: - * await page.frameLocator('.result-frame').first().getByRole('button').click(); + * await page.locator('.result-frame').contentFrame().first().getByRole('button').click(); * ``` * * **Converting Locator to FrameLocator** @@ -18274,6 +18275,8 @@ export interface FileChooser { export interface FrameLocator { /** * Returns locator to the first matching frame. + * @deprecated Use [locator.first()](https://playwright.dev/docs/api/class-locator#locator-first) followed by + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame) instead. */ first(): FrameLocator; @@ -18598,6 +18601,8 @@ export interface FrameLocator { /** * Returns locator to the last matching frame. + * @deprecated Use [locator.last()](https://playwright.dev/docs/api/class-locator#locator-last) followed by + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame) instead. */ last(): FrameLocator; @@ -18650,6 +18655,8 @@ export interface FrameLocator { /** * Returns locator to the n-th matching frame. It's zero based, `nth(0)` selects the first frame. + * @deprecated Use [locator.nth(index)](https://playwright.dev/docs/api/class-locator#locator-nth) followed by + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame) instead. * @param index */ nth(index: number): FrameLocator; @@ -18666,7 +18673,7 @@ export interface FrameLocator { * **Usage** * * ```js - * const frameLocator = page.frameLocator('iframe[name="embedded"]'); + * const frameLocator = page.locator('iframe[name="embedded"]').contentFrame(); * // ... * const locator = frameLocator.owner(); * await expect(locator).toBeVisible(); diff --git a/tests/library/locator-generator.spec.ts b/tests/library/locator-generator.spec.ts index 30ee22ef0e34e..d177fa7489e29 100644 --- a/tests/library/locator-generator.spec.ts +++ b/tests/library/locator-generator.spec.ts @@ -28,7 +28,7 @@ function generate(locator: Locator | FrameLocator) { function generateForSelector(selector: string) { const result: any = {}; for (const lang of ['javascript', 'python', 'java', 'csharp']) { - const locatorString = asLocator(lang, selector, false); + const locatorString = asLocator(lang, selector); expect.soft(parseLocator(lang, locatorString, 'data-testid'), lang + ' mismatch').toBe(selector); result[lang] = locatorString; } @@ -39,7 +39,7 @@ async function generateForNode(pageOrFrame: Page | Frame, target: string): Promi const selector = await pageOrFrame.locator(target).evaluate(e => (window as any).playwright.selector(e)); const result: any = {}; for (const lang of ['javascript', 'python', 'java', 'csharp']) { - const locatorString = asLocator(lang, selector, false); + const locatorString = asLocator(lang, selector); expect.soft(parseLocator(lang, locatorString)).toBe(selector); result[lang] = locatorString; } @@ -374,15 +374,15 @@ it('reverse engineer frameLocator', async ({ page }) => { .frameLocator('iframe') .locator('span'); expect.soft(generate(locator)).toEqual({ - csharp: `FrameLocator("iframe").GetByText("foo", new() { Exact = true }).FrameLocator("frame").First.FrameLocator("iframe").Locator("span")`, - java: `frameLocator("iframe").getByText("foo", new FrameLocator.GetByTextOptions().setExact(true)).frameLocator("frame").first().frameLocator("iframe").locator("span")`, - javascript: `frameLocator('iframe').getByText('foo', { exact: true }).frameLocator('frame').first().frameLocator('iframe').locator('span')`, - python: `frame_locator("iframe").get_by_text("foo", exact=True).frame_locator("frame").first.frame_locator("iframe").locator("span")`, + csharp: `Locator("iframe").ContentFrame.GetByText("foo", new() { Exact = true }).Locator("frame").First.ContentFrame.Locator("iframe").ContentFrame.Locator("span")`, + java: `locator("iframe").contentFrame().getByText("foo", new FrameLocator.GetByTextOptions().setExact(true)).locator("frame").first().contentFrame().locator("iframe").contentFrame().locator("span")`, + javascript: `locator('iframe').contentFrame().getByText('foo', { exact: true }).locator('frame').first().contentFrame().locator('iframe').contentFrame().locator('span')`, + python: `locator("iframe").content_frame.get_by_text("foo", exact=True).locator("frame").first.content_frame.locator("iframe").content_frame.locator("span")`, }); // Note that frame locators with ">>" are not restored back due to ambiguity. const selector = (page.frameLocator('div >> iframe').locator('span') as any)._selector; - expect.soft(asLocator('javascript', selector, false)).toBe(`locator('div').frameLocator('iframe').locator('span')`); + expect.soft(asLocator('javascript', selector)).toBe(`locator('div').locator('iframe').contentFrame().locator('span')`); }); it('generate multiple locators', async ({ page }) => { @@ -462,7 +462,7 @@ it('generate multiple locators', async ({ page }) => { ], }; for (const lang of ['javascript', 'java', 'python', 'csharp'] as const) { - expect.soft(asLocators(lang, selector, false)).toEqual(locators[lang]); + expect.soft(asLocators(lang, selector)).toEqual(locators[lang]); for (const locator of locators[lang]) expect.soft(parseLocator(lang, locator, 'data-testid'), `parse(${lang}): ${locator}`).toBe(selector); } @@ -485,38 +485,38 @@ it.describe(() => { python: 'locator("div").filter(has_text="Goodbye world").locator("span")', }); - expect.soft(asLocator('javascript', 'div >> internal:has-text="foo"s', false)).toBe(`locator('div').locator('internal:has-text="foo"s')`); - expect.soft(asLocator('javascript', 'div >> internal:has-not-text="foo"s', false)).toBe(`locator('div').locator('internal:has-not-text="foo"s')`); + expect.soft(asLocator('javascript', 'div >> internal:has-text="foo"s')).toBe(`locator('div').locator('internal:has-text="foo"s')`); + expect.soft(asLocator('javascript', 'div >> internal:has-not-text="foo"s')).toBe(`locator('div').locator('internal:has-not-text="foo"s')`); }); }); it('asLocator internal:and', async () => { - expect.soft(asLocator('javascript', 'div >> internal:and="span >> article"', false)).toBe(`locator('div').and(locator('span').locator('article'))`); - expect.soft(asLocator('python', 'div >> internal:and="span >> article"', false)).toBe(`locator("div").and_(locator("span").locator("article"))`); - expect.soft(asLocator('java', 'div >> internal:and="span >> article"', false)).toBe(`locator("div").and(locator("span").locator("article"))`); - expect.soft(asLocator('csharp', 'div >> internal:and="span >> article"', false)).toBe(`Locator("div").And(Locator("span").Locator("article"))`); + expect.soft(asLocator('javascript', 'div >> internal:and="span >> article"')).toBe(`locator('div').and(locator('span').locator('article'))`); + expect.soft(asLocator('python', 'div >> internal:and="span >> article"')).toBe(`locator("div").and_(locator("span").locator("article"))`); + expect.soft(asLocator('java', 'div >> internal:and="span >> article"')).toBe(`locator("div").and(locator("span").locator("article"))`); + expect.soft(asLocator('csharp', 'div >> internal:and="span >> article"')).toBe(`Locator("div").And(Locator("span").Locator("article"))`); }); it('asLocator internal:or', async () => { - expect.soft(asLocator('javascript', 'div >> internal:or="span >> article"', false)).toBe(`locator('div').or(locator('span').locator('article'))`); - expect.soft(asLocator('python', 'div >> internal:or="span >> article"', false)).toBe(`locator("div").or_(locator("span").locator("article"))`); - expect.soft(asLocator('java', 'div >> internal:or="span >> article"', false)).toBe(`locator("div").or(locator("span").locator("article"))`); - expect.soft(asLocator('csharp', 'div >> internal:or="span >> article"', false)).toBe(`Locator("div").Or(Locator("span").Locator("article"))`); + expect.soft(asLocator('javascript', 'div >> internal:or="span >> article"')).toBe(`locator('div').or(locator('span').locator('article'))`); + expect.soft(asLocator('python', 'div >> internal:or="span >> article"')).toBe(`locator("div").or_(locator("span").locator("article"))`); + expect.soft(asLocator('java', 'div >> internal:or="span >> article"')).toBe(`locator("div").or(locator("span").locator("article"))`); + expect.soft(asLocator('csharp', 'div >> internal:or="span >> article"')).toBe(`Locator("div").Or(Locator("span").Locator("article"))`); }); it('asLocator internal:chain', async () => { - expect.soft(asLocator('javascript', 'div >> internal:chain="span >> article"', false)).toBe(`locator('div').locator(locator('span').locator('article'))`); - expect.soft(asLocator('python', 'div >> internal:chain="span >> article"', false)).toBe(`locator("div").locator(locator("span").locator("article"))`); - expect.soft(asLocator('java', 'div >> internal:chain="span >> article"', false)).toBe(`locator("div").locator(locator("span").locator("article"))`); - expect.soft(asLocator('csharp', 'div >> internal:chain="span >> article"', false)).toBe(`Locator("div").Locator(Locator("span").Locator("article"))`); + expect.soft(asLocator('javascript', 'div >> internal:chain="span >> article"')).toBe(`locator('div').locator(locator('span').locator('article'))`); + expect.soft(asLocator('python', 'div >> internal:chain="span >> article"')).toBe(`locator("div").locator(locator("span").locator("article"))`); + expect.soft(asLocator('java', 'div >> internal:chain="span >> article"')).toBe(`locator("div").locator(locator("span").locator("article"))`); + expect.soft(asLocator('csharp', 'div >> internal:chain="span >> article"')).toBe(`Locator("div").Locator(Locator("span").Locator("article"))`); }); it('asLocator xpath', async () => { const selector = `//*[contains(normalizer-text(), 'foo']`; - expect.soft(asLocator('javascript', selector, false)).toBe(`locator('//*[contains(normalizer-text(), \\'foo\\']')`); - expect.soft(asLocator('python', selector, false)).toBe(`locator(\"//*[contains(normalizer-text(), 'foo']\")`); - expect.soft(asLocator('java', selector, false)).toBe(`locator(\"//*[contains(normalizer-text(), 'foo']\")`); - expect.soft(asLocator('csharp', selector, false)).toBe(`Locator(\"//*[contains(normalizer-text(), 'foo']\")`); + expect.soft(asLocator('javascript', selector)).toBe(`locator('//*[contains(normalizer-text(), \\'foo\\']')`); + expect.soft(asLocator('python', selector)).toBe(`locator(\"//*[contains(normalizer-text(), 'foo']\")`); + expect.soft(asLocator('java', selector)).toBe(`locator(\"//*[contains(normalizer-text(), 'foo']\")`); + expect.soft(asLocator('csharp', selector)).toBe(`Locator(\"//*[contains(normalizer-text(), 'foo']\")`); expect.soft(parseLocator('javascript', `locator('//*[contains(normalizer-text(), \\'foo\\']')`, 'data-testid')).toBe("//*[contains(normalizer-text(), 'foo']"); expect.soft(parseLocator('javascript', `locator("//*[contains(normalizer-text(), 'foo']")`, 'data-testid')).toBe("//*[contains(normalizer-text(), 'foo']"); expect.soft(parseLocator('javascript', `locator('xpath=//*[contains(normalizer-text(), \\'foo\\']')`, 'data-testid')).toBe("xpath=//*[contains(normalizer-text(), 'foo']"); diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 44c1258eb7e8b..780e9473feb94 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -1232,16 +1232,16 @@ test('should pick locator in iframe', async ({ page, runAndTrace, server }) => { const snapshot = await traceViewer.snapshotFrame('page.evaluate'); await snapshot.frameLocator('#frame1').getByText('Hello1').click(); - await expect.soft(cmWrapper).toContainText(`frameLocator('#frame1').getByText('Hello1')`); + await expect.soft(cmWrapper).toContainText(`locator('#frame1').contentFrame().getByText('Hello1')`); await snapshot.frameLocator('#frame1').frameLocator('iframe').getByText('Hello2').click(); - await expect.soft(cmWrapper).toContainText(`frameLocator('#frame1').frameLocator('iframe').getByText('Hello2')`, { timeout: 0 }); + await expect.soft(cmWrapper).toContainText(`locator('#frame1').contentFrame().locator('iframe').contentFrame().getByText('Hello2')`, { timeout: 0 }); await snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('[name=one]').getByText('HelloNameOne').click(); - await expect.soft(cmWrapper).toContainText(`frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').getByText('HelloNameOne')`, { timeout: 0 }); + await expect.soft(cmWrapper).toContainText(`locator('#frame1').contentFrame().locator('iframe').contentFrame().locator('iframe[name="one"]').contentFrame().getByText('HelloNameOne')`, { timeout: 0 }); await snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('[name=two]').getByText('HelloNameTwo').click(); - await expect.soft(cmWrapper).toContainText(`frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="two"]').getByText('HelloNameTwo')`, { timeout: 0 }); + await expect.soft(cmWrapper).toContainText(`locator('#frame1').contentFrame().locator('iframe').contentFrame().locator('iframe[name="two"]').contentFrame().getByText('HelloNameTwo')`, { timeout: 0 }); }); test('should highlight locator in iframe while typing', async ({ page, runAndTrace, server, platform }) => { @@ -1270,15 +1270,15 @@ test('should highlight locator in iframe while typing', async ({ page, runAndTra await traceViewer.page.locator('.CodeMirror').click(); const locators = [{ - text: `frameLocator('#frame1').getByText('Hello1')`, + text: `locator('#frame1').contentFrame().getByText('Hello1')`, element: snapshot.frameLocator('#frame1').locator('div', { hasText: 'Hello1' }), highlight: snapshot.frameLocator('#frame1').locator('x-pw-highlight'), }, { - text: `frameLocator('#frame1').frameLocator('iframe').getByText('Hello2')`, + text: `locator('#frame1').contentFrame().locator('iframe').contentFrame().getByText('Hello2')`, element: snapshot.frameLocator('#frame1').frameLocator('iframe').locator('div', { hasText: 'Hello2' }), highlight: snapshot.frameLocator('#frame1').frameLocator('iframe').locator('x-pw-highlight'), }, { - text: `frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').getByText('HelloNameOne')`, + text: `locator('#frame1').contentFrame().locator('iframe').contentFrame().locator('iframe[name="one"]').contentFrame().getByText('HelloNameOne')`, element: snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').locator('div', { hasText: 'HelloNameOne' }), highlight: snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').locator('x-pw-highlight'), }]; diff --git a/tests/page/locator-frame.spec.ts b/tests/page/locator-frame.spec.ts index 09767331bdb82..661cc24cc6cb0 100644 --- a/tests/page/locator-frame.spec.ts +++ b/tests/page/locator-frame.spec.ts @@ -98,7 +98,7 @@ it('should work for $ and $$', async ({ page, server }) => { it('should wait for frame', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); const error = await page.locator('body').frameLocator('iframe').locator('span').click({ timeout: 1000 }).catch(e => e); - expect(error.message).toContain(`waiting for locator('body').frameLocator('iframe')`); + expect(error.message).toContain(`waiting for locator('body').locator('iframe').contentFrame()`); }); it('should wait for frame 2', async ({ page, server }) => { diff --git a/tests/page/locator-query.spec.ts b/tests/page/locator-query.spec.ts index f1da9bbb34920..e7b1b859ed50e 100644 --- a/tests/page/locator-query.spec.ts +++ b/tests/page/locator-query.spec.ts @@ -246,9 +246,9 @@ it('should allow some, but not all nested frameLocators', async ({ page }) => { await expect(page.frameLocator('iframe').locator('article').or(page.frameLocator('iframe').locator('span'))).toHaveText('world'); await expect(page.frameLocator('iframe').locator('span').and(page.frameLocator('iframe').locator('#target'))).toHaveText('world'); const error1 = await expect(page.frameLocator('iframe').locator('div').or(page.frameLocator('#iframe').locator('span'))).toHaveText('world').catch(e => e); - expect(error1.message).toContain(`Frame locators are not allowed inside composite locators, while querying "frameLocator('iframe').locator('div').or(frameLocator('#iframe').locator('span'))`); + expect(error1.message).toContain(`Frame locators are not allowed inside composite locators, while querying "locator('iframe').contentFrame().locator('div').or(locator('#iframe').contentFrame().locator('span'))`); const error2 = await expect(page.frameLocator('iframe').locator('div').and(page.frameLocator('#iframe').locator('span'))).toHaveText('world').catch(e => e); - expect(error2.message).toContain(`Frame locators are not allowed inside composite locators, while querying "frameLocator('iframe').locator('div').and(frameLocator('#iframe').locator('span'))`); + expect(error2.message).toContain(`Frame locators are not allowed inside composite locators, while querying "locator('iframe').contentFrame().locator('div').and(locator('#iframe').contentFrame().locator('span'))`); }); it('should enforce same frame for has/leftOf/rightOf/above/below/near', async ({ page, server }) => {