Skip to content

Commit

Permalink
feat(api): introduce FrameLocator.waitForFunction() method (microsoft…
Browse files Browse the repository at this point in the history
  • Loading branch information
Joone Hur committed Dec 2, 2023
1 parent 607a243 commit 8950bfa
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 1 deletion.
141 changes: 141 additions & 0 deletions docs/src/api/class-framelocator.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,144 @@ 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]>

## async method: FrameLocator.waitForFunction
* since: v1.41
- returns: <[JSHandle]>

Returns when the [`param: expression`] returns a truthy value. It resolves to a JSHandle of the truthy value.

**Usage**

The [`method: FrameLocator.waitForFunction`] can be used to observe viewport size change:

```js
const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.

(async () => {
const browser = await webkit.launch();
const page = await browser.newPage();
const watchDog = page.frameLocator('iframe').waitForFunction(() => window.innerWidth < 100);
await page.setViewportSize({ width: 50, height: 50 });
await watchDog;
await browser.close();
})();
```

```java
import com.microsoft.playwright.*;

public class Example {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
BrowserType webkit = playwright.webkit();
Browser browser = webkit.launch();
Page page = browser.newPage();
page.setViewportSize(50, 50);
page.frameLocator('iframe').waitForFunction("() => window.innerWidth < 100");
browser.close();
}
}
}
```

```python async
import asyncio
from playwright.async_api import async_playwright, Playwright

async def run(playwright: Playwright):
webkit = playwright.webkit
browser = await webkit.launch()
page = await browser.new_page()
await page.evaluate("window.x = 0; setTimeout(() => { window.x = 100 }, 1000);")
await page.frame_locator('iframe').wait_for_function("() => window.x > 0")
await browser.close()

async def main():
async with async_playwright() as playwright:
await run(playwright)
asyncio.run(main())
```

```python sync
from playwright.sync_api import sync_playwright, Playwright

def run(playwright: Playwright):
webkit = playwright.webkit
browser = webkit.launch()
page = browser.new_page()
page.evaluate("window.x = 0; setTimeout(() => { window.x = 100 }, 1000);")
page.frame_locator('iframe').wait_for_function("() => window.x > 0")
browser.close()

with sync_playwright() as playwright:
run(playwright)
```

```csharp
using Microsoft.Playwright;
using System.Threading.Tasks;

class FrameExamples
{
public static async Task WaitForFunction()
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Webkit.LaunchAsync();
var page = await browser.NewPageAsync();
await page.SetViewportSizeAsync(50, 50);
await page.MainFrame.WaitForFunctionAsync("window.innerWidth < 100");
}
}
```

To pass an argument to the predicate of [`method: FrameLocator.waitForFunction`] function:

```js
const selector = '.foo';
await page.frameLocator('iframe').waitForFunction(selector => !!document.querySelector(selector), selector);
```

```java
String selector = ".foo";
page.frameLocator('iframe').waitForFunction("selector => !!document.querySelector(selector)", selector);
```

```python async
selector = ".foo"
await page.frame_locator('iframe').wait_for_function("selector => !!document.querySelector(selector)", selector)
```

```python sync
selector = ".foo"
page.frame_locator('iframe').wait_for_function("selector => !!document.querySelector(selector)", selector)
```

```csharp
var selector = ".foo";
await page.frameLocator('iframe').WaitForFunctionAsync("selector => !!document.querySelector(selector)", selector);
```

### param: FrameLocator.waitForFunction.expression = %%-evaluate-expression-%%
* since: v1.41

### param: FrameLocator.waitForFunction.expression = %%-js-evaluate-pagefunction-%%
* since: v1.41

### param: FrameLocator.waitForFunction.arg
* since: v1.41
- `arg` ?<[EvaluationArgument]>

Optional argument to pass to [`param: expression`].

### option: FrameLocator.waitForFunction.polling = %%-js-python-wait-for-function-polling-%%
* since: v1.41

### option: FrameLocator.waitForFunction.polling = %%-csharp-java-wait-for-function-polling-%%
* since: v1.41

### option: FrameLocator.waitForFunction.timeout = %%-wait-for-function-timeout-%%
* since: v1.41

### option: FrameLocator.waitForFunction.timeout = %%-wait-for-function-timeout-js-%%
* since: v1.41
6 changes: 5 additions & 1 deletion packages/playwright-core/src/client/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import * as util from 'util';
import { asLocator, isString, monotonicTime } from '../utils';
import { ElementHandle } from './elementHandle';
import type { Frame } from './frame';
import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions, WaitForFunctionOptions } from './types';
import { parseResult, serializeArgument } from './jsHandle';
import { escapeForTextSelector } from '../utils/isomorphic/stringUtils';
import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils';
Expand Down Expand Up @@ -419,6 +419,10 @@ export class FrameLocator implements api.FrameLocator {
nth(index: number): FrameLocator {
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`);
}

async waitForFunction<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg, options?: WaitForFunctionOptions): Promise<structs.SmartHandle<R>> {
return this._frame.waitForFunction(pageFunction, arg, options);
}
}

let _testIdAttributeName: string = 'data-testid';
Expand Down
52 changes: 52 additions & 0 deletions packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17737,6 +17737,58 @@ export interface FrameLocator {
* @param index
*/
nth(index: number): FrameLocator;

/**
* Returns when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value.
*
* **Usage**
*
* The
* [frameLocator.waitForFunction(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-framelocator#frame-locator-wait-for-function)
* can be used to observe viewport size change:
*
* ```js
* const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
*
* (async () => {
* const browser = await webkit.launch();
* const page = await browser.newPage();
* const watchDog = page.frameLocator('iframe').waitForFunction(() => window.innerWidth < 100);
* await page.setViewportSize({ width: 50, height: 50 });
* await watchDog;
* await browser.close();
* })();
* ```
*
* To pass an argument to the predicate of
* [frameLocator.waitForFunction(pageFunction[, arg, options])](https://playwright.dev/docs/api/class-framelocator#frame-locator-wait-for-function)
* function:
*
* ```js
* const selector = '.foo';
* await page.frameLocator('iframe').waitForFunction(selector => !!document.querySelector(selector), selector);
* ```
*
* @param pageFunction Function to be evaluated in the page context.
* @param arg Optional argument to pass to `pageFunction`.
* @param options
*/
waitForFunction(pageFunction: Function|string, arg?: EvaluationArgument, options?: {
/**
* If `polling` is `'raf'`, then `pageFunction` is constantly executed in `requestAnimationFrame` callback. If
* `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed.
* Defaults to `raf`.
*/
polling?: number|"raf";

/**
* Maximum time to wait for in milliseconds. Defaults to `0` - no timeout. The default value can be changed via
* `actionTimeout` option in the config, or by using the
* [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout)
* or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods.
*/
timeout?: number;
}): Promise<JSHandle>;
}

/**
Expand Down
8 changes: 8 additions & 0 deletions tests/page/locator-frame.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,11 @@ it('should work with COEP/COOP/CORP isolated iframe', async ({ page, server, bro
await page.frameLocator('iframe').getByRole('button').click();
expect(await page.frames()[1].evaluate(() => window['__clicked'])).toBe(true);
});

it('should wait until the function is resolved', async ({ page, server }) => {
await routeIframe(page);
await page.goto(server.EMPTY_PAGE);
let resolved = false;
await page.frameLocator('iframe').waitForFunction('window.innerWidth == 1280').then(() => resolved = true);
expect(resolved).toBe(true);
});

0 comments on commit 8950bfa

Please sign in to comment.