chore: update navigation and timers docs (#22941)
This commit is contained in:
parent
4d41535081
commit
e41b21dc7b
|
|
@ -2243,6 +2243,8 @@ class FrameExamples
|
||||||
|
|
||||||
## async method: Frame.waitForTimeout
|
## async method: Frame.waitForTimeout
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
|
* discouraged: Never wait for timeout in production. Tests that wait for time are
|
||||||
|
inherently flaky. Use [Locator] actions and web assertions that wait automatically.
|
||||||
|
|
||||||
Waits for the given [`param: timeout`] in milliseconds.
|
Waits for the given [`param: timeout`] in milliseconds.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,12 @@ Math.random = () => 42;
|
||||||
await page.addInitScript({ path: './preload.js' });
|
await page.addInitScript({ path: './preload.js' });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
await page.addInitScript(mock => {
|
||||||
|
window.mock = mock;
|
||||||
|
}, mock);
|
||||||
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// In your playwright script, assuming the preload.js file is in same directory
|
// In your playwright script, assuming the preload.js file is in same directory
|
||||||
page.addInitScript(Paths.get("./preload.js"));
|
page.addInitScript(Paths.get("./preload.js"));
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,152 @@ id: navigations
|
||||||
title: "Navigations"
|
title: "Navigations"
|
||||||
---
|
---
|
||||||
|
|
||||||
Playwright can navigate to URLs and handle navigations caused by page interactions. This guide covers common scenarios to wait for page navigations and loading to complete.
|
Playwright can navigate to URLs and handle navigations caused by the page interactions.
|
||||||
|
|
||||||
## Navigation lifecycle
|
## Basic navigation
|
||||||
|
|
||||||
|
Simplest form of a navigation is opening a URL:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Navigate the page
|
||||||
|
await page.goto('https://example.com');
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Navigate the page
|
||||||
|
page.navigate("https://example.com");
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
# Navigate the page
|
||||||
|
await page.goto("https://example.com")
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
# Navigate the page
|
||||||
|
page.goto("https://example.com")
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Navigate the page
|
||||||
|
await page.GotoAsync("https://example.com");
|
||||||
|
```
|
||||||
|
|
||||||
|
The code above loads the page and waits for the web page to fire the
|
||||||
|
[load](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event) event.
|
||||||
|
The load event is fired when the whole page has loaded, including all dependent
|
||||||
|
resources such as stylesheets, scripts, iframes, and images.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If the page does a client-side redirect before `load`, [`method: Page.goto`] will
|
||||||
|
wait for the redirected page to fire the `load` event.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## When is the page loaded?
|
||||||
|
|
||||||
|
Modern pages perform numerous activities after the `load` event was fired. They
|
||||||
|
fetch data lazily, populate UI, load expensive resources, scripts and styles after
|
||||||
|
the `load` event was fired. There is no way to tell that the page is `loaded`,
|
||||||
|
it depends on the page, framework, etc. So when can you start interacting with
|
||||||
|
it?
|
||||||
|
|
||||||
|
In Playwright you can interact with the page at any moment. It will automatically
|
||||||
|
wait for the target elements to become [actionable](./actionability.md).
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Navigate and click element
|
||||||
|
// Click will auto-wait for the element
|
||||||
|
await page.goto('https://example.com');
|
||||||
|
await page.getByText('Example Domain').click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Navigate and click element
|
||||||
|
// Click will auto-wait for the element
|
||||||
|
page.navigate("https://example.com");
|
||||||
|
page.getByText("Example Domain").click();
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
# Navigate and click element
|
||||||
|
# Click will auto-wait for the element
|
||||||
|
await page.goto("https://example.com")
|
||||||
|
await page.get_by_text("example domain").click()
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
# Navigate and click element
|
||||||
|
# Click will auto-wait for the element
|
||||||
|
page.goto("https://example.com")
|
||||||
|
page.get_by_text("example domain").click()
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Navigate and click element
|
||||||
|
// Click will auto-wait for the element
|
||||||
|
await page.GotoAsync("https://example.com");
|
||||||
|
await page.GetByText("Example Domain").ClickAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
For the scenario above, Playwright will wait for the text to become visible,
|
||||||
|
will wait for the rest of the actionability checks to pass for that element,
|
||||||
|
and will click it.
|
||||||
|
|
||||||
|
Playwright operates as a very fast user - the moment it sees the button, it
|
||||||
|
clicks it. In the general case, you don't need to worry about whether all the
|
||||||
|
resources loaded, etc.
|
||||||
|
|
||||||
|
## Hydration
|
||||||
|
|
||||||
|
At some point in time, you'll stumble upon a use case where Playwright performs
|
||||||
|
an action, but nothing seemingly happens. Or you enter some text into the input
|
||||||
|
field and will disappear. The most probable reason behind that is a poor page
|
||||||
|
[hydration](https://en.wikipedia.org/wiki/Hydration_(web_development)).
|
||||||
|
|
||||||
|
When page is hydrated, first, a static version of the page is sent to the browser.
|
||||||
|
Then the dynamic part is sent and the page becomes "live". As a very fast user,
|
||||||
|
Playwright will start interacting with the page the moment it sees it. And if
|
||||||
|
the button on a page is enabled, but the listeners have not yet been added,
|
||||||
|
Playwright will do its job, but the click won't have any effect.
|
||||||
|
|
||||||
|
A simple way to verify if your page suffers from a poor hydration is to open Chrome
|
||||||
|
DevTools, pick "Slow 3G" network emulation in the Network panel and reload the page.
|
||||||
|
Once you see the element of interest, interact with it. You'll see that the button
|
||||||
|
clicks will be ignored and the entered text will be reset by the subsequent page
|
||||||
|
load code. The right fix for this issue is to make sure that all the interactive
|
||||||
|
controls are disabled until after the hydration, when the page is fully functional.
|
||||||
|
|
||||||
|
## Waiting for navigation
|
||||||
|
|
||||||
|
Clicking an element could trigger multiple navigations. In these cases, it is
|
||||||
|
recommended to explicitly [`method: Page.waitForURL`] to a specific url.
|
||||||
|
|
||||||
|
```js
|
||||||
|
await page.getByText('Click me').click();
|
||||||
|
await page.waitForURL('**/login');
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
page.getByText("Click me").click();
|
||||||
|
page.waitForURL("**/login");
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
await page.get_by_text("Click me").click()
|
||||||
|
await page.wait_for_url("**/login")
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
page.get_by_text("Click me").click()
|
||||||
|
page.wait_for_url("**/login")
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
await page.GetByText("Click me").ClickAsync();
|
||||||
|
await page.WaitForURL("**/login");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Navigation events
|
||||||
|
|
||||||
Playwright splits the process of showing a new document in a page into **navigation** and **loading**.
|
Playwright splits the process of showing a new document in a page into **navigation** and **loading**.
|
||||||
|
|
||||||
|
|
@ -23,391 +166,3 @@ events:
|
||||||
- page executes some scripts and loads resources like stylesheets and images
|
- page executes some scripts and loads resources like stylesheets and images
|
||||||
- [`event: Page.load`] event is fired
|
- [`event: Page.load`] event is fired
|
||||||
- page executes dynamically loaded scripts
|
- page executes dynamically loaded scripts
|
||||||
|
|
||||||
## Scenarios initiated by browser UI
|
|
||||||
|
|
||||||
Navigations can be initiated by changing the URL bar, reloading the page or going back or forward in session history.
|
|
||||||
|
|
||||||
### Auto-wait
|
|
||||||
|
|
||||||
Navigating to a URL auto-waits for the page to fire the `load` event. If the page does a client-side redirect before
|
|
||||||
`load`, [`method: Page.goto`] will auto-wait for the redirected page to fire the `load` event.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Navigate the page
|
|
||||||
await page.goto('https://example.com');
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Navigate the page
|
|
||||||
page.navigate("https://example.com");
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Navigate the page
|
|
||||||
await page.goto("https://example.com")
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Navigate the page
|
|
||||||
page.goto("https://example.com")
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Navigate the page
|
|
||||||
await page.GotoAsync("https://example.com");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Wait for element
|
|
||||||
|
|
||||||
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Locator.waitFor`].
|
|
||||||
Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Navigate and wait for element
|
|
||||||
await page.goto('https://example.com');
|
|
||||||
await page.getByText('Example Domain').waitFor();
|
|
||||||
|
|
||||||
// Navigate and click element
|
|
||||||
// Click will auto-wait for the element
|
|
||||||
await page.goto('https://example.com');
|
|
||||||
await page.getByText('Example Domain').click();
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Navigate and wait for element
|
|
||||||
page.navigate("https://example.com");
|
|
||||||
page.getByText("Example Domain").waitFor();
|
|
||||||
|
|
||||||
// Navigate and click element
|
|
||||||
// Click will auto-wait for the element
|
|
||||||
page.navigate("https://example.com");
|
|
||||||
page.getByText("Example Domain").click();
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Navigate and wait for element
|
|
||||||
await page.goto("https://example.com")
|
|
||||||
await page.get_by_text("example domain").wait_for()
|
|
||||||
|
|
||||||
# Navigate and click element
|
|
||||||
# Click will auto-wait for the element
|
|
||||||
await page.goto("https://example.com")
|
|
||||||
await page.get_by_text("example domain").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Navigate and wait for element
|
|
||||||
page.goto("https://example.com")
|
|
||||||
page.get_by_text("example domain").wait_for()
|
|
||||||
|
|
||||||
# Navigate and click element
|
|
||||||
# Click will auto-wait for the element
|
|
||||||
page.goto("https://example.com")
|
|
||||||
page.get_by_text("example domain").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Navigate and wait for element
|
|
||||||
await page.GotoAsync("https://example.com");
|
|
||||||
await page.GetByText("Example Domain").WaitForAsync();
|
|
||||||
|
|
||||||
// Navigate and click element
|
|
||||||
// Click will auto-wait for the element
|
|
||||||
await page.GotoAsync("https://example.com");
|
|
||||||
await page.GetByText("Example Domain").ClickAsync();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Scenarios initiated by page interaction
|
|
||||||
|
|
||||||
In the scenarios below, [`method: Locator.click`] initiates a navigation and then waits for the navigation to complete.
|
|
||||||
|
|
||||||
### Auto-wait
|
|
||||||
|
|
||||||
By default, [`method: Locator.click`] will wait for the navigation step to complete. This can be combined with a page interaction on the navigated page which would auto-wait for an element.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Click will auto-wait for navigation to complete
|
|
||||||
await page.getByText('Login').click();
|
|
||||||
|
|
||||||
// Fill will auto-wait for element on navigated page
|
|
||||||
await page.getByLabel('User Name').fill('John Doe');
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Click will auto-wait for navigation to complete
|
|
||||||
page.getByText("Login").click();
|
|
||||||
|
|
||||||
// Fill will auto-wait for element on navigated page
|
|
||||||
page.getByLabel("User Name").fill("John Doe");
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Click will auto-wait for navigation to complete
|
|
||||||
await page.get_by_text("Login").click()
|
|
||||||
|
|
||||||
# Fill will auto-wait for element on navigated page
|
|
||||||
await page.get_by_label("User Name").fill("John Doe")
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Click will auto-wait for navigation to complete
|
|
||||||
page.get_by_text("Login").click()
|
|
||||||
|
|
||||||
# Fill will auto-wait for element on navigated page
|
|
||||||
page.get_by_label("User Name").fill("John Doe")
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Click will auto-wait for navigation to complete
|
|
||||||
await page.GetByText("Login").ClickAsync();
|
|
||||||
|
|
||||||
// Fill will auto-wait for element on navigated page
|
|
||||||
await page.GetByLabel("User Name").FillAsync("John Doe");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Wait for element
|
|
||||||
|
|
||||||
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Locator.waitFor`].
|
|
||||||
Alternatively, page interactions like [`method: Locator.click`] auto-wait for elements.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Click will auto-wait for the element and trigger navigation
|
|
||||||
await page.getByText('Login').click();
|
|
||||||
// Wait for the element
|
|
||||||
await page.getByLabel('User Name').waitFor();
|
|
||||||
|
|
||||||
// Click triggers navigation
|
|
||||||
await page.getByText('Login').click();
|
|
||||||
// Fill will auto-wait for element
|
|
||||||
await page.getByLabel('User Name').fill('John Doe');
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Click will auto-wait for the element and trigger navigation
|
|
||||||
page.getByText("Login").click();
|
|
||||||
// Wait for the element
|
|
||||||
page.getByLabel("User Name").waitFor();
|
|
||||||
|
|
||||||
// Click triggers navigation
|
|
||||||
page.getByText("Login").click();
|
|
||||||
// Fill will auto-wait for element
|
|
||||||
page.getByLabel("User Name").fill("John Doe");
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Click will auto-wait for the element and trigger navigation
|
|
||||||
await page.get_by_text("Login").click()
|
|
||||||
# Wait for the element
|
|
||||||
await page.get_by_label("User Name").wait_for()
|
|
||||||
|
|
||||||
# Click triggers navigation
|
|
||||||
await page.get_by_text("Login").click()
|
|
||||||
# Fill will auto-wait for element
|
|
||||||
await page.get_by_label("User Name").fill("John Doe")
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Click triggers navigation
|
|
||||||
page.get_by_text("Login").click()
|
|
||||||
# Click will auto-wait for the element
|
|
||||||
page.get_by_label("User Name").wait_for()
|
|
||||||
|
|
||||||
# Click triggers navigation
|
|
||||||
page.get_by_text("Login").click()
|
|
||||||
# Fill will auto-wait for element
|
|
||||||
page.get_by_label("User Name").fill("John Doe")
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Click will auto-wait for the element and trigger navigation
|
|
||||||
await page.GetByText("Login").ClickAsync();
|
|
||||||
// Wait for the element
|
|
||||||
await page.GetByLabel("User Name").WaitForAsync();
|
|
||||||
|
|
||||||
// Click triggers navigation
|
|
||||||
await page.GetByText("Login").ClickAsync();
|
|
||||||
// Fill will auto-wait for element
|
|
||||||
await page.GetByLabel("User Name").FillAsync("John Doe");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Asynchronous navigation
|
|
||||||
|
|
||||||
Clicking an element could trigger asynchronous processing before initiating the navigation. In these cases, it is
|
|
||||||
recommended to explicitly call [`method: Page.waitForNavigation`]. For example:
|
|
||||||
* Navigation is triggered from a `setTimeout`
|
|
||||||
* Page waits for network requests before navigation
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Start waiting for navigation before clicking. Note no await.
|
|
||||||
const navigationPromise = page.waitForNavigation();
|
|
||||||
await page.getByText('Navigate after timeout').click();
|
|
||||||
await navigationPromise;
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Using waitForNavigation with a callback prevents a race condition
|
|
||||||
// between clicking and waiting for a navigation.
|
|
||||||
page.waitForNavigation(() -> { // Waits for the next navigation
|
|
||||||
page.getByText("Navigate after timeout").click(); // Triggers a navigation after a timeout
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Waits for the next navigation. Using Python context manager
|
|
||||||
# prevents a race condition between clicking and waiting for a navigation.
|
|
||||||
async with page.expect_navigation():
|
|
||||||
# Triggers a navigation after a timeout
|
|
||||||
await page.get_by_text("Navigate after timeout").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Waits for the next navigation. Using Python context manager
|
|
||||||
# prevents a race condition between clicking and waiting for a navigation.
|
|
||||||
with page.expect_navigation():
|
|
||||||
# Triggers a navigation after a timeout
|
|
||||||
page.get_by_text("Navigate after timeout").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Using waitForNavigation with a callback prevents a race condition
|
|
||||||
// between clicking and waiting for a navigation.
|
|
||||||
await page.RunAndWaitForNavigationAsync(async () =>
|
|
||||||
{
|
|
||||||
// Triggers a navigation after a timeout
|
|
||||||
await page.GetByText("Navigate after timeout").ClickAsync();
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple navigations
|
|
||||||
|
|
||||||
Clicking an element could trigger multiple navigations. In these cases, it is recommended to explicitly
|
|
||||||
[`method: Page.waitForNavigation`] to a specific url. For example:
|
|
||||||
* Client-side redirects issued after the `load` event
|
|
||||||
* Multiple pushes to history state
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Start waiting for navigation before clicking. Note no await.
|
|
||||||
const navigationPromise = page.waitForNavigation({ url: '**/login' });
|
|
||||||
// This action triggers the navigation with a script redirect.
|
|
||||||
await page.getByText('Click me').click();
|
|
||||||
await navigationPromise;
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// Running action in the callback of waitForNavigation prevents a race
|
|
||||||
// condition between clicking and waiting for a navigation.
|
|
||||||
page.waitForNavigation(new Page.WaitForNavigationOptions().setUrl("**/login"), () -> {
|
|
||||||
page.getByText("Click me").click(); // Triggers a navigation with a script redirect
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
# Using Python context manager prevents a race condition
|
|
||||||
# between clicking and waiting for a navigation.
|
|
||||||
async with page.expect_navigation(url="**/login"):
|
|
||||||
# Triggers a navigation with a script redirect
|
|
||||||
await page.get_by_text("Click me").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
# Using Python context manager prevents a race condition
|
|
||||||
# between clicking and waiting for a navigation.
|
|
||||||
with page.expect_navigation(url="**/login"):
|
|
||||||
# Triggers a navigation with a script redirect
|
|
||||||
page.get_by_text("Click me").click()
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// Running action in the callback of waitForNavigation prevents a race
|
|
||||||
// condition between clicking and waiting for a navigation.
|
|
||||||
await page.RunAndWaitForNavigationAsync(async () =>
|
|
||||||
{
|
|
||||||
// Triggers a navigation with a script redirect.
|
|
||||||
await page.GetByText("Click me").ClickAsync();
|
|
||||||
}, new()
|
|
||||||
{
|
|
||||||
UrlString = "**/login"
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Loading a popup
|
|
||||||
|
|
||||||
When popup is opened, explicitly calling [`method: Page.waitForLoadState`] ensures that popup is loaded to the desired state.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Start waiting for popup before clicking. Note no await.
|
|
||||||
const popupPromise = page.waitForEvent('popup');
|
|
||||||
await page.getByText('Open popup').click();
|
|
||||||
const popup = await popupPromise;
|
|
||||||
// Wait for the popup to load.
|
|
||||||
await popup.waitForLoadState('load');
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
Page popup = page.waitForPopup(() -> {
|
|
||||||
page.getByText("Open popup").click(); // Opens popup
|
|
||||||
});
|
|
||||||
popup.waitForLoadState(LoadState.LOAD);
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
async with page.expect_popup() as popup_info:
|
|
||||||
await page.get_by_text("Open popup").click() # Opens popup
|
|
||||||
popup = await popup_info.value
|
|
||||||
await popup.wait_for_load_state("load")
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
with page.expect_popup() as popup_info:
|
|
||||||
page.get_by_text("Open popup").click() # Opens popup
|
|
||||||
popup = popup_info.value
|
|
||||||
popup.wait_for_load_state("load")
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var popup = await page.RunAndWaitForPopupAsync(async () =>
|
|
||||||
{
|
|
||||||
await page.GetByText("Open popup").ClickAsync(); // Opens popup
|
|
||||||
});
|
|
||||||
popup.WaitForLoadStateAsync(LoadState.Load);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Advanced patterns
|
|
||||||
|
|
||||||
For pages that have complicated loading patterns, [`method: Page.waitForFunction`] is a powerful and extensible approach to define a custom wait criteria.
|
|
||||||
|
|
||||||
```js
|
|
||||||
await page.goto('http://example.com');
|
|
||||||
await page.waitForFunction(() => window.amILoadedYet());
|
|
||||||
// Ready to take a screenshot, according to the page itself.
|
|
||||||
await page.screenshot();
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
page.navigate("http://example.com");
|
|
||||||
page.waitForFunction("() => window.amILoadedYet()");
|
|
||||||
// Ready to take a screenshot, according to the page itself.
|
|
||||||
page.screenshot();
|
|
||||||
```
|
|
||||||
|
|
||||||
```python async
|
|
||||||
await page.goto("http://example.com")
|
|
||||||
await page.wait_for_function("() => window.amILoadedYet()")
|
|
||||||
# Ready to take a screenshot, according to the page itself.
|
|
||||||
await page.screenshot()
|
|
||||||
```
|
|
||||||
|
|
||||||
```python sync
|
|
||||||
page.goto("http://example.com")
|
|
||||||
page.wait_for_function("() => window.amILoadedYet()")
|
|
||||||
# Ready to take a screenshot, according to the page itself.
|
|
||||||
page.screenshot()
|
|
||||||
```
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
await page.GotoAsync("http://example.com");
|
|
||||||
await page.WaitForFunctionAsync("() => window.amILoadedYet()");
|
|
||||||
// Ready to take a screenshot, according to the page itself.
|
|
||||||
await page.ScreenshotAsync();
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,6 @@ Playwright Test has multiple configurable timeouts for various tasks.
|
||||||
|:----------|:----------------|:--------------------------------|
|
|:----------|:----------------|:--------------------------------|
|
||||||
|Test timeout|30000 ms|Timeout for each test, includes test, hooks and fixtures:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { timeout: 60000 }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`test.setTimeout(120000)` |
|
|Test timeout|30000 ms|Timeout for each test, includes test, hooks and fixtures:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { timeout: 60000 }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`test.setTimeout(120000)` |
|
||||||
|Expect timeout|5000 ms|Timeout for each assertion:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { expect: { timeout: 10000 } }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`expect(locator).toBeVisible({ timeout: 10000 })` |
|
|Expect timeout|5000 ms|Timeout for each assertion:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { expect: { timeout: 10000 } }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`expect(locator).toBeVisible({ timeout: 10000 })` |
|
||||||
|Action timeout| no timeout |Timeout for each action:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { use: { actionTimeout: 10000 } }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`locator.click({ timeout: 10000 })` |
|
|
||||||
|Navigation timeout| no timeout |Timeout for each navigation action:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { use: { navigationTimeout: 30000 } }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`page.goto('/', { timeout: 30000 })` |
|
|
||||||
|Global timeout|no timeout |Global timeout for the whole test run:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set in config</span><br/>`config = { globalTimeout: 60*60*1000 }`<br/> |
|
|
||||||
|`beforeAll`/`afterAll` timeout|30000 ms|Timeout for the hook:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set in hook</span><br/>`test.setTimeout(60000)`<br/> |
|
|
||||||
|Fixture timeout|no timeout |Timeout for an individual fixture:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set in fixture</span><br/>`{ scope: 'test', timeout: 30000 }`<br/> |
|
|
||||||
|
|
||||||
## Test timeout
|
## Test timeout
|
||||||
|
|
||||||
|
|
@ -115,7 +110,44 @@ export default defineConfig({
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
API reference: [`property: TestConfig.expect`].
|
## Global timeout
|
||||||
|
|
||||||
|
Playwright Test supports a timeout for the whole test run. This prevents excess resource usage when everything went wrong. There is no default global timeout, but you can set a reasonable one in the config, for example one hour. Global timeout produces the following error:
|
||||||
|
|
||||||
|
```
|
||||||
|
Running 1000 tests using 10 workers
|
||||||
|
|
||||||
|
514 skipped
|
||||||
|
486 passed
|
||||||
|
Timed out waiting 3600s for the entire test run
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set global timeout in the config.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// playwright.config.ts
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
globalTimeout: 60 * 60 * 1000,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
API reference: [`property: TestConfig.globalTimeout`].
|
||||||
|
|
||||||
|
## Advanced: low level timeouts
|
||||||
|
|
||||||
|
These are the low-level timeouts that are pre-configured by the test runner, you should not need to change these.
|
||||||
|
If you happen to be in this section because your test are flaky, it is very likely that you should be looking for the solution elsewhere.
|
||||||
|
|
||||||
|
|Timeout |Default |Description |
|
||||||
|
|:----------|:----------------|:--------------------------------|
|
||||||
|
|Action timeout| no timeout |Timeout for each action:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { use: { actionTimeout: 10000 } }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`locator.click({ timeout: 10000 })` |
|
||||||
|
|Navigation timeout| no timeout |Timeout for each navigation action:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set default</span><br/><code>{`config = { use: { navigationTimeout: 30000 } }`}</code><br/><span style={{textTransform: 'uppercase',fontSize: 'smaller', fontWeight: 'bold', opacity: '0.6'}}>Override</span><br/>`page.goto('/', { timeout: 30000 })` |
|
||||||
|
|Global timeout|no timeout |Global timeout for the whole test run:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set in config</span><br/>`config = { globalTimeout: 60*60*1000 }`<br/> |
|
||||||
|
|`beforeAll`/`afterAll` timeout|30000 ms|Timeout for the hook:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set in hook</span><br/>`test.setTimeout(60000)`<br/> |
|
||||||
|
|Fixture timeout|no timeout |Timeout for an individual fixture:<br/><span style={{textTransform:'uppercase',fontSize:'smaller',fontWeight:'bold',opacity:'0.6'}}>Set in fixture</span><br/>`{ scope: 'test', timeout: 30000 }`<br/> |
|
||||||
|
|
||||||
|
|
||||||
### Set timeout for a single assertion
|
### Set timeout for a single assertion
|
||||||
|
|
||||||
|
|
@ -126,22 +158,6 @@ test('basic test', async ({ page }) => {
|
||||||
await expect(page.getByRole('button')).toHaveText('Sign in', { timeout: 10000 });
|
await expect(page.getByRole('button')).toHaveText('Sign in', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Action and navigation timeouts
|
|
||||||
|
|
||||||
Test usually performs some actions by calling Playwright APIs, for example `locator.click()`. These actions do not have a timeout by default, but you can set one. Action that timed out produces the following error:
|
|
||||||
|
|
||||||
```
|
|
||||||
example.spec.ts:3:1 › basic test ===========================
|
|
||||||
|
|
||||||
locator.click: Timeout 1000ms exceeded.
|
|
||||||
=========================== logs ===========================
|
|
||||||
waiting for "locator('button')"
|
|
||||||
============================================================
|
|
||||||
```
|
|
||||||
|
|
||||||
Playwright also allows to set a separate timeout for navigation actions like `page.goto()` because loading a page is usually slower.
|
|
||||||
|
|
||||||
### Set action and navigation timeouts in the config
|
### Set action and navigation timeouts in the config
|
||||||
|
|
||||||
```js title="playwright.config.ts"
|
```js title="playwright.config.ts"
|
||||||
|
|
@ -168,30 +184,6 @@ test('basic test', async ({ page }) => {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Global timeout
|
|
||||||
|
|
||||||
Playwright Test supports a timeout for the whole test run. This prevents excess resource usage when everything went wrong. There is no default global timeout, but you can set a reasonable one in the config, for example one hour. Global timeout produces the following error:
|
|
||||||
|
|
||||||
```
|
|
||||||
Running 1000 tests using 10 workers
|
|
||||||
|
|
||||||
514 skipped
|
|
||||||
486 passed
|
|
||||||
Timed out waiting 3600s for the entire test run
|
|
||||||
```
|
|
||||||
|
|
||||||
You can set global timeout in the config.
|
|
||||||
|
|
||||||
```js title="playwright.config.ts"
|
|
||||||
import { defineConfig } from '@playwright/test';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
globalTimeout: 60 * 60 * 1000,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
API reference: [`property: TestConfig.globalTimeout`].
|
|
||||||
|
|
||||||
## Fixture timeout
|
## Fixture timeout
|
||||||
|
|
||||||
By default, [fixture](./test-fixtures) shares timeout with the test. However, for slow fixtures, especially [worker-scoped](./test-fixtures#worker-scoped-fixtures) ones, it is convenient to have a separate timeout. This way you can keep the overall test timeout small, and give the slow fixture more time.
|
By default, [fixture](./test-fixtures) shares timeout with the test. However, for slow fixtures, especially [worker-scoped](./test-fixtures#worker-scoped-fixtures) ones, it is convenient to have a separate timeout. This way you can keep the overall test timeout small, and give the slow fixture more time.
|
||||||
|
|
|
||||||
9
packages/playwright-core/types/types.d.ts
vendored
9
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -276,6 +276,12 @@ export interface Page {
|
||||||
* await page.addInitScript({ path: './preload.js' });
|
* await page.addInitScript({ path: './preload.js' });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* ```js
|
||||||
|
* await page.addInitScript(mock => {
|
||||||
|
* window.mock = mock;
|
||||||
|
* }, mock);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* **NOTE** The order of evaluation of multiple scripts installed via
|
* **NOTE** The order of evaluation of multiple scripts installed via
|
||||||
* [browserContext.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script)
|
* [browserContext.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script)
|
||||||
* and [page.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-page#page-add-init-script) is not
|
* and [page.addInitScript(script[, arg])](https://playwright.dev/docs/api/class-page#page-add-init-script) is not
|
||||||
|
|
@ -7289,6 +7295,9 @@ export interface Frame {
|
||||||
}): Promise<null|Response>;
|
}): Promise<null|Response>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* **NOTE** Never wait for timeout in production. Tests that wait for time are inherently flaky. Use [Locator] actions and web
|
||||||
|
* assertions that wait automatically.
|
||||||
|
*
|
||||||
* Waits for the given `timeout` in milliseconds.
|
* Waits for the given `timeout` in milliseconds.
|
||||||
*
|
*
|
||||||
* Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going
|
* Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue