Skip to content

Commit 64b175c

Browse files
authored
api(waitForLoadState): restore it (#1390)
1 parent 6731d37 commit 64b175c

File tree

6 files changed

+78
-35
lines changed

6 files changed

+78
-35
lines changed

docs/api.md

+40
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ page.removeListener('request', logRequest);
686686
- [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
687687
- [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate)
688688
- [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
689+
- [page.waitForLoadState([options])](#pagewaitforloadstateoptions)
689690
- [page.waitForNavigation([options])](#pagewaitfornavigationoptions)
690691
- [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options)
691692
- [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options)
@@ -1703,6 +1704,26 @@ await page.waitForFunction(selector => !!document.querySelector(selector), {}, s
17031704

17041705
Shortcut for [page.mainFrame().waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args).
17051706

1707+
#### page.waitForLoadState([options])
1708+
- `options` <[Object]> Navigation parameters which might have the following properties:
1709+
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout), [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout), [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
1710+
- `waitUntil` <"load"|"domcontentloaded"|"networkidle0"|"networkidle2"> When to consider navigation succeeded, defaults to `load`. Events can be either:
1711+
- `'load'` - consider navigation to be finished when the `load` event is fired.
1712+
- `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
1713+
- `'networkidle0'` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
1714+
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
1715+
- returns: <[Promise]> Promise which resolves when the load state has been achieved.
1716+
1717+
This resolves when the page reaches a required load state, `load` by default. The navigation can be in progress when it is called.
1718+
If navigation is already at a required state, resolves immediately.
1719+
1720+
```js
1721+
await page.click('button'); // Click triggers navigation.
1722+
await page.waitForLoadState(); // The promise resolves after navigation has finished.
1723+
```
1724+
1725+
Shortcut for [page.mainFrame().waitForLoadState([options])](#framewaitforloadstateoptions).
1726+
17061727
#### page.waitForNavigation([options])
17071728
- `options` <[Object]> Navigation parameters which might have the following properties:
17081729
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout), [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout), [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
@@ -1887,6 +1908,7 @@ An example of getting text from an iframe element:
18871908
- [frame.url()](#frameurl)
18881909
- [frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args)
18891910
- [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args)
1911+
- [frame.waitForLoadState([options])](#framewaitforloadstateoptions)
18901912
- [frame.waitForNavigation([options])](#framewaitfornavigationoptions)
18911913
- [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
18921914
<!-- GEN:stop -->
@@ -2374,6 +2396,24 @@ const selector = '.foo';
23742396
await page.waitForFunction(selector => !!document.querySelector(selector), {}, selector);
23752397
```
23762398

2399+
#### frame.waitForLoadState([options])
2400+
- `options` <[Object]> Navigation parameters which might have the following properties:
2401+
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout), [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout), [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
2402+
- `waitUntil` <"load"|"domcontentloaded"|"networkidle0"|"networkidle2"> When to consider navigation succeeded, defaults to `load`. Events can be either:
2403+
- `'load'` - consider navigation to be finished when the `load` event is fired.
2404+
- `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
2405+
- `'networkidle0'` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
2406+
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
2407+
- returns: <[Promise]> Promise which resolves when the load state has been achieved.
2408+
2409+
This resolves when the page reaches a required load state, `load` by default. The navigation can be in progress when it is called.
2410+
If navigation is already at a required state, resolves immediately.
2411+
2412+
```js
2413+
await frame.click('button'); // Click triggers navigation.
2414+
await frame.waitForLoadState(); // The promise resolves after navigation has finished.
2415+
```
2416+
23772417
#### frame.waitForNavigation([options])
23782418
- `options` <[Object]> Navigation parameters which might have the following properties:
23792419
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout), [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout), [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.

src/frames.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,8 @@ export class Frame {
433433
return request ? request._finalRequest().response() : null;
434434
}
435435

436-
async _waitForLoadState(options: types.NavigateOptions = {}): Promise<void> {
437-
const {timeout = this._page._timeoutSettings.navigationTimeout()} = options;
436+
async waitForLoadState(options: types.NavigateOptions = {}): Promise<void> {
437+
const { timeout = this._page._timeoutSettings.navigationTimeout() } = options;
438438
const disposer = new Disposer();
439439
const error = await Promise.race([
440440
this._createFrameDestroyedPromise(),
@@ -464,7 +464,7 @@ export class Frame {
464464
let resolve: (error: {error?: Error, documentId: string}) => void;
465465
const promise = new Promise<{error?: Error, documentId: string}>(x => resolve = x);
466466
const watch = (documentId: string, error?: Error) => {
467-
if (!error && !platform.urlMatches(this.url(), url))
467+
if (!error && !helper.urlMatches(this.url(), url))
468468
return;
469469
resolve({error, documentId});
470470
};
@@ -477,7 +477,7 @@ export class Frame {
477477
let resolve: () => void;
478478
const promise = new Promise<void>(x => resolve = x);
479479
const watch = () => {
480-
if (platform.urlMatches(this.url(), url))
480+
if (helper.urlMatches(this.url(), url))
481481
resolve();
482482
};
483483
const dispose = () => this._sameDocumentNavigationWatchers.delete(watch);
@@ -639,7 +639,7 @@ export class Frame {
639639
this._page._frameManager._consoleMessageTags.set(tag, () => {
640640
// Clear lifecycle right after document.open() - see 'tag' below.
641641
this._page._frameManager.clearFrameLifecycle(this);
642-
this._waitForLoadState(options).then(resolve).catch(reject);
642+
this.waitForLoadState(options).then(resolve).catch(reject);
643643
});
644644
});
645645
const contentPromise = context.evaluate((html, tag) => {

src/helper.ts

+17
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,23 @@ class Helper {
279279
static enclosingIntSize(size: types.Size): types.Size {
280280
return { width: Math.floor(size.width + 1e-3), height: Math.floor(size.height + 1e-3) };
281281
}
282+
283+
static urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
284+
if (match === undefined || match === '')
285+
return true;
286+
if (helper.isString(match))
287+
match = helper.globToRegex(match);
288+
if (helper.isRegExp(match))
289+
return match.test(urlString);
290+
if (typeof match === 'string' && match === urlString)
291+
return true;
292+
const url = new URL(urlString);
293+
if (typeof match === 'string')
294+
return url.pathname === match;
295+
296+
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
297+
return match(url);
298+
}
282299
}
283300

284301
export function assert(value: any, message?: string): asserts value {

src/page.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export class Page extends platform.EventEmitter {
234234
return this.frames().find(f => {
235235
if (name)
236236
return f.name() === name;
237-
return platform.urlMatches(f.url(), url);
237+
return helper.urlMatches(f.url(), url);
238238
}) || null;
239239
}
240240

@@ -332,6 +332,10 @@ export class Page extends platform.EventEmitter {
332332
return waitPromise;
333333
}
334334

335+
async waitForLoadState(options?: types.NavigateOptions): Promise<void> {
336+
return this.mainFrame().waitForLoadState(options);
337+
}
338+
335339
async waitForNavigation(options?: types.WaitForNavigationOptions): Promise<network.Response | null> {
336340
return this.mainFrame().waitForNavigation(options);
337341
}
@@ -347,7 +351,7 @@ export class Page extends platform.EventEmitter {
347351
const { timeout = this._timeoutSettings.timeout() } = options;
348352
return helper.waitForEvent(this, Events.Page.Request, (request: network.Request) => {
349353
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
350-
return platform.urlMatches(request.url(), urlOrPredicate);
354+
return helper.urlMatches(request.url(), urlOrPredicate);
351355
return urlOrPredicate(request);
352356
}, timeout, this._disconnectedPromise);
353357
}
@@ -356,7 +360,7 @@ export class Page extends platform.EventEmitter {
356360
const { timeout = this._timeoutSettings.timeout() } = options;
357361
return helper.waitForEvent(this, Events.Page.Response, (response: network.Response) => {
358362
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
359-
return platform.urlMatches(response.url(), urlOrPredicate);
363+
return helper.urlMatches(response.url(), urlOrPredicate);
360364
return urlOrPredicate(response);
361365
}, timeout, this._disconnectedPromise);
362366
}
@@ -423,13 +427,13 @@ export class Page extends platform.EventEmitter {
423427
if (!route)
424428
return;
425429
for (const { url, handler } of this._routes) {
426-
if (platform.urlMatches(request.url(), url)) {
430+
if (helper.urlMatches(request.url(), url)) {
427431
handler(route, request);
428432
return;
429433
}
430434
}
431435
for (const { url, handler } of this._browserContext._routes) {
432-
if (platform.urlMatches(request.url(), url)) {
436+
if (helper.urlMatches(request.url(), url)) {
433437
handler(route, request);
434438
return;
435439
}

src/platform.ts

-18
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import * as https from 'https';
2828
import * as NodeWebSocket from 'ws';
2929

3030
import { assert, helper } from './helper';
31-
import * as types from './types';
3231
import { ConnectionTransport } from './transport';
3332

3433
export const isNode = typeof process === 'object' && !!process && typeof process.versions === 'object' && !!process.versions && !!process.versions.node;
@@ -222,23 +221,6 @@ export function getMimeType(file: string): string {
222221
return extensionToMime[extension] || 'application/octet-stream';
223222
}
224223

225-
export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
226-
if (match === undefined || match === '')
227-
return true;
228-
if (helper.isString(match))
229-
match = helper.globToRegex(match);
230-
if (helper.isRegExp(match))
231-
return match.test(urlString);
232-
if (typeof match === 'string' && match === urlString)
233-
return true;
234-
const url = new URL(urlString);
235-
if (typeof match === 'string')
236-
return url.pathname === match;
237-
238-
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
239-
return match(url);
240-
}
241-
242224
export function pngToJpeg(buffer: Buffer, quality?: number): Buffer {
243225
assert(isNode, 'Converting from png to jpeg is only supported in Node.js');
244226
return jpeg.encode(png.PNG.sync.read(buffer), quality).data;

test/navigation.spec.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -1011,34 +1011,34 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
10111011
});
10121012
});
10131013

1014-
describe('Page._waitForLoadState', () => {
1014+
describe('Page.waitForLoadState', () => {
10151015
it('should pick up ongoing navigation', async({page, server}) => {
10161016
let response = null;
10171017
server.setRoute('/one-style.css', (req, res) => response = res);
10181018
await Promise.all([
10191019
server.waitForRequest('/one-style.css'),
10201020
page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'}),
10211021
]);
1022-
const waitPromise = page.mainFrame()._waitForLoadState();
1022+
const waitPromise = page.waitForLoadState();
10231023
response.statusCode = 404;
10241024
response.end('Not found');
10251025
await waitPromise;
10261026
});
10271027
it('should respect timeout', async({page, server}) => {
10281028
server.setRoute('/one-style.css', (req, res) => response = res);
10291029
await page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
1030-
const error = await page.mainFrame()._waitForLoadState({ timeout: 1 }).catch(e => e);
1030+
const error = await page.waitForLoadState({ timeout: 1 }).catch(e => e);
10311031
expect(error.message).toBe('Navigation timeout of 1 ms exceeded');
10321032
});
10331033
it('should resolve immediately if loaded', async({page, server}) => {
10341034
await page.goto(server.PREFIX + '/one-style.html');
1035-
await page.mainFrame()._waitForLoadState();
1035+
await page.waitForLoadState();
10361036
});
10371037
it('should resolve immediately if load state matches', async({page, server}) => {
10381038
await page.goto(server.EMPTY_PAGE);
10391039
server.setRoute('/one-style.css', (req, res) => response = res);
10401040
await page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
1041-
await page.mainFrame()._waitForLoadState({ waitUntil: 'domcontentloaded' });
1041+
await page.waitForLoadState({ waitUntil: 'domcontentloaded' });
10421042
});
10431043
it('should work with pages that have loaded before being connected to', async({page, context, server}) => {
10441044
await page.goto(server.EMPTY_PAGE);
@@ -1047,7 +1047,7 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
10471047
page.evaluate(() => window._popup = window.open(document.location.href)),
10481048
]);
10491049
expect(popup.url()).toBe(server.EMPTY_PAGE);
1050-
await popup.mainFrame()._waitForLoadState();
1050+
await popup.waitForLoadState();
10511051
expect(popup.url()).toBe(server.EMPTY_PAGE);
10521052
});
10531053
});
@@ -1171,7 +1171,7 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
11711171
await frame.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
11721172
const request = await requestPromise;
11731173
let resolved = false;
1174-
const loadPromise = frame._waitForLoadState().then(() => resolved = true);
1174+
const loadPromise = frame.waitForLoadState().then(() => resolved = true);
11751175
// give the promise a chance to resolve, even though it shouldn't
11761176
await page.evaluate('1');
11771177
expect(resolved).toBe(false);

0 commit comments

Comments
 (0)