feat(waitForURL): add a new waitForURL api (#6010)
This commit is contained in:
Родитель
98f1f715c5
Коммит
85ab1dc7a4
|
@ -1395,3 +1395,31 @@ be flaky. Use signals such as network events, selectors becoming visible and oth
|
|||
- `timeout` <[float]>
|
||||
|
||||
A timeout to wait for
|
||||
|
||||
## async method: Frame.waitForURL
|
||||
|
||||
Waits for the frame to navigate to the given URL.
|
||||
|
||||
```js
|
||||
await frame.click('a.delayed-navigation'); // Clicking the link will indirectly cause a navigation
|
||||
await frame.waitForURL('**/target.html');
|
||||
```
|
||||
|
||||
```java
|
||||
frame.click("a.delayed-navigation"); // Clicking the link will indirectly cause a navigation
|
||||
frame.waitForURL("**/target.html");
|
||||
```
|
||||
|
||||
```python async
|
||||
await frame.click("a.delayed-navigation") # clicking the link will indirectly cause a navigation
|
||||
await frame.wait_for_url("**/target.html")
|
||||
```
|
||||
|
||||
```python sync
|
||||
frame.click("a.delayed-navigation") # clicking the link will indirectly cause a navigation
|
||||
frame.wait_for_url("**/target.html")
|
||||
```
|
||||
|
||||
### param: Frame.waitForURL.url = %%-wait-for-navigation-url-%%
|
||||
### option: Frame.waitForURL.timeout = %%-navigation-timeout-%%
|
||||
### option: Frame.waitForURL.waitUntil = %%-navigation-wait-until-%%
|
||||
|
|
|
@ -2274,6 +2274,7 @@ This setting will change the default maximum navigation time for the following m
|
|||
* [`method: Page.reload`]
|
||||
* [`method: Page.setContent`]
|
||||
* [`method: Page.waitForNavigation`]
|
||||
* [`method: Page.waitForURL`]
|
||||
|
||||
:::note
|
||||
[`method: Page.setDefaultNavigationTimeout`] takes priority over [`method: Page.setDefaultTimeout`],
|
||||
|
@ -3109,6 +3110,36 @@ Shortcut for main frame's [`method: Frame.waitForTimeout`].
|
|||
|
||||
A timeout to wait for
|
||||
|
||||
## async method: Page.waitForURL
|
||||
|
||||
Waits for the main frame to navigate to the given URL.
|
||||
|
||||
```js
|
||||
await page.click('a.delayed-navigation'); // Clicking the link will indirectly cause a navigation
|
||||
await page.waitForURL('**/target.html');
|
||||
```
|
||||
|
||||
```java
|
||||
page.click("a.delayed-navigation"); // Clicking the link will indirectly cause a navigation
|
||||
page.waitForURL("**/target.html");
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.click("a.delayed-navigation") # clicking the link will indirectly cause a navigation
|
||||
await page.wait_for_url("**/target.html")
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.click("a.delayed-navigation") # clicking the link will indirectly cause a navigation
|
||||
page.wait_for_url("**/target.html")
|
||||
```
|
||||
|
||||
Shortcut for main frame's [`method: Frame.waitForURL`].
|
||||
|
||||
### param: Page.waitForURL.url = %%-wait-for-navigation-url-%%
|
||||
### option: Page.waitForURL.timeout = %%-navigation-timeout-%%
|
||||
### option: Page.waitForURL.waitUntil = %%-navigation-wait-until-%%
|
||||
|
||||
## async method: Page.waitForWebSocket
|
||||
* langs: csharp, java
|
||||
- returns: <[WebSocket]>
|
||||
|
|
|
@ -159,6 +159,12 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
|
|||
});
|
||||
}
|
||||
|
||||
async waitForURL(url: URLMatch, options: { waitUntil?: LifecycleEvent, timeout?: number } = {}): Promise<void> {
|
||||
if (urlMatches(this.url(), url))
|
||||
return await this.waitForLoadState(options?.waitUntil, options);
|
||||
await this.waitForNavigation({ url, ...options });
|
||||
}
|
||||
|
||||
async frameElement(): Promise<ElementHandle> {
|
||||
return this._wrapApiCall(this._apiName('frameElement'), async (channel: channels.FrameChannel) => {
|
||||
return ElementHandle.from((await channel.frameElement()).element);
|
||||
|
|
|
@ -362,6 +362,10 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
return this._attributeToPage(() => this._mainFrame.waitForNavigation(options));
|
||||
}
|
||||
|
||||
async waitForURL(url: URLMatch, options?: { waitUntil?: LifecycleEvent, timeout?: number }): Promise<void> {
|
||||
return this._attributeToPage(() => this._mainFrame.waitForURL(url, options));
|
||||
}
|
||||
|
||||
async waitForRequest(urlOrPredicate: string | RegExp | ((r: Request) => boolean), options: { timeout?: number } = {}): Promise<Request> {
|
||||
return this._wrapApiCall('page.waitForRequest', async (channel: channels.PageChannel) => {
|
||||
const predicate = (request: Request) => {
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from '../fixtures';
|
||||
|
||||
it('should work', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(url => window.location.href = url, server.PREFIX + '/grid.html');
|
||||
await page.waitForURL('**/grid.html');
|
||||
});
|
||||
|
||||
it('should respect timeout', async ({page, server}) => {
|
||||
const promise = page.waitForURL('**/frame.html', { timeout: 2500 });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const error = await promise.catch(e => e);
|
||||
expect(error.message).toContain('page.waitForNavigation: Timeout 2500ms exceeded.');
|
||||
});
|
||||
|
||||
it('should work with both domcontentloaded and load', async ({page, server}) => {
|
||||
let response = null;
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html');
|
||||
const domContentLoadedPromise = page.waitForURL('**/one-style.html', { waitUntil: 'domcontentloaded' });
|
||||
let bothFired = false;
|
||||
const bothFiredPromise = Promise.all([
|
||||
page.waitForURL('**/one-style.html', { waitUntil: 'load' }),
|
||||
domContentLoadedPromise
|
||||
]).then(() => bothFired = true);
|
||||
|
||||
await server.waitForRequest('/one-style.css');
|
||||
await domContentLoadedPromise;
|
||||
expect(bothFired).toBe(false);
|
||||
response.end();
|
||||
await bothFiredPromise;
|
||||
await navigationPromise;
|
||||
});
|
||||
|
||||
it('should work with clicking on anchor links', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href='#foobar'>foobar</a>`);
|
||||
await page.click('a');
|
||||
await page.waitForURL('**/*#foobar');
|
||||
});
|
||||
|
||||
it('should work with history.pushState()', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`
|
||||
<a onclick='javascript:pushState()'>SPA</a>
|
||||
<script>
|
||||
function pushState() { history.pushState({}, '', 'wow.html') }
|
||||
</script>
|
||||
`);
|
||||
await page.click('a');
|
||||
await page.waitForURL('**/wow.html');
|
||||
expect(page.url()).toBe(server.PREFIX + '/wow.html');
|
||||
});
|
||||
|
||||
it('should work with history.replaceState()', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`
|
||||
<a onclick='javascript:replaceState()'>SPA</a>
|
||||
<script>
|
||||
function replaceState() { history.replaceState({}, '', '/replaced.html') }
|
||||
</script>
|
||||
`);
|
||||
await page.click('a');
|
||||
await page.waitForURL('**/replaced.html');
|
||||
expect(page.url()).toBe(server.PREFIX + '/replaced.html');
|
||||
});
|
||||
|
||||
it('should work with DOM history.back()/history.forward()', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`
|
||||
<a id=back onclick='javascript:goBack()'>back</a>
|
||||
<a id=forward onclick='javascript:goForward()'>forward</a>
|
||||
<script>
|
||||
function goBack() { history.back(); }
|
||||
function goForward() { history.forward(); }
|
||||
history.pushState({}, '', '/first.html');
|
||||
history.pushState({}, '', '/second.html');
|
||||
</script>
|
||||
`);
|
||||
expect(page.url()).toBe(server.PREFIX + '/second.html');
|
||||
|
||||
await page.click('a#back');
|
||||
await page.waitForURL('**/first.html');
|
||||
expect(page.url()).toBe(server.PREFIX + '/first.html');
|
||||
|
||||
await page.click('a#forward');
|
||||
await page.waitForURL('**/second.html');
|
||||
expect(page.url()).toBe(server.PREFIX + '/second.html');
|
||||
});
|
||||
|
||||
it('should work with url match for same document navigations', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let resolved = false;
|
||||
const waitPromise = page.waitForURL(/third\.html/).then(() => resolved = true);
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, '', '/first.html');
|
||||
});
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, '', '/second.html');
|
||||
});
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, '', '/third.html');
|
||||
});
|
||||
await waitPromise;
|
||||
expect(resolved).toBe(true);
|
||||
});
|
||||
|
||||
it('should work on frame', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
const frame = page.frames()[1];
|
||||
await frame.evaluate(url => window.location.href = url, server.PREFIX + '/grid.html');
|
||||
await frame.waitForURL('**/grid.html');
|
||||
});
|
|
@ -2531,6 +2531,7 @@ export interface Page {
|
|||
* - [page.reload([options])](https://playwright.dev/docs/api/class-page#pagereloadoptions)
|
||||
* - [page.setContent(html[, options])](https://playwright.dev/docs/api/class-page#pagesetcontenthtml-options)
|
||||
* - [page.waitForNavigation([options])](https://playwright.dev/docs/api/class-page#pagewaitfornavigationoptions)
|
||||
* - [page.waitForURL(url[, options])](https://playwright.dev/docs/api/class-page#pagewaitforurlurl-options)
|
||||
*
|
||||
* > NOTE:
|
||||
* [page.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaultnavigationtimeouttimeout)
|
||||
|
@ -3174,6 +3175,39 @@ export interface Page {
|
|||
*/
|
||||
waitForTimeout(timeout: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* Waits for the main frame to navigate to the given URL.
|
||||
*
|
||||
* ```js
|
||||
* await page.click('a.delayed-navigation'); // Clicking the link will indirectly cause a navigation
|
||||
* await page.waitForURL('**\/target.html');
|
||||
* ```
|
||||
*
|
||||
* Shortcut for main frame's
|
||||
* [frame.waitForURL(url[, options])](https://playwright.dev/docs/api/class-frame#framewaitforurlurl-options).
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param options
|
||||
*/
|
||||
waitForURL(url: string|RegExp|((url: URL) => boolean), options?: {
|
||||
/**
|
||||
* Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be
|
||||
* changed by using the
|
||||
* [browserContext.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browsercontextsetdefaultnavigationtimeouttimeout),
|
||||
* [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browsercontextsetdefaulttimeouttimeout),
|
||||
* [page.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaultnavigationtimeouttimeout)
|
||||
* or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) methods.
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* When to consider operation succeeded, defaults to `load`. Events can be either:
|
||||
* - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.
|
||||
* - `'load'` - consider operation to be finished when the `load` event is fired.
|
||||
* - `'networkidle'` - consider operation to be finished when there are no network connections for at least `500` ms.
|
||||
*/
|
||||
waitUntil?: "load"|"domcontentloaded"|"networkidle";
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* This method returns all of the dedicated [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
|
||||
* associated with the page.
|
||||
|
@ -4476,7 +4510,38 @@ export interface Frame {
|
|||
* be flaky. Use signals such as network events, selectors becoming visible and others instead.
|
||||
* @param timeout A timeout to wait for
|
||||
*/
|
||||
waitForTimeout(timeout: number): Promise<void>;}
|
||||
waitForTimeout(timeout: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* Waits for the frame to navigate to the given URL.
|
||||
*
|
||||
* ```js
|
||||
* await frame.click('a.delayed-navigation'); // Clicking the link will indirectly cause a navigation
|
||||
* await frame.waitForURL('**\/target.html');
|
||||
* ```
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param options
|
||||
*/
|
||||
waitForURL(url: string|RegExp|((url: URL) => boolean), options?: {
|
||||
/**
|
||||
* Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be
|
||||
* changed by using the
|
||||
* [browserContext.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browsercontextsetdefaultnavigationtimeouttimeout),
|
||||
* [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browsercontextsetdefaulttimeouttimeout),
|
||||
* [page.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaultnavigationtimeouttimeout)
|
||||
* or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) methods.
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* When to consider operation succeeded, defaults to `load`. Events can be either:
|
||||
* - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.
|
||||
* - `'load'` - consider operation to be finished when the `load` event is fired.
|
||||
* - `'networkidle'` - consider operation to be finished when there are no network connections for at least `500` ms.
|
||||
*/
|
||||
waitUntil?: "load"|"domcontentloaded"|"networkidle";
|
||||
}): Promise<void>;}
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter]
|
||||
|
|
Загрузка…
Ссылка в новой задаче