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
83 changes: 35 additions & 48 deletions docs/src/api/class-frame.md
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,10 @@ Console.WriteLine(frame == contentFrame); // -> True
When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements
in that iframe.

When called without [`param: selector`], the search starts in this frame or in any of the iframes inside it,
so that you don't need to locate each iframe first. Note that the rest of the locator is resolved inside a single
frame, just like any other locator. If it matches elements inside multiple frames, an error is thrown.

**Usage**

Following snippet locates element with text "Submit" in the iframe with id `my-frame`, like `<iframe id="my-frame">`:
Expand Down Expand Up @@ -999,8 +1003,38 @@ var locator = frame.FrameLocator("#my-iframe").GetByText("Submit");
await locator.ClickAsync();
```

### param: Frame.frameLocator.selector = %%-find-selector-%%
Following snippet locates a button, either in the frame or in one of the iframes inside it:

```js
const locator = frame.frameLocator().getByRole('button');
await locator.click();
```

```java
Locator locator = frame.frameLocator().getByRole(AriaRole.BUTTON);
locator.click();
```

```python async
locator = frame.frame_locator().get_by_role("button")
await locator.click()
```

```python sync
locator = frame.frame_locator().get_by_role("button")
locator.click()
```

```csharp
var locator = frame.FrameLocator().GetByRole(AriaRole.Button);
await locator.ClickAsync();
```

### param: Frame.frameLocator.selector
* since: v1.17
- `selector` ?<[string]>

A selector that matches the frame element. When not specified, locator is matched in this frame or in any of the iframes inside it.

## async method: Frame.getAttribute
* since: v1.8
Expand Down Expand Up @@ -1439,53 +1473,6 @@ Returns the page containing this frame.

Parent frame, if any. Detached frames and main frames return `null`.

## method: Frame.pierceFrames
* since: v1.63
- returns: <[FrameLocator]>

When working with iframes, you can create a frame locator that will search for elements in this frame
and in all iframes inside it, so that you don't need to locate each iframe first.

Note that all elements matching the locator must belong to a single frame. For example, if the frame contains
two iframes, each with a `Submit` button, piercing frames and locating a button will throw an error
because it matches elements from multiple frames.

**Usage**

Following snippet locates a button, either in the frame or in one of the iframes inside it:

```js
const locator = frame.pierceFrames().getByRole('button');
await locator.click();
```

```java
Locator locator = frame.pierceFrames().getByRole(AriaRole.BUTTON);
locator.click();
```

```python async
locator = frame.pierce_frames.get_by_role("button")
await locator.click()
```

```python sync
locator = frame.pierce_frames.get_by_role("button")
locator.click()
```

```csharp
var locator = frame.PierceFrames.GetByRole(AriaRole.Button);
await locator.ClickAsync();
```

### option: Frame.pierceFrames.pierce
* since: v1.63
- `pierce` <[boolean]>

Whether to pierce frames. Pass `false` to opt out of frame piercing enabled
by the [`option: Browser.newContext.pierceFrames`] context option. Defaults to `true`.

## async method: Frame.press
* since: v1.8
* discouraged: Use locator-based [`method: Locator.press`] instead. Read more about [locators](../locators.md).
Expand Down
52 changes: 52 additions & 0 deletions docs/src/api/class-framelocator.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,58 @@ await page.Locator(".result-frame").ContentFrame.GetByRole(AriaRole.Button).Clic
await page.Locator(".result-frame").First.ContentFrame.getByRole(AriaRole.Button).ClickAsync();
```

**Any frame**

Calling [`method: Page.frameLocator`] or [`method: Frame.frameLocator`] without a selector creates a frame locator that
starts the search in any frame of the subtree - so that you don't
need to locate the iframe first.

```js
// Finds the button in any frame on the page:
await page.frameLocator().getByRole('button').click();

// Finds the iframe with id "my-frame" anywhere on the page, and clicks the button inside it:
await page.frameLocator().locator('#my-frame').contentFrame().getByRole('button').click();
```

```java
// Finds the button in any frame on the page:
page.frameLocator().getByRole(AriaRole.BUTTON).click();

// Finds the iframe with id "my-frame" anywhere on the page, and clicks the button inside it:
page.frameLocator().locator("#my-frame").contentFrame().getByRole(AriaRole.BUTTON).click();
```

```python async
# Finds the button in any frame on the page:
await page.frame_locator().get_by_role("button").click()

# Finds the iframe with id "my-frame" anywhere on the page, and clicks the button inside it:
await page.frame_locator().locator("#my-frame").content_frame.get_by_role("button").click()
```

```python sync
# Finds the button in any frame on the page:
page.frame_locator().get_by_role("button").click()

# Finds the iframe with id "my-frame" anywhere on the page, and clicks the button inside it:
page.frame_locator().locator("#my-frame").content_frame.get_by_role("button").click()
```

