playwright/docs/loading.md

129 lines
4.7 KiB
Markdown
Raw Normal View History

# Navigation and loading
Playwright logically splits the process of showing a new document in the page into **navigation** and **loading**.
<!-- GEN:toc-top-level -->
- [Navigation](#navigation)
- [Loading](#loading)
- [Common scenarios](#common-scenarios)
- [Loading a popup](#loading-a-popup)
<!-- GEN:stop -->
2020-05-03 21:16:52 +03:00
## Navigation
2020-04-17 01:02:17 +03:00
Page navigation can be either initiated by the Playwright call:
```js
// Load a page
await page.goto('https://example.com');
2020-04-17 01:02:17 +03:00
// Reload a page
await page.reload();
2020-04-17 01:02:17 +03:00
// Click a link
await page.click('text="Continue"');
2020-04-17 01:02:17 +03:00
```
or by the page itself:
```js
// Programmatic navigation
window.location.href = 'https://example.com';
2020-04-17 01:02:17 +03:00
// Single page app navigation
history.pushState({}, 'title', '#deep-link');
2020-04-17 01:02:17 +03:00
```
Navigation intent may result in being canceled, for example transformed into a download or hitting an unresolved DNS address. Only when the navigation succeeds, page starts **loading** the document.
2020-05-03 21:16:52 +03:00
## Loading
2020-04-17 01:02:17 +03:00
Page load takes time retrieving the response body over the network, parsing, executing the scripts and firing the events. Typical load scenario goes through the following load states:
- [`page.url()`](api.md#pageurl) is set to the new url
- document content is loaded over network and parsed
- [`domcontentloaded`](api.md#event-domcontentloaded) event is fired
- page executes some scripts and loads resources like stylesheets and images
- [`load`](api.md#event-load) event is fired
- page executes dynamically loaded scripts
- `networkidle` is fired - no new network requests made for at least `500` ms
2020-05-03 21:16:52 +03:00
## Common scenarios
2020-04-17 01:02:17 +03:00
By default, Playwright handles navigations seamlessly so that you did not need to think about them. Consider the following scenario, where everything is handled by Playwright behind the scenes:
```js
await page.goto('http://example.com');
2020-04-17 01:02:17 +03:00
// If the page does a client-side redirect to 'http://example.com/login'.
// Playwright will automatically wait for the login page to load.
// Playwright waits for the lazy loaded #username and #password inputs
// to appear before filling the values.
await page.fill('#username', 'John Doe');
await page.fill('#password', '********');
// Playwright waits for the login button to become enabled and clicks it.
2020-04-17 01:02:17 +03:00
await page.click('text=Login');
// Clicking the button navigates to the logged-in page and Playwright
// automatically waits for that.
```
2020-04-17 01:02:17 +03:00
Explicit loading handling may be required for more complicated scenarios though.
2020-05-03 21:16:52 +03:00
## Loading a popup
When popup is opened, explicitly calling [`page.waitForLoadState()`](api.md#pagewaitforloadstatestate-options) ensures that popup is loaded to the desired state.
```js
const [ popup ] = await Promise.all([
2020-04-17 01:02:17 +03:00
page.waitForEvent('popup'),
page.click('a[target="_blank"]'), // <-- opens popup
2020-04-17 01:02:17 +03:00
]);
await popup.waitForLoadState('load');
await popup.evaluate(() => window.globalVariableInitializedByOnLoadHandler);
```
### Unusual client-side redirects
Usually, the client-side redirect happens before the `load` event, and `page.goto()` method automatically waits for the redirect. However, when redirecting from a link click or after the `load` event, it would be easier to explicitly [`waitForNavigation()`](api.md#pagewaitfornavigationoptions) to a specific url.
```js
await Promise.all([
page.waitForNavigation({ url: '**/login' }),
2020-04-17 01:02:17 +03:00
page.click('a'), // Triggers a navigation with a script redirect.
]);
```
2020-04-17 01:02:17 +03:00
Notice the `Promise.all` to click and wait for navigation. Awaiting these methods one after the other is racy, because navigation could happen too fast.
### Click triggers navigation after a timeout
When `onclick` handler triggers a navigation from a `setTimeout`, use an explicit [`waitForNavigation()`](api.md#pagewaitfornavigationoptions) call as a last resort.
```js
await Promise.all([
page.waitForNavigation(), // Waits for the next navigation.
2020-04-17 01:02:17 +03:00
page.click('a'), // Triggers a navigation after a timeout.
]);
```
2020-04-17 01:02:17 +03:00
Notice the `Promise.all` to click and wait for navigation. Awaiting these methods one after the other is racy, because navigation could happen too fast.
### Unpredictable patterns
When the page has a complex loading pattern, the custom waiting function is most reliable.
```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();
```
When clicking on a button triggers some asynchronous processing, issues a couple GET requests and pushes a new history state multiple times, explicit [`waitForNavigation()`](api.md#pagewaitfornavigationoptions) to a specific url is the most reliable option.
```js
await Promise.all([
page.waitForNavigation({ url: '**/invoice#processed' }),
2020-04-17 01:02:17 +03:00
page.click('text=Process the invoice'), // Triggers some complex handling.
]);
```
2020-04-17 01:02:17 +03:00
### Lazy loading, hydration
TBD