Skip to content

Commit 7f8aa70

Browse files
authored
api(waitFor): remove waitFor, use specialized wait functions (#1995)
1 parent f9f5fd0 commit 7f8aa70

File tree

7 files changed

+51
-138
lines changed

7 files changed

+51
-138
lines changed

docs/api.md

Lines changed: 26 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -710,14 +710,14 @@ page.removeListener('request', logRequest);
710710
- [page.unroute(url[, handler])](#pageunrouteurl-handler)
711711
- [page.url()](#pageurl)
712712
- [page.viewportSize()](#pageviewportsize)
713-
- [page.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])](#pagewaitforselectororfunctionortimeout-options-arg)
714713
- [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate)
715714
- [page.waitForFunction(pageFunction[, arg, options])](#pagewaitforfunctionpagefunction-arg-options)
716715
- [page.waitForLoadState([state[, options]])](#pagewaitforloadstatestate-options)
717716
- [page.waitForNavigation([options])](#pagewaitfornavigationoptions)
718717
- [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options)
719718
- [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options)
720719
- [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
720+
- [page.waitForTimeout(timeout)](#pagewaitfortimeouttimeout)
721721
- [page.workers()](#pageworkers)
722722
<!-- GEN:stop -->
723723

@@ -1688,41 +1688,6 @@ This is a shortcut for [page.mainFrame().url()](#frameurl)
16881688
- `width` <[number]> page width in pixels.
16891689
- `height` <[number]> page height in pixels.
16901690

1691-
#### page.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])
1692-
- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
1693-
- `options` <[Object]> Optional waiting parameters
1694-
- `waitFor` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `attached`.
1695-
- `polling` <[number]|"raf"|"mutation"> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
1696-
- `'raf'` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
1697-
- `'mutation'` - to execute `pageFunction` on every DOM mutation.
1698-
- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
1699-
- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction`
1700-
- returns: <[Promise]<?[JSHandle]>> Promise which resolves to a JSHandle of the success value
1701-
1702-
This method behaves differently with respect to the type of the first parameter:
1703-
- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] and the method is a shortcut for [page.waitForSelector](#pagewaitforselectorselector-options)
1704-
- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [page.waitForFunction()](#pagewaitforfunctionpagefunction-arg-options).
1705-
- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
1706-
- otherwise, an exception is thrown
1707-
1708-
```js
1709-
// wait for selector
1710-
await page.waitFor('.foo');
1711-
// wait for 1 second
1712-
await page.waitFor(1000);
1713-
// wait for predicate
1714-
await page.waitFor(() => !!document.querySelector('.foo'));
1715-
```
1716-
1717-
To pass an argument from node.js to the predicate of `page.waitFor` function:
1718-
1719-
```js
1720-
const selector = '.foo';
1721-
await page.waitFor(selector => !!document.querySelector(selector), {}, selector);
1722-
```
1723-
1724-
Shortcut for [page.mainFrame().waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-arg).
1725-
17261691
#### page.waitForEvent(event[, optionsOrPredicate])
17271692
- `event` <[string]> Event name, same one would pass into `page.on(event)`.
17281693
- `optionsOrPredicate` <[Function]|[Object]> Either a predicate that receives an event or an options object.
@@ -1871,7 +1836,22 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
18711836
await browser.close();
18721837
})();
18731838
```
1874-
Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectororfunctionortimeout-options-arg).
1839+
Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectorselector-options).
1840+
1841+
#### page.waitForTimeout(timeout)
1842+
- `timeout` <[number]> A timeout to wait for
1843+
- returns: <[Promise]>
1844+
1845+
Returns a promise that resolves after the timeout.
1846+
1847+
Note that `page.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going to be flaky. Use signals such as network events, selectors becoming visible and others instead.
1848+
1849+
```js
1850+
// wait for 1 second
1851+
await page.waitForTimeout(1000);
1852+
```
1853+
1854+
Shortcut for [page.mainFrame().waitForTimeout(timeout)](#pagewaitfortimeouttimeout).
18751855

18761856
#### page.workers()
18771857
- returns: <[Array]<[Worker]>>
@@ -1948,11 +1928,11 @@ An example of getting text from an iframe element:
19481928
- [frame.type(selector, text[, options])](#frametypeselector-text-options)
19491929
- [frame.uncheck(selector, [options])](#frameuncheckselector-options)
19501930
- [frame.url()](#frameurl)
1951-
- [frame.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])](#framewaitforselectororfunctionortimeout-options-arg)
19521931
- [frame.waitForFunction(pageFunction[, arg, options])](#framewaitforfunctionpagefunction-arg-options)
19531932
- [frame.waitForLoadState([state[, options]])](#framewaitforloadstatestate-options)
19541933
- [frame.waitForNavigation([options])](#framewaitfornavigationoptions)
19551934
- [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
1935+
- [frame.waitForTimeout(timeout)](#framewaitfortimeouttimeout)
19561936
<!-- GEN:stop -->
19571937

19581938
#### frame.$(selector)
@@ -2385,39 +2365,6 @@ If there's no element matching `selector`, the method throws an error.
23852365

23862366
Returns frame's url.
23872367

2388-
#### frame.waitFor(selectorOrFunctionOrTimeout[, options[, arg]])
2389-
- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
2390-
- `options` <[Object]> Optional waiting parameters
2391-
- `waitFor` <"attached"|"detached"|"visible"|"hidden"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`attached`) or not present in dom (`detached`). Defaults to `attached`.
2392-
- `polling` <[number]|"raf"|"mutation"> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
2393-
- `'raf'` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
2394-
- `'mutation'` - to execute `pageFunction` on every DOM mutation.
2395-
- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
2396-
- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction`
2397-
- returns: <[Promise]<?[JSHandle]>> Promise which resolves to a JSHandle of the success value
2398-
2399-
This method behaves differently with respect to the type of the first parameter:
2400-
- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] and the method is a shortcut for [frame.waitForSelector](#framewaitforselectororfunctionortimeout-options-arg)
2401-
- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [frame.waitForFunction()](#framewaitforfunctionpagefunction-arg-options).
2402-
- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
2403-
- otherwise, an exception is thrown
2404-
2405-
```js
2406-
// wait for selector
2407-
await frame.waitFor('.foo');
2408-
// wait for 1 second
2409-
await frame.waitFor(1000);
2410-
// wait for predicate
2411-
await frame.waitFor(() => !!document.querySelector('.foo'));
2412-
```
2413-
2414-
To pass an argument from node.js to the predicate of `frame.waitFor` function:
2415-
2416-
```js
2417-
const selector = '.foo';
2418-
await frame.waitFor(selector => !!document.querySelector(selector), {}, selector);
2419-
```
2420-
24212368
#### frame.waitForFunction(pageFunction[, arg, options])
24222369
- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
24232370
- `arg` <[Serializable]|[JSHandle]> Optional argument to pass to `pageFunction`
@@ -2514,6 +2461,14 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
25142461
})();
25152462
```
25162463

2464+
#### frame.waitForTimeout(timeout)
2465+
- `timeout` <[number]> A timeout to wait for
2466+
- returns: <[Promise]>
2467+
2468+
Returns a promise that resolves after the timeout.
2469+
2470+
Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going to be flaky. Use signals such as network events, selectors becoming visible and others instead.
2471+
25172472
### class: ElementHandle
25182473
* extends: [JSHandle]
25192474

docs/intro.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
- [Usage](#usage)
66
- [Writing your first script](#writing-your-first-script)
77
- [Debugging scripts](#debugging-scripts)
8-
- [Deploying to CI](#deploying-to-ci)
8+
- [Continuous Integration](#continuous-integration)
99
<!-- GEN:stop -->
1010

1111
## Installation

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/frames.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import * as network from './network';
2727
import { Page } from './page';
2828
import { selectors } from './selectors';
2929
import * as types from './types';
30-
import { waitForTimeWasUsed } from './hints';
30+
import { waitForTimeoutWasUsed } from './hints';
3131

3232
type ContextType = 'main' | 'utility';
3333
type ContextData = {
@@ -755,16 +755,9 @@ export class Frame {
755755
(handle, deadline) => handle.uncheck(helper.optionsWithUpdatedTimeout(options, deadline)));
756756
}
757757

758-
async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options: types.WaitForFunctionOptions & types.WaitForElementOptions = {}, arg?: any): Promise<js.JSHandle | null> {
759-
if (helper.isString(selectorOrFunctionOrTimeout))
760-
return this.waitForSelector(selectorOrFunctionOrTimeout, options) as any;
761-
if (helper.isNumber(selectorOrFunctionOrTimeout)) {
762-
waitForTimeWasUsed(this._page);
763-
return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout));
764-
}
765-
if (typeof selectorOrFunctionOrTimeout === 'function')
766-
return this.waitForFunction(selectorOrFunctionOrTimeout as any, arg, options);
767-
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
758+
async waitForTimeout(timeout: number) {
759+
waitForTimeoutWasUsed(this._page);
760+
await new Promise(fulfill => setTimeout(fulfill, timeout));
768761
}
769762

770763
async waitForFunction<R, Arg>(pageFunction: types.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<types.SmartHandle<R>>;

src/hints.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ const hintsLog: Log = {
2222
severity: 'warning'
2323
};
2424

25-
let waitForTimeWasUsedReported = false;
26-
export function waitForTimeWasUsed(page: Page) {
27-
if (waitForTimeWasUsedReported)
25+
let waitForTimeoutWasUsedReported = false;
26+
export function waitForTimeoutWasUsed(page: Page) {
27+
if (waitForTimeoutWasUsedReported)
2828
return;
29-
waitForTimeWasUsedReported = true;
30-
page._log(hintsLog, `WARNING: page.waitFor(timeout) should only be used for debugging.
31-
It is likely that the tests using timer in production are going to be flaky.
29+
waitForTimeoutWasUsedReported = true;
30+
page._log(hintsLog, `WARNING: page.waitForTimeout(timeout) should only be used for debugging.
31+
Tests using the timer in production are going to be flaky.
3232
Use signals such as network events, selectors becoming visible, etc. instead.`);
3333
}

src/page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,8 @@ export class Page extends ExtendedEventEmitter implements InnerLogger {
481481
return this.mainFrame().uncheck(selector, options);
482482
}
483483

484-
async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & types.WaitForElementOptions, arg?: any): Promise<js.JSHandle | null> {
485-
return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, arg);
484+
async waitForTimeout(timeout: number) {
485+
await this.mainFrame().waitForTimeout(timeout);
486486
}
487487

488488
async waitForFunction<R, Arg>(pageFunction: types.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<types.SmartHandle<R>>;

test/waittask.spec.js

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,57 +18,13 @@
1818
const utils = require('./utils');
1919
const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
2020

21-
describe('Page.waitFor', function() {
22-
it('should wait for selector', async({page, server}) => {
23-
let found = false;
24-
const waitFor = page.waitFor('div').then(() => found = true);
25-
await page.goto(server.EMPTY_PAGE);
26-
expect(found).toBe(false);
27-
await page.goto(server.PREFIX + '/grid.html');
28-
await waitFor;
29-
expect(found).toBe(true);
30-
});
31-
it('should wait for an xpath', async({page, server}) => {
32-
let found = false;
33-
const waitFor = page.waitFor('//div').then(() => found = true);
34-
await page.goto(server.EMPTY_PAGE);
35-
expect(found).toBe(false);
36-
await page.goto(server.PREFIX + '/grid.html');
37-
await waitFor;
38-
expect(found).toBe(true);
39-
});
40-
it('should not allow you to select an element with single slash xpath', async({page, server}) => {
41-
await page.setContent(`<div>some text</div>`);
42-
let error = null;
43-
await page.waitFor('/html/body/div').catch(e => error = e);
44-
expect(error).toBeTruthy();
45-
});
21+
describe('Page.waitForTimeout', function() {
4622
it('should timeout', async({page, server}) => {
4723
const startTime = Date.now();
4824
const timeout = 42;
49-
await page.waitFor(timeout);
25+
await page.waitForTimeout(timeout);
5026
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
5127
});
52-
it('should work with multiline body', async({page, server}) => {
53-
const result = await page.waitForFunction(`
54-
(() => true)()
55-
`);
56-
expect(await result.jsonValue()).toBe(true);
57-
});
58-
it('should wait for predicate', async({page, server}) => {
59-
await Promise.all([
60-
page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide
61-
page.setViewportSize({width: 10, height: 10}),
62-
]);
63-
});
64-
it('should throw when unknown type', async({page, server}) => {
65-
let error = null;
66-
await page.waitFor({foo: 'bar'}).catch(e => error = e);
67-
expect(error.message).toContain('Unsupported target type');
68-
});
69-
it('should wait for predicate with arguments', async({page, server}) => {
70-
await page.waitFor(({arg1, arg2}) => arg1 + arg2 === 3, {}, { arg1: 1, arg2: 2});
71-
});
7228
});
7329

7430
describe('Frame.waitForFunction', function() {
@@ -198,6 +154,15 @@ describe('Frame.waitForFunction', function() {
198154
await page.evaluate(() => window.__done = true);
199155
await watchdog;
200156
});
157+
it('should work with multiline body', async({page, server}) => {
158+
const result = await page.waitForFunction(`
159+
(() => true)()
160+
`);
161+
expect(await result.jsonValue()).toBe(true);
162+
});
163+
it('should wait for predicate with arguments', async({page, server}) => {
164+
await page.waitForFunction(({arg1, arg2}) => arg1 + arg2 === 3, { arg1: 1, arg2: 2});
165+
});
201166
});
202167

203168
describe('Frame.waitForSelector', function() {

0 commit comments

Comments
 (0)