From 95d649b406006f950f9a265ec061fdcf435d10a2 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 25 Mar 2024 07:42:13 -0700 Subject: [PATCH] feat: address api review feedback for 1.43 (#30066) - Update docs for `trace: retain-on-first-failure`. - Update docs for `devtools` option. - Rename `Locator.enterFrame()` to `Locator.contentFrame()`. - Rename `FrameLocator.exitFrame()` to `FrameLocator.owner()`. --- docs/src/api/class-framelocator.md | 98 +++++++++-------- docs/src/api/class-locator.md | 14 ++- docs/src/api/params.md | 5 +- docs/src/test-api/class-testoptions.md | 6 +- .../playwright-core/src/client/locator.ts | 4 +- packages/playwright-core/types/types.d.ts | 103 ++++++++++-------- packages/playwright/types/test.d.ts | 7 +- tests/page/locator-frame.spec.ts | 8 +- 8 files changed, 130 insertions(+), 115 deletions(-) diff --git a/docs/src/api/class-framelocator.md b/docs/src/api/class-framelocator.md index cf75fc8422..320ad36ba2 100644 --- a/docs/src/api/class-framelocator.md +++ b/docs/src/api/class-framelocator.md @@ -74,57 +74,11 @@ await page.FrameLocator(".result-frame").First.getByRole(AriaRole.Button).ClickA **Converting Locator to FrameLocator** -If you have a [Locator] object pointing to an `iframe` it can be converted to [FrameLocator] using [`method: Locator.enterFrame`]. +If you have a [Locator] object pointing to an `iframe` it can be converted to [FrameLocator] using [`method: Locator.contentFrame`]. **Converting FrameLocator to Locator** -If you have a [FrameLocator] object it can be converted to [Locator] pointing to the same `iframe` using [`method: FrameLocator.exitFrame`]. - - -## method: FrameLocator.exitFrame -* since: v1.43 -- returns: <[Locator]> - -Returns a [Locator] object pointing to the same `iframe` as this frame locator. - -Useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element. - -**Usage** - -```js -const frameLocator = page.frameLocator('iframe[name="embedded"]'); -// ... -const locator = frameLocator.exitFrame(); -await expect(locator).toBeVisible(); -``` - -```java -FrameLocator frameLocator = page.frameLocator("iframe[name=\"embedded\"]"); -// ... -Locator locator = frameLocator.exitFrame(); -assertThat(locator).isVisible(); -``` - -```python async -frame_locator = page.frame_locator("iframe[name=\"embedded\"]") -# ... -locator = frame_locator.exit_frame -await expect(locator).to_be_visible() -``` - -```python sync -frame_locator = page.frame_locator("iframe[name=\"embedded\"]") -# ... -locator = frame_locator.exit_frame -expect(locator).to_be_visible() -``` - -```csharp -var frameLocator = Page.FrameLocator("iframe[name=\"embedded\"]"); -// ... -var locator = frameLocator.ExitFrame; -await Expect(locator).ToBeVisibleAsync(); -``` +If you have a [FrameLocator] object it can be converted to [Locator] pointing to the same `iframe` using [`method: FrameLocator.owner`]. ## method: FrameLocator.first @@ -248,3 +202,51 @@ Returns locator to the n-th matching frame. It's zero based, `nth(0)` selects th ### param: FrameLocator.nth.index * since: v1.17 - `index` <[int]> + +## method: FrameLocator.owner +* since: v1.43 +- returns: <[Locator]> + +Returns a [Locator] object pointing to the same `iframe` as this frame locator. + +Useful when you have a [FrameLocator] object obtained somewhere, and later on would like to interact with the `iframe` element. + +For a reverse operation, use [`method: Locator.contentFrame`]. + +**Usage** + +```js +const frameLocator = page.frameLocator('iframe[name="embedded"]'); +// ... +const locator = frameLocator.owner(); +await expect(locator).toBeVisible(); +``` + +```java +FrameLocator frameLocator = page.frameLocator("iframe[name=\"embedded\"]"); +// ... +Locator locator = frameLocator.owner(); +assertThat(locator).isVisible(); +``` + +```python async +frame_locator = page.frame_locator("iframe[name=\"embedded\"]") +# ... +locator = frame_locator.owner +await expect(locator).to_be_visible() +``` + +```python sync +frame_locator = page.frame_locator("iframe[name=\"embedded\"]") +# ... +locator = frame_locator.owner +expect(locator).to_be_visible() +``` + +```csharp +var frameLocator = Page.FrameLocator("iframe[name=\"embedded\"]"); +// ... +var locator = frameLocator.Owner; +await Expect(locator).ToBeVisibleAsync(); +``` + diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index c254adc1c0..50ef738c9a 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -747,7 +747,7 @@ Resolves given locator to the first matching DOM element. If there are no matchi Resolves given locator to all matching DOM elements. If there are no matching elements, returns an empty list. -## method: Locator.enterFrame +## method: Locator.contentFrame * since: v1.43 - returns: <[FrameLocator]> @@ -755,40 +755,42 @@ Returns a [FrameLocator] object pointing to the same `iframe` as this locator. Useful when you have a [Locator] object obtained somewhere, and later on would like to interact with the content inside the frame. +For a reverse operation, use [`method: FrameLocator.owner`]. + **Usage** ```js const locator = page.locator('iframe[name="embedded"]'); // ... -const frameLocator = locator.enterFrame(); +const frameLocator = locator.contentFrame(); await frameLocator.getByRole('button').click(); ``` ```java Locator locator = page.locator("iframe[name=\"embedded\"]"); // ... -FrameLocator frameLocator = locator.enterFrame(); +FrameLocator frameLocator = locator.contentFrame(); frameLocator.getByRole(AriaRole.BUTTON).click(); ``` ```python async locator = page.locator("iframe[name=\"embedded\"]") # ... -frame_locator = locator.enter_frame +frame_locator = locator.content_frame await frame_locator.get_by_role("button").click() ``` ```python sync locator = page.locator("iframe[name=\"embedded\"]") # ... -frame_locator = locator.enter_frame +frame_locator = locator.content_frame frame_locator.get_by_role("button").click() ``` ```csharp var locator = Page.Locator("iframe[name=\"embedded\"]"); // ... -var frameLocator = locator.EnterFrame; +var frameLocator = locator.ContentFrame; await frameLocator.GetByRole(AriaRole.Button).ClickAsync(); ``` diff --git a/docs/src/api/params.md b/docs/src/api/params.md index e3b2894c3c..e33cd7d664 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -999,10 +999,11 @@ disable timeout. If specified, traces are saved into this directory. ## browser-option-devtools -* deprecated: Use [debugging tools](../debug.md) instead. - `devtools` <[boolean]> -**Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the +**Deprecated, Chromium-only.** Use [debugging tools](../debug.md) instead. + +Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the [`option: headless`] option will be set `false`. ## browser-option-slowmo diff --git a/docs/src/test-api/class-testoptions.md b/docs/src/test-api/class-testoptions.md index 0fba476d73..e8cbfa373c 100644 --- a/docs/src/test-api/class-testoptions.md +++ b/docs/src/test-api/class-testoptions.md @@ -556,10 +556,10 @@ export default defineConfig({ Whether to record trace for each test. Defaults to `'off'`. * `'off'`: Do not record trace. * `'on'`: Record trace for each test. -* `'retain-on-failure'`: Record trace for each test, but remove all traces from successful test runs. * `'on-first-retry'`: Record trace only when retrying a test for the first time. -* `'on-all-retries'`: Record traces only when retrying for all retries. -* `'retain-on-first-failure'`: Record traces only when the test fails for the first time. +* `'on-all-retries'`: Record trace only when retrying a test. +* `'retain-on-failure'`: Record trace for each test. When test run passes, remove the recorded trace. +* `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retires. When test run passes, remove the recorded trace. For more control, pass an object that specifies `mode` and trace features to enable. diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index a24286e71e..8fd2d8e088 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -192,7 +192,7 @@ export class Locator implements api.Locator { return await this._frame.$$(this._selector); } - enterFrame() { + contentFrame() { return new FrameLocator(this._frame, this._selector); } @@ -408,7 +408,7 @@ export class FrameLocator implements api.FrameLocator { return this.locator(getByRoleSelector(role, options)); } - exitFrame() { + owner() { return new Locator(this._frame, this._frameSelector); } diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 235de894cd..3bc56b0a83 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -11238,6 +11238,27 @@ export interface Locator { trial?: boolean; }): Promise; + /** + * Returns a {@link FrameLocator} object pointing to the same `iframe` as this locator. + * + * Useful when you have a {@link Locator} object obtained somewhere, and later on would like to interact with the + * content inside the frame. + * + * For a reverse operation, use + * [frameLocator.owner()](https://playwright.dev/docs/api/class-framelocator#frame-locator-owner). + * + * **Usage** + * + * ```js + * const locator = page.locator('iframe[name="embedded"]'); + * // ... + * const frameLocator = locator.contentFrame(); + * await frameLocator.getByRole('button').click(); + * ``` + * + */ + contentFrame(): FrameLocator; + /** * Returns the number of elements matching the locator. * @@ -11462,24 +11483,6 @@ export interface Locator { */ elementHandles(): Promise>; - /** - * Returns a {@link FrameLocator} object pointing to the same `iframe` as this locator. - * - * Useful when you have a {@link Locator} object obtained somewhere, and later on would like to interact with the - * content inside the frame. - * - * **Usage** - * - * ```js - * const locator = page.locator('iframe[name="embedded"]'); - * // ... - * const frameLocator = locator.enterFrame(); - * await frameLocator.getByRole('button').click(); - * ``` - * - */ - enterFrame(): FrameLocator; - /** * Set a value to the input field. * @@ -13170,9 +13173,10 @@ export interface BrowserType { deviceScaleFactor?: number; /** - * **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the - * `headless` option will be set `false`. - * @deprecated Use [debugging tools](https://playwright.dev/docs/debug) instead. + * **Deprecated, Chromium-only.** Use [debugging tools](https://playwright.dev/docs/debug) instead. + * + * Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be + * set `false`. */ devtools?: boolean; @@ -13575,9 +13579,10 @@ export interface BrowserType { chromiumSandbox?: boolean; /** - * **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the - * `headless` option will be set `false`. - * @deprecated Use [debugging tools](https://playwright.dev/docs/debug) instead. + * **Deprecated, Chromium-only.** Use [debugging tools](https://playwright.dev/docs/debug) instead. + * + * Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be + * set `false`. */ devtools?: boolean; @@ -17783,32 +17788,14 @@ export interface FileChooser { * **Converting Locator to FrameLocator** * * If you have a {@link Locator} object pointing to an `iframe` it can be converted to {@link FrameLocator} using - * [locator.enterFrame()](https://playwright.dev/docs/api/class-locator#locator-enter-frame). + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame). * * **Converting FrameLocator to Locator** * * If you have a {@link FrameLocator} object it can be converted to {@link Locator} pointing to the same `iframe` - * using [frameLocator.exitFrame()](https://playwright.dev/docs/api/class-framelocator#frame-locator-exit-frame). + * using [frameLocator.owner()](https://playwright.dev/docs/api/class-framelocator#frame-locator-owner). */ export interface FrameLocator { - /** - * Returns a {@link Locator} object pointing to the same `iframe` as this frame locator. - * - * Useful when you have a {@link FrameLocator} object obtained somewhere, and later on would like to interact with the - * `iframe` element. - * - * **Usage** - * - * ```js - * const frameLocator = page.frameLocator('iframe[name="embedded"]'); - * // ... - * const locator = frameLocator.exitFrame(); - * await expect(locator).toBeVisible(); - * ``` - * - */ - exitFrame(): Locator; - /** * Returns locator to the first matching frame. */ @@ -18190,6 +18177,27 @@ export interface FrameLocator { * @param index */ nth(index: number): FrameLocator; + + /** + * Returns a {@link Locator} object pointing to the same `iframe` as this frame locator. + * + * Useful when you have a {@link FrameLocator} object obtained somewhere, and later on would like to interact with the + * `iframe` element. + * + * For a reverse operation, use + * [locator.contentFrame()](https://playwright.dev/docs/api/class-locator#locator-content-frame). + * + * **Usage** + * + * ```js + * const frameLocator = page.frameLocator('iframe[name="embedded"]'); + * // ... + * const locator = frameLocator.owner(); + * await expect(locator).toBeVisible(); + * ``` + * + */ + owner(): Locator; } /** @@ -20270,9 +20278,10 @@ export interface LaunchOptions { chromiumSandbox?: boolean; /** - * **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the - * `headless` option will be set `false`. - * @deprecated Use [debugging tools](https://playwright.dev/docs/debug) instead. + * **Deprecated, Chromium-only.** Use [debugging tools](https://playwright.dev/docs/debug) instead. + * + * Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be + * set `false`. */ devtools?: boolean; diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 497b137774..044c0ba63c 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -5590,10 +5590,11 @@ export interface PlaywrightWorkerOptions { * Whether to record trace for each test. Defaults to `'off'`. * - `'off'`: Do not record trace. * - `'on'`: Record trace for each test. - * - `'retain-on-failure'`: Record trace for each test, but remove all traces from successful test runs. * - `'on-first-retry'`: Record trace only when retrying a test for the first time. - * - `'on-all-retries'`: Record traces only when retrying for all retries. - * - `'retain-on-first-failure'`: Record traces only when the test fails for the first time. + * - `'on-all-retries'`: Record trace only when retrying a test. + * - `'retain-on-failure'`: Record trace for each test. When test run passes, remove the recorded trace. + * - `'retain-on-first-failure'`: Record trace for the first run of each test, but not for retires. When test run + * passes, remove the recorded trace. * * For more control, pass an object that specifies `mode` and trace features to enable. * diff --git a/tests/page/locator-frame.spec.ts b/tests/page/locator-frame.spec.ts index 4b79039ff9..09767331bd 100644 --- a/tests/page/locator-frame.spec.ts +++ b/tests/page/locator-frame.spec.ts @@ -299,22 +299,22 @@ it('should work with COEP/COOP/CORP isolated iframe', async ({ page, server, bro expect(await page.frames()[1].evaluate(() => window['__clicked'])).toBe(true); }); -it('locator.enterFrame should work', async ({ page, server }) => { +it('locator.contentFrame should work', async ({ page, server }) => { await routeIframe(page); await page.goto(server.EMPTY_PAGE); const locator = page.locator('iframe'); - const frameLocator = locator.enterFrame(); + const frameLocator = locator.contentFrame(); const button = frameLocator.locator('button'); expect(await button.innerText()).toBe('Hello iframe'); await expect(button).toHaveText('Hello iframe'); await button.click(); }); -it('frameLocator.exitFrame should work', async ({ page, server }) => { +it('frameLocator.owner should work', async ({ page, server }) => { await routeIframe(page); await page.goto(server.EMPTY_PAGE); const frameLocator = page.frameLocator('iframe'); - const locator = frameLocator.exitFrame(); + const locator = frameLocator.owner(); await expect(locator).toBeVisible(); expect(await locator.getAttribute('name')).toBe('frame1'); });