```csharp
// Finds the button in any frame on the page:
await page.FrameLocator().GetByRole(AriaRole.Button).ClickAsync();

// Finds the iframe with id "my-frame" anywhere on the page, and clicks the button inside it:
await page.FrameLocator().Locator("#my-frame").ContentFrame.GetByRole(AriaRole.Button).ClickAsync();
```

Only the start of the search is affected - the rest of the locator is resolved inside a single frame, just like any
other locator. Following the strictness rules above, an error is thrown when elements are matched in multiple frames.

Such a frame locator does not point to a particular `iframe`, so [`method: FrameLocator.owner`],
[`method: FrameLocator.first`], [`method: FrameLocator.last`] and [`method: FrameLocator.nth`] are not supported on it.

**Converting Locator to FrameLocator**

If you have a [Locator] object pointing to an `iframe` it can be converted to [FrameLocator] using [`method: Locator.contentFrame`].
Expand Down
83 changes: 35 additions & 48 deletions docs/src/api/class-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,10 @@ A glob pattern, regex pattern or predicate receiving frame's `url` as a [URL] ob
When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements
in that iframe.

When called without [`param: selector`], the search starts in any frame on the page - the main frame or any of
the iframes - so that you don't need to locate each iframe first. Note that the rest of the locator is resolved
inside a single frame, just like any other locator. If it matches elements inside multiple frames, an error is thrown.

**Usage**

Following snippet locates element with text "Submit" in the iframe with id `my-frame`,
Expand Down Expand Up @@ -2243,8 +2247,38 @@ var locator = page.FrameLocator("#my-iframe").GetByText("Submit");
await locator.ClickAsync();
```

### param: Page.frameLocator.selector = %%-find-selector-%%
Following snippet locates a button, either in the main frame or in one of the iframes:

```js
const locator = page.frameLocator().getByRole('button');
await locator.click();
```

```java
Locator locator = page.frameLocator().getByRole(AriaRole.BUTTON);
locator.click();
```

```python async
locator = page.frame_locator().get_by_role("button")
await locator.click()
```

```python sync
locator = page.frame_locator().get_by_role("button")
locator.click()
```

```csharp
var locator = page.FrameLocator().GetByRole(AriaRole.Button);
await locator.ClickAsync();
```

### param: Page.frameLocator.selector
* since: v1.17
- `selector` ?<[string]>

A selector that matches the frame element. When not specified, locator is matched in any frame on the page.

## method: Page.frames
* since: v1.8
Expand Down Expand Up @@ -3168,53 +3202,6 @@ var locator = await page.PickLocatorAsync();
Console.WriteLine(locator);
```

## method: Page.pierceFrames
* since: v1.63
- returns: <[FrameLocator]>

When working with iframes, you can create a frame locator that will search for elements in the main frame
and in all iframes on the page, so that you don't need to locate each iframe first.

Note that all elements matching the locator must belong to a single frame. For example, if the page contains
two iframes, each with a `Submit` button, piercing frames and locating a button will throw an error
because it matches elements from multiple frames.

**Usage**

Following snippet locates a button, either in the main frame or in one of the iframes:

```js
const locator = page.pierceFrames().getByRole('button');
await locator.click();
```

```java
Locator locator = page.pierceFrames().getByRole(AriaRole.BUTTON);
locator.click();
```

```python async
locator = page.pierce_frames.get_by_role("button")
await locator.click()
```

```python sync
locator = page.pierce_frames.get_by_role("button")
locator.click()
```

```csharp
var locator = page.PierceFrames.GetByRole(AriaRole.Button);
await locator.ClickAsync();
```

### option: Page.pierceFrames.pierce
* since: v1.63
- `pierce` <[boolean]>

Whether to pierce frames. Pass `false` to opt out of frame piercing enabled
by the [`option: Browser.newContext.pierceFrames`] context option. Defaults to `true`.

## async method: Page.press
* since: v1.8
* discouraged: Use locator-based [`method: Locator.press`] instead. Read more about [locators](../locators.md).
Expand Down
7 changes: 0 additions & 7 deletions docs/src/api/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -885,12 +885,6 @@ Actual picture of each page will be scaled down if necessary to fit the specifie

Network proxy settings to use with this context. Defaults to none.

## context-option-pierce-frames
- `pierceFrames` <[boolean]>

If set to true, all selectors in this context will pierce frames by default, as if every locator
was created through [`method: Page.pierceFrames`]. Defaults to `false`.

## context-option-strict
- `strictSelectors` <[boolean]>

