mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
api(waitForLoadState): restore it (#1390)
This commit is contained in:
parent
6731d37546
commit
64b175ce10
40
docs/api.md
40
docs/api.md
@ -686,6 +686,7 @@ page.removeListener('request', logRequest);
|
||||
- [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
|
||||
- [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate)
|
||||
- [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
|
||||
- [page.waitForLoadState([options])](#pagewaitforloadstateoptions)
|
||||
- [page.waitForNavigation([options])](#pagewaitfornavigationoptions)
|
||||
- [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options)
|
||||
- [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options)
|
||||
@ -1703,6 +1704,26 @@ await page.waitForFunction(selector => !!document.querySelector(selector), {}, s
|
||||
|
||||
Shortcut for [page.mainFrame().waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args).
|
||||
|
||||
#### page.waitForLoadState([options])
|
||||
- `options` <[Object]> Navigation parameters which might have the following properties:
|
||||
- `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.
|
||||
- `waitUntil` <"load"|"domcontentloaded"|"networkidle0"|"networkidle2"> When to consider navigation succeeded, defaults to `load`. Events can be either:
|
||||
- `'load'` - consider navigation to be finished when the `load` event is fired.
|
||||
- `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
|
||||
- `'networkidle0'` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
|
||||
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
|
||||
- returns: <[Promise]> Promise which resolves when the load state has been achieved.
|
||||
|
||||
This resolves when the page reaches a required load state, `load` by default. The navigation can be in progress when it is called.
|
||||
If navigation is already at a required state, resolves immediately.
|
||||
|
||||
```js
|
||||
await page.click('button'); // Click triggers navigation.
|
||||
await page.waitForLoadState(); // The promise resolves after navigation has finished.
|
||||
```
|
||||
|
||||
Shortcut for [page.mainFrame().waitForLoadState([options])](#framewaitforloadstateoptions).
|
||||
|
||||
#### page.waitForNavigation([options])
|
||||
- `options` <[Object]> Navigation parameters which might have the following properties:
|
||||
- `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:
|
||||
- [frame.url()](#frameurl)
|
||||
- [frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args)
|
||||
- [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args)
|
||||
- [frame.waitForLoadState([options])](#framewaitforloadstateoptions)
|
||||
- [frame.waitForNavigation([options])](#framewaitfornavigationoptions)
|
||||
- [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
|
||||
<!-- GEN:stop -->
|
||||
@ -2374,6 +2396,24 @@ const selector = '.foo';
|
||||
await page.waitForFunction(selector => !!document.querySelector(selector), {}, selector);
|
||||
```
|
||||
|
||||
#### frame.waitForLoadState([options])
|
||||
- `options` <[Object]> Navigation parameters which might have the following properties:
|
||||
- `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.
|
||||
- `waitUntil` <"load"|"domcontentloaded"|"networkidle0"|"networkidle2"> When to consider navigation succeeded, defaults to `load`. Events can be either:
|
||||
- `'load'` - consider navigation to be finished when the `load` event is fired.
|
||||
- `'domcontentloaded'` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
|
||||
- `'networkidle0'` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
|
||||
- `'networkidle2'` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
|
||||
- returns: <[Promise]> Promise which resolves when the load state has been achieved.
|
||||
|
||||
This resolves when the page reaches a required load state, `load` by default. The navigation can be in progress when it is called.
|
||||
If navigation is already at a required state, resolves immediately.
|
||||
|
||||
```js
|
||||
await frame.click('button'); // Click triggers navigation.
|
||||
await frame.waitForLoadState(); // The promise resolves after navigation has finished.
|
||||
```
|
||||
|
||||
#### frame.waitForNavigation([options])
|
||||
- `options` <[Object]> Navigation parameters which might have the following properties:
|
||||
- `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.
|
||||
|
||||
@ -433,8 +433,8 @@ export class Frame {
|
||||
return request ? request._finalRequest().response() : null;
|
||||
}
|
||||
|
||||
async _waitForLoadState(options: types.NavigateOptions = {}): Promise<void> {
|
||||
const {timeout = this._page._timeoutSettings.navigationTimeout()} = options;
|
||||
async waitForLoadState(options: types.NavigateOptions = {}): Promise<void> {
|
||||
const { timeout = this._page._timeoutSettings.navigationTimeout() } = options;
|
||||
const disposer = new Disposer();
|
||||
const error = await Promise.race([
|
||||
this._createFrameDestroyedPromise(),
|
||||
@ -464,7 +464,7 @@ export class Frame {
|
||||
let resolve: (error: {error?: Error, documentId: string}) => void;
|
||||
const promise = new Promise<{error?: Error, documentId: string}>(x => resolve = x);
|
||||
const watch = (documentId: string, error?: Error) => {
|
||||
if (!error && !platform.urlMatches(this.url(), url))
|
||||
if (!error && !helper.urlMatches(this.url(), url))
|
||||
return;
|
||||
resolve({error, documentId});
|
||||
};
|
||||
@ -477,7 +477,7 @@ export class Frame {
|
||||
let resolve: () => void;
|
||||
const promise = new Promise<void>(x => resolve = x);
|
||||
const watch = () => {
|
||||
if (platform.urlMatches(this.url(), url))
|
||||
if (helper.urlMatches(this.url(), url))
|
||||
resolve();
|
||||
};
|
||||
const dispose = () => this._sameDocumentNavigationWatchers.delete(watch);
|
||||
@ -639,7 +639,7 @@ export class Frame {
|
||||
this._page._frameManager._consoleMessageTags.set(tag, () => {
|
||||
// Clear lifecycle right after document.open() - see 'tag' below.
|
||||
this._page._frameManager.clearFrameLifecycle(this);
|
||||
this._waitForLoadState(options).then(resolve).catch(reject);
|
||||
this.waitForLoadState(options).then(resolve).catch(reject);
|
||||
});
|
||||
});
|
||||
const contentPromise = context.evaluate((html, tag) => {
|
||||
|
||||
@ -279,6 +279,23 @@ class Helper {
|
||||
static enclosingIntSize(size: types.Size): types.Size {
|
||||
return { width: Math.floor(size.width + 1e-3), height: Math.floor(size.height + 1e-3) };
|
||||
}
|
||||
|
||||
static urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
|
||||
if (match === undefined || match === '')
|
||||
return true;
|
||||
if (helper.isString(match))
|
||||
match = helper.globToRegex(match);
|
||||
if (helper.isRegExp(match))
|
||||
return match.test(urlString);
|
||||
if (typeof match === 'string' && match === urlString)
|
||||
return true;
|
||||
const url = new URL(urlString);
|
||||
if (typeof match === 'string')
|
||||
return url.pathname === match;
|
||||
|
||||
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
|
||||
return match(url);
|
||||
}
|
||||
}
|
||||
|
||||
export function assert(value: any, message?: string): asserts value {
|
||||
|
||||
14
src/page.ts
14
src/page.ts
@ -234,7 +234,7 @@ export class Page extends platform.EventEmitter {
|
||||
return this.frames().find(f => {
|
||||
if (name)
|
||||
return f.name() === name;
|
||||
return platform.urlMatches(f.url(), url);
|
||||
return helper.urlMatches(f.url(), url);
|
||||
}) || null;
|
||||
}
|
||||
|
||||
@ -332,6 +332,10 @@ export class Page extends platform.EventEmitter {
|
||||
return waitPromise;
|
||||
}
|
||||
|
||||
async waitForLoadState(options?: types.NavigateOptions): Promise<void> {
|
||||
return this.mainFrame().waitForLoadState(options);
|
||||
}
|
||||
|
||||
async waitForNavigation(options?: types.WaitForNavigationOptions): Promise<network.Response | null> {
|
||||
return this.mainFrame().waitForNavigation(options);
|
||||
}
|
||||
@ -347,7 +351,7 @@ export class Page extends platform.EventEmitter {
|
||||
const { timeout = this._timeoutSettings.timeout() } = options;
|
||||
return helper.waitForEvent(this, Events.Page.Request, (request: network.Request) => {
|
||||
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
|
||||
return platform.urlMatches(request.url(), urlOrPredicate);
|
||||
return helper.urlMatches(request.url(), urlOrPredicate);
|
||||
return urlOrPredicate(request);
|
||||
}, timeout, this._disconnectedPromise);
|
||||
}
|
||||
@ -356,7 +360,7 @@ export class Page extends platform.EventEmitter {
|
||||
const { timeout = this._timeoutSettings.timeout() } = options;
|
||||
return helper.waitForEvent(this, Events.Page.Response, (response: network.Response) => {
|
||||
if (helper.isString(urlOrPredicate) || helper.isRegExp(urlOrPredicate))
|
||||
return platform.urlMatches(response.url(), urlOrPredicate);
|
||||
return helper.urlMatches(response.url(), urlOrPredicate);
|
||||
return urlOrPredicate(response);
|
||||
}, timeout, this._disconnectedPromise);
|
||||
}
|
||||
@ -423,13 +427,13 @@ export class Page extends platform.EventEmitter {
|
||||
if (!route)
|
||||
return;
|
||||
for (const { url, handler } of this._routes) {
|
||||
if (platform.urlMatches(request.url(), url)) {
|
||||
if (helper.urlMatches(request.url(), url)) {
|
||||
handler(route, request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const { url, handler } of this._browserContext._routes) {
|
||||
if (platform.urlMatches(request.url(), url)) {
|
||||
if (helper.urlMatches(request.url(), url)) {
|
||||
handler(route, request);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -28,7 +28,6 @@ import * as https from 'https';
|
||||
import * as NodeWebSocket from 'ws';
|
||||
|
||||
import { assert, helper } from './helper';
|
||||
import * as types from './types';
|
||||
import { ConnectionTransport } from './transport';
|
||||
|
||||
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 {
|
||||
return extensionToMime[extension] || 'application/octet-stream';
|
||||
}
|
||||
|
||||
export function urlMatches(urlString: string, match: types.URLMatch | undefined): boolean {
|
||||
if (match === undefined || match === '')
|
||||
return true;
|
||||
if (helper.isString(match))
|
||||
match = helper.globToRegex(match);
|
||||
if (helper.isRegExp(match))
|
||||
return match.test(urlString);
|
||||
if (typeof match === 'string' && match === urlString)
|
||||
return true;
|
||||
const url = new URL(urlString);
|
||||
if (typeof match === 'string')
|
||||
return url.pathname === match;
|
||||
|
||||
assert(typeof match === 'function', 'url parameter should be string, RegExp or function');
|
||||
return match(url);
|
||||
}
|
||||
|
||||
export function pngToJpeg(buffer: Buffer, quality?: number): Buffer {
|
||||
assert(isNode, 'Converting from png to jpeg is only supported in Node.js');
|
||||
return jpeg.encode(png.PNG.sync.read(buffer), quality).data;
|
||||
|
||||
@ -1011,7 +1011,7 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page._waitForLoadState', () => {
|
||||
describe('Page.waitForLoadState', () => {
|
||||
it('should pick up ongoing navigation', async({page, server}) => {
|
||||
let response = null;
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
@ -1019,7 +1019,7 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
|
||||
server.waitForRequest('/one-style.css'),
|
||||
page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'}),
|
||||
]);
|
||||
const waitPromise = page.mainFrame()._waitForLoadState();
|
||||
const waitPromise = page.waitForLoadState();
|
||||
response.statusCode = 404;
|
||||
response.end('Not found');
|
||||
await waitPromise;
|
||||
@ -1027,18 +1027,18 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
await page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
|
||||
const error = await page.mainFrame()._waitForLoadState({ timeout: 1 }).catch(e => e);
|
||||
const error = await page.waitForLoadState({ timeout: 1 }).catch(e => e);
|
||||
expect(error.message).toBe('Navigation timeout of 1 ms exceeded');
|
||||
});
|
||||
it('should resolve immediately if loaded', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await page.mainFrame()._waitForLoadState();
|
||||
await page.waitForLoadState();
|
||||
});
|
||||
it('should resolve immediately if load state matches', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
await page.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
|
||||
await page.mainFrame()._waitForLoadState({ waitUntil: 'domcontentloaded' });
|
||||
await page.waitForLoadState({ waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
it('should work with pages that have loaded before being connected to', async({page, context, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
@ -1047,7 +1047,7 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
|
||||
page.evaluate(() => window._popup = window.open(document.location.href)),
|
||||
]);
|
||||
expect(popup.url()).toBe(server.EMPTY_PAGE);
|
||||
await popup.mainFrame()._waitForLoadState();
|
||||
await popup.waitForLoadState();
|
||||
expect(popup.url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
});
|
||||
@ -1171,7 +1171,7 @@ module.exports.describe = function({testRunner, expect, playwright, MAC, WIN, FF
|
||||
await frame.goto(server.PREFIX + '/one-style.html', {waitUntil: 'domcontentloaded'});
|
||||
const request = await requestPromise;
|
||||
let resolved = false;
|
||||
const loadPromise = frame._waitForLoadState().then(() => resolved = true);
|
||||
const loadPromise = frame.waitForLoadState().then(() => resolved = true);
|
||||
// give the promise a chance to resolve, even though it shouldn't
|
||||
await page.evaluate('1');
|
||||
expect(resolved).toBe(false);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user