Skip to content

Commit 5323700

Browse files
authored
feat($wait): make $wait a shortcut for waitForSelector (#932)
1 parent 3a32b14 commit 5323700

File tree

5 files changed

+46
-78
lines changed

5 files changed

+46
-78
lines changed

docs/api.md

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ page.removeListener('request', logRequest);
452452
- [page.$$(selector)](#pageselector-1)
453453
- [page.$$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args)
454454
- [page.$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args-1)
455-
- [page.$wait(selector, pageFunction[, options[, ...args]])](#pagewaitselector-pagefunction-options-args)
455+
- [page.$wait(selector[, options])](#pagewaitselector-options)
456456
- [page.accessibility](#pageaccessibility)
457457
- [page.addScriptTag(options)](#pageaddscripttagoptions)
458458
- [page.addStyleTag(options)](#pageaddstyletagoptions)
@@ -683,23 +683,24 @@ const html = await page.$eval('.main-container', e => e.outerHTML);
683683

684684
Shortcut for [page.mainFrame().$eval(selector, pageFunction)](#frameevalselector-pagefunction-args).
685685

686-
#### page.$wait(selector, pageFunction[, options[, ...args]])
687-
- `selector` <[string]> A selector to query page for
688-
- `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context
689-
- `options` <[Object]> Optional waiting parameters
690-
- `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:
691-
- `'raf'` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
692-
- `'mutation'` - to execute `pageFunction` on every DOM mutation.
693-
- `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 [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
694-
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
695-
- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
686+
#### page.$wait(selector[, options])
687+
- `selector` <[string]> A selector of an element to wait for
688+
- `options` <[Object]>
689+
- `visibility` <"visible"|"hidden"|"any"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`any`). Defaults to `any`.
690+
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
691+
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector string is added to DOM. Resolves to `null` if waiting for `hidden: true` and selector is not found in DOM.
696692

697-
This method runs `document.querySelector` within the page and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
693+
Wait for the `selector` to appear in page. If at the moment of calling
694+
the method the `selector` already exists, the method will return
695+
immediately. If the selector doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
698696

699-
If `pageFunction` returns a [Promise], then `page.$wait` would wait for the promise to resolve and return its value. The function
700-
is being called on the element periodically until either timeout expires or the function returns the truthy value.
697+
This method works across navigations:
698+
```js
699+
const handle = await page.$wait(selector);
700+
await handle.click();
701+
```
701702

702-
Shortcut for [page.mainFrame().$wait(selector, pageFunction[, options[, ...args]])](#framewaitselector-pagefunction-options-args).
703+
This is a shortcut to [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options).
703704

704705
#### page.accessibility
705706
- returns: <[Accessibility]>
@@ -1668,7 +1669,7 @@ An example of getting text from an iframe element:
16681669
- [frame.$$(selector)](#frameselector-1)
16691670
- [frame.$$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args)
16701671
- [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args-1)
1671-
- [frame.$wait(selector, pageFunction[, options[, ...args]])](#framewaitselector-pagefunction-options-args)
1672+
- [frame.$wait(selector[, options])](#framewaitselector-options)
16721673
- [frame.addScriptTag(options)](#frameaddscripttagoptions)
16731674
- [frame.addStyleTag(options)](#frameaddstyletagoptions)
16741675
- [frame.check(selector, [options])](#framecheckselector-options)
@@ -1744,21 +1745,24 @@ const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
17441745
const html = await frame.$eval('.main-container', e => e.outerHTML);
17451746
```
17461747

1747-
#### frame.$wait(selector, pageFunction[, options[, ...args]])
1748-
- `selector` <[string]> A selector to query page for
1749-
- `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context
1750-
- `options` <[Object]> Optional waiting parameters
1751-
- `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:
1752-
- `'raf'` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
1753-
- `'mutation'` - to execute `pageFunction` on every DOM mutation.
1754-
- `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 [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
1755-
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
1756-
- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
1748+
#### frame.$wait(selector[, options])
1749+
- `selector` <[string]> A selector of an element to wait for
1750+
- `options` <[Object]>
1751+
- `visibility` <"visible"|"hidden"|"any"> Wait for element to become visible (`visible`), hidden (`hidden`), present in dom (`any`). Defaults to `any`.
1752+
- `timeout` <[number]> Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
1753+
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector string is added to DOM. Resolves to `null` if waiting for `hidden: true` and selector is not found in DOM.
17571754

1758-
This method runs `document.querySelector` within the frame and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
1755+
Wait for the `selector` to appear in page. If at the moment of calling
1756+
the method the `selector` already exists, the method will return
1757+
immediately. If the selector doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
1758+
1759+
This method works across navigations:
1760+
```js
1761+
const handle = await page.$wait(selector);
1762+
await handle.click();
1763+
```
17591764

1760-
If `pageFunction` returns a [Promise], then `page.$wait` would wait for the promise to resolve and return its value. The function
1761-
is being called on the element periodically until either timeout expires or the function returns the truthy value.
1765+
This is a shortcut to [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options).
17621766

17631767
#### frame.addScriptTag(options)
17641768
- `options` <[Object]>

src/frames.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,10 @@ export class Frame {
620620
return handle;
621621
}
622622

623+
async $wait(selector: string, options?: types.TimeoutOptions & { visibility?: types.Visibility }): Promise<dom.ElementHandle<Element> | null> {
624+
return this.waitForSelector(selector, options);
625+
}
626+
623627
$eval: types.$Eval = async (selector, pageFunction, ...args) => {
624628
const context = await this._mainContext();
625629
const elementHandle = await context._$(selector);
@@ -938,12 +942,6 @@ export class Frame {
938942
return this._scheduleRerunnableTask(task, 'main', options.timeout);
939943
}
940944

941-
$wait: types.$Wait = async (selector, pageFunction, options, ...args) => {
942-
options = { timeout: this._page._timeoutSettings.timeout(), ...(options || {}) };
943-
const task = dom.waitForFunctionTask(selector, pageFunction, options, ...args);
944-
return this._scheduleRerunnableTask(task, 'main', options.timeout) as any;
945-
}
946-
947945
async title(): Promise<string> {
948946
const context = await this._utilityContext();
949947
return context.evaluate(() => document.title);

src/page.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ export class Page extends platform.EventEmitter {
215215
return this.mainFrame().waitForSelector(selector, options);
216216
}
217217

218+
async $wait(selector: string, options?: types.TimeoutOptions & { visibility?: types.Visibility }): Promise<dom.ElementHandle<Element> | null> {
219+
return this.waitForSelector(selector, options);
220+
}
221+
218222
evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => {
219223
return this.mainFrame().evaluateHandle(pageFunction, ...args as any);
220224
}
@@ -529,10 +533,6 @@ export class Page extends platform.EventEmitter {
529533
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
530534
}
531535

532-
$wait: types.$Wait = async (selector, pageFunction, options, ...args) => {
533-
return this.mainFrame().$wait(selector, pageFunction, options, ...args as any);
534-
}
535-
536536
workers(): Worker[] {
537537
return [...this._workers.values()];
538538
}

src/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export type Evaluate = <Args extends any[], R>(pageFunction: PageFunction<Args,
2727
export type EvaluateHandle = <Args extends any[], R>(pageFunction: PageFunction<Args, R>, ...args: Boxed<Args>) => Promise<Handle<R>>;
2828
export type $Eval = <Args extends any[], R>(selector: string, pageFunction: PageFunctionOn<Element, Args, R>, ...args: Boxed<Args>) => Promise<R>;
2929
export type $$Eval = <Args extends any[], R>(selector: string, pageFunction: PageFunctionOn<Element[], Args, R>, ...args: Boxed<Args>) => Promise<R>;
30-
export type $Wait = <Args extends any[], R>(selector: string, pageFunction: PageFunctionOn<Element | undefined, Args, R>, options?: WaitForFunctionOptions, ...args: Boxed<Args>) => Promise<Handle<R>>;
3130
export type EvaluateOn<T> = <Args extends any[], R>(pageFunction: PageFunctionOn<T, Args, R>, ...args: Boxed<Args>) => Promise<R>;
3231
export type EvaluateHandleOn<T> = <Args extends any[], R>(pageFunction: PageFunctionOn<T, Args, R>, ...args: Boxed<Args>) => Promise<Handle<R>>;
3332

test/waittask.spec.js

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -208,47 +208,15 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
208208
});
209209
});
210210

211-
describe('Frame.$wait', function() {
212-
it('should accept arguments', async({page, server}) => {
213-
await page.setContent('<div></div>');
214-
const result = await page.$wait('div', (e, foo, bar) => e.nodeName + foo + bar, {}, 'foo1', 'bar2');
215-
expect(await result.jsonValue()).toBe('DIVfoo1bar2');
216-
});
217-
it('should query selector constantly', async({page, server}) => {
218-
await page.setContent('<div></div>');
219-
let done = null;
220-
const resultPromise = page.$wait('span', e => e).then(r => done = r);
221-
expect(done).toBe(null);
222-
await page.setContent('<section></section>');
223-
expect(done).toBe(null);
224-
await page.setContent('<span>text</span>');
225-
await resultPromise;
226-
expect(done).not.toBe(null);
227-
expect(await done.evaluate(e => e.textContent)).toBe('text');
228-
});
229-
it('should be able to wait for removal', async({page}) => {
230-
await page.setContent('<div></div>');
231-
let done = null;
232-
const resultPromise = page.$wait('div', e => !e).then(r => done = r);
233-
expect(done).toBe(null);
234-
await page.setContent('<section></section>');
235-
await resultPromise;
236-
expect(done).not.toBe(null);
237-
expect(await done.jsonValue()).toBe(true);
238-
});
239-
});
240-
241211
describe('Frame.waitForSelector', function() {
242212
const addElement = tag => document.body.appendChild(document.createElement(tag));
243-
244213
it('should immediately resolve promise if node exists', async({page, server}) => {
245214
await page.goto(server.EMPTY_PAGE);
246215
const frame = page.mainFrame();
247216
await frame.waitForSelector('*');
248217
await frame.evaluate(addElement, 'div');
249218
await frame.waitForSelector('div');
250219
});
251-
252220
it('should work with removed MutationObserver', async({page, server}) => {
253221
await page.evaluate(() => delete window.MutationObserver);
254222
const [handle] = await Promise.all([
@@ -257,7 +225,6 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
257225
]);
258226
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
259227
});
260-
261228
it('should resolve promise when node is added', async({page, server}) => {
262229
await page.goto(server.EMPTY_PAGE);
263230
const frame = page.mainFrame();
@@ -268,15 +235,13 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
268235
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
269236
expect(tagName).toBe('DIV');
270237
});
271-
272238
it('should work when node is added through innerHTML', async({page, server}) => {
273239
await page.goto(server.EMPTY_PAGE);
274240
const watchdog = page.waitForSelector('h3 div');
275241
await page.evaluate(addElement, 'span');
276242
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
277243
await watchdog;
278244
});
279-
280245
it('Page.$ waitFor is shortcut for main frame', async({page, server}) => {
281246
await page.goto(server.EMPTY_PAGE);
282247
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
@@ -287,7 +252,6 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
287252
const eHandle = await watchdog;
288253
expect(await eHandle.ownerFrame()).toBe(page.mainFrame());
289254
});
290-
291255
it('should run in specified frame', async({page, server}) => {
292256
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
293257
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
@@ -299,7 +263,6 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
299263
const eHandle = await waitForSelectorPromise;
300264
expect(await eHandle.ownerFrame()).toBe(frame2);
301265
});
302-
303266
it('should throw when frame is detached', async({page, server}) => {
304267
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
305268
const frame = page.frames()[1];
@@ -391,7 +354,6 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
391354
expect(error).toBeTruthy();
392355
expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
393356
});
394-
395357
it('should respond to node attribute mutation', async({page, server}) => {
396358
let divFound = false;
397359
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
@@ -441,6 +403,11 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
441403
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
442404
expect(tagName).toBe('SPAN');
443405
});
406+
it('$wait alias should work', async({page, server}) => {
407+
await page.setContent('<section>test</section>');
408+
const handle = await page.$wait('section');
409+
expect(await handle.evaluate(e => e.textContent)).toBe('test');
410+
});
444411
});
445412

446413
describe('Frame.waitForSelector xpath', function() {

0 commit comments

Comments
 (0)