chore: use contentFrame() as a canonical locator representation (#32697)

This commit is contained in:
Pavel Feldman 2024-09-18 20:15:01 -07:00 committed by GitHub
parent 790dbfd78f
commit 2f4acbb001
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 82 additions and 81 deletions

View File

@ -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();
```

View File

@ -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':

View File

@ -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')

View File

@ -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();

View File

@ -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']");

View File

@ -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'),
}];

View File

@ -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 }) => {

View File

@ -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 }) => {