Expand Down Expand Up @@ -1130,7 +1124,6 @@ between the same pixel in compared images, between zero (strict) and one (lax),
- %%-context-option-recordvideo-%%
- %%-context-option-recordvideo-dir-%%
- %%-context-option-recordvideo-size-%%
- %%-context-option-pierce-frames-%%
- %%-context-option-strict-%%
- %%-context-option-service-worker-policy-%%

Expand Down
15 changes: 0 additions & 15 deletions docs/src/test-api/class-testoptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,21 +492,6 @@ export default defineConfig({
});
```

## property: TestOptions.pierceFrames = %%-context-option-pierce-frames-%%
* since: v1.63

**Usage**

```js title="playwright.config.ts"
import { defineConfig } from '@playwright/test';

export default defineConfig({
use: {
pierceFrames: true,
}
});
```

## property: TestOptions.proxy = %%-browser-option-proxy-%%
* since: v1.10

Expand Down
2 changes: 1 addition & 1 deletion packages/injected/src/injectedScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ export class InjectedScript {
queryAll(root: SelectorRoot, body: any) {
if (body === 'enter-frame')
return [];
if (body === 'pierce-frames')
if (body === 'any-frame')
return [];
if (body === 'return-empty')
return [];
Expand Down
30 changes: 11 additions & 19 deletions packages/isomorphic/locatorGenerators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { NestedSelectorBody } from './selectorParser';
import type { ParsedSelector } from './selectorParser';

export type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl';
export type LocatorType = 'default' | 'role' | 'text' | 'label' | 'placeholder' | 'alt' | 'title' | 'test-id' | 'nth' | 'first' | 'last' | 'visible' | 'has-text' | 'has-not-text' | 'has' | 'hasNot' | 'frame' | 'frame-locator' | 'pierce-frames' | 'no-pierce-frames' | 'and' | 'or' | 'chain';
export type LocatorType = 'default' | 'role' | 'text' | 'label' | 'placeholder' | 'alt' | 'title' | 'test-id' | 'nth' | 'first' | 'last' | 'visible' | 'has-text' | 'has-not-text' | 'has' | 'hasNot' | 'frame' | 'frame-locator' | 'any-frame' | 'and' | 'or' | 'chain';
export type LocatorBase = 'page' | 'locator' | 'frame-locator';
export type Quote = '\'' | '"' | '`';

Expand Down Expand Up @@ -205,8 +205,8 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram
continue;
}
}
if (part.name === 'internal:control' && ((part.body as string) === 'pierce-frames' || (part.body as string) === 'no-pierce-frames')) {
tokens.push([factory.generateLocator(base, part.body as LocatorType, '')]);
if (part.name === 'internal:control' && (part.body as string) === 'any-frame') {
tokens.push([factory.generateLocator(base, 'any-frame', '')]);
nextBase = 'frame-locator';
continue;
}
Expand Down Expand Up @@ -320,10 +320,8 @@ export class JavaScriptLocatorFactory implements LocatorFactory {
return `frameLocator(${this.quote(body as string)})`;
case 'frame':
return `contentFrame()`;
case 'pierce-frames':
return `pierceFrames()`;
case 'no-pierce-frames':
return `pierceFrames({ pierce: false })`;
case 'any-frame':
return `frameLocator()`;
case 'nth':
return `nth(${body})`;
case 'first':
Expand Down Expand Up @@ -423,10 +421,8 @@ export class PythonLocatorFactory implements LocatorFactory {
return `frame_locator(${this.quote(body as string)})`;
case 'frame':
return `content_frame`;
case 'pierce-frames':
return `pierce_frames`;
case 'no-pierce-frames':
return `pierce_frames(pierce=False)`;
case 'any-frame':
return `frame_locator()`;
case 'nth':
return `nth(${body})`;
case 'first':
Expand Down Expand Up @@ -539,10 +535,8 @@ export class JavaLocatorFactory implements LocatorFactory {
return `frameLocator(${this.quote(body as string)})`;
case 'frame':
return `contentFrame()`;
case 'pierce-frames':
return `pierceFrames()`;
case 'no-pierce-frames':
return `pierceFrames(new ${clazz}.PierceFramesOptions().setPierce(false))`;
case 'any-frame':
return `frameLocator()`;
case 'nth':
return `nth(${body})`;
case 'first':
Expand Down Expand Up @@ -645,10 +639,8 @@ export class CSharpLocatorFactory implements LocatorFactory {
return `FrameLocator(${this.quote(body as string)})`;
case 'frame':
return `ContentFrame`;
case 'pierce-frames':
return `PierceFrames`;
case 'no-pierce-frames':
return `PierceFrames(new() { Pierce = false })`;
case 'any-frame':
return `FrameLocator()`;
case 'nth':
return `Nth(${body})`;
case 'first':
Expand Down
Loading
Loading