mirror of
https://github.com/microsoft/playwright.git
synced 2024-10-06 09:27:31 +03:00
docs: more python docs and snippets (#5021)
This commit is contained in:
parent
61b0dbb3ef
commit
e85f278869
@ -7,10 +7,10 @@ assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Scre
|
||||
Accessibility is a very platform-specific thing. On different platforms, there are different screen readers that might
|
||||
have wildly different output.
|
||||
|
||||
Blink - Chromium's rendering engine - has a concept of "accessibility tree", which is then translated into different
|
||||
platform-specific APIs. Accessibility namespace gives users access to the Blink Accessibility Tree.
|
||||
Rendering engines of Chromium, Firefox and Webkit have a concept of "accessibility tree", which is then translated into different
|
||||
platform-specific APIs. Accessibility namespace gives access to this Accessibility Tree.
|
||||
|
||||
Most of the accessibility tree gets filtered out when converting from Blink AX Tree to Platform-specific AX-Tree or by
|
||||
Most of the accessibility tree gets filtered out when converting from internal browser AX Tree to Platform-specific AX-Tree or by
|
||||
assistive technologies themselves. By default, Playwright tries to approximate this filtering, exposing only the
|
||||
"interesting" nodes of the tree.
|
||||
|
||||
@ -98,7 +98,7 @@ def find_focused_node(node):
|
||||
snapshot = await page.accessibility.snapshot()
|
||||
node = find_focused_node(snapshot)
|
||||
if node:
|
||||
print(node["name"])
|
||||
print(node["name"])
|
||||
```
|
||||
|
||||
```python sync
|
||||
@ -113,7 +113,7 @@ def find_focused_node(node):
|
||||
snapshot = page.accessibility.snapshot()
|
||||
node = find_focused_node(snapshot)
|
||||
if node:
|
||||
print(node["name"])
|
||||
print(node["name"])
|
||||
```
|
||||
|
||||
### option: Accessibility.snapshot.interestingOnly
|
||||
|
@ -1,5 +1,5 @@
|
||||
# class: Browser
|
||||
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* extends: [EventEmitter]
|
||||
|
||||
A Browser is created via [`method: BrowserType.launch`]. An example of using a [Browser] to create a [Page]:
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# class: BrowserContext
|
||||
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* extends: [EventEmitter]
|
||||
|
||||
BrowserContexts provide a way to operate multiple independent browser sessions.
|
||||
|
||||
@ -567,7 +567,7 @@ await browser.close();
|
||||
```python async
|
||||
context = await browser.new_context()
|
||||
page = await context.new_page()
|
||||
await context.route(r"(\.png$)|(\.jpg$)", lambda route => route.abort())
|
||||
await context.route(r"(\.png$)|(\.jpg$)", lambda route: route.abort())
|
||||
page = await context.new_page()
|
||||
await page.goto("https://example.com")
|
||||
await browser.close()
|
||||
@ -576,7 +576,7 @@ await browser.close()
|
||||
```python sync
|
||||
context = browser.new_context()
|
||||
page = context.new_page()
|
||||
context.route(r"(\.png$)|(\.jpg$)", lambda route => route.abort())
|
||||
context.route(r"(\.png$)|(\.jpg$)", lambda route: route.abort())
|
||||
page = await context.new_page()
|
||||
page = context.new_page()
|
||||
page.goto("https://example.com")
|
||||
|
@ -1,5 +1,5 @@
|
||||
# class: CDPSession
|
||||
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* extends: [EventEmitter]
|
||||
|
||||
The `CDPSession` instances are used to talk raw Chrome Devtools Protocol:
|
||||
* protocol methods can be called with `session.send` method.
|
||||
|
@ -1,5 +1,5 @@
|
||||
# class: Page
|
||||
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* extends: [EventEmitter]
|
||||
|
||||
Page provides methods to interact with a single tab in a [Browser], or an
|
||||
[extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One [Browser]
|
||||
|
@ -1,3 +1,22 @@
|
||||
## async method: Playwright.start
|
||||
* langs: python
|
||||
|
||||
Starts a new instance of Playwright without using the Python context manager. This is useful in REPL applications. Requires [`method: Playwright.stop`] to be called to cleanup resources.
|
||||
|
||||
```py
|
||||
>>> from playwright.sync_api import sync_playwright
|
||||
|
||||
>>> playwright = sync_playwright().start()
|
||||
|
||||
>>> browser = playwright.chromium.launch()
|
||||
>>> page = browser.newPage()
|
||||
>>> page.goto("http://whatsmyuseragent.org/")
|
||||
>>> page.screenshot(path="example.png")
|
||||
>>> browser.close()
|
||||
|
||||
>>> playwright.stop()
|
||||
```
|
||||
|
||||
## async method: Playwright.stop
|
||||
* langs: python
|
||||
|
||||
@ -202,13 +221,13 @@ from a `setTimeout`. Consider this example:
|
||||
|
||||
```python async
|
||||
async with page.expect_navigation():
|
||||
await page.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
await page.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
# Context manager waited for the navigation to happen.
|
||||
```
|
||||
|
||||
```python sync
|
||||
with page.expect_navigation():
|
||||
page.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
page.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
# Context manager waited for the navigation to happen.
|
||||
```
|
||||
|
||||
@ -236,13 +255,13 @@ from a `setTimeout`. Consider this example:
|
||||
|
||||
```python async
|
||||
async with frame.expect_navigation():
|
||||
await frame.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
await frame.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
# Context manager waited for the navigation to happen.
|
||||
```
|
||||
|
||||
```python sync
|
||||
with frame.expect_navigation():
|
||||
frame.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
frame.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
|
||||
# Context manager waited for the navigation to happen.
|
||||
```
|
||||
|
||||
|
@ -4,7 +4,7 @@ title: "Assertions"
|
||||
---
|
||||
|
||||
The Playwright API can be used to read element contents and properties for test assertions. These values are fetched from the browser page and asserted in
|
||||
Node.js.
|
||||
your script.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
|
@ -4,12 +4,13 @@ title: "Device and environment emulation"
|
||||
---
|
||||
|
||||
Playwright allows overriding various parameters of the device where the browser is running:
|
||||
- viewport size, device scale factor, touch support
|
||||
- locale, timezone
|
||||
- color scheme
|
||||
- geolocation
|
||||
- viewport size, device scale factor, touch support
|
||||
- locale, timezone
|
||||
- color scheme
|
||||
- geolocation
|
||||
|
||||
Most of these parameters are configured during the browser context construction, but some of them such as viewport size can be changed for individual pages.
|
||||
Most of these parameters are configured during the browser context construction, but some of them such as viewport size
|
||||
can be changed for individual pages.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
@ -17,7 +18,8 @@ Most of these parameters are configured during the browser context construction,
|
||||
|
||||
## Devices
|
||||
|
||||
Playwright comes with a registry of device parameters for selected mobile devices. It can be used to simulate browser behavior on a mobile device:
|
||||
Playwright comes with a registry of device parameters for selected mobile devices. It can be used to simulate browser
|
||||
behavior on a mobile device:
|
||||
|
||||
```js
|
||||
const { chromium, devices } = require('playwright');
|
||||
@ -33,32 +35,36 @@ const context = await browser.newContext({
|
||||
import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
async def main():
|
||||
async with async_playwright() as p:
|
||||
pixel_2 = p.devices['Pixel 2']
|
||||
browser = await p.webkit.launch(headless=False)
|
||||
context = await browser.new_context(
|
||||
**pixel_2,
|
||||
)
|
||||
async def run(playwright):
|
||||
pixel_2 = playwright.devices['Pixel 2']
|
||||
browser = await playwright.webkit.launch(headless=False)
|
||||
context = await browser.new_context(
|
||||
**pixel_2,
|
||||
)
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(main())
|
||||
async def main():
|
||||
async with async_playwright() as playwright:
|
||||
await run(playwright)
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
with sync_playwright() as p:
|
||||
pixel_2 = p.devices['Pixel 2']
|
||||
browser = p.webkit.launch(headless=False)
|
||||
def run(playwright):
|
||||
pixel_2 = playwright.devices['Pixel 2']
|
||||
browser = playwright.webkit.launch(headless=False)
|
||||
context = browser.new_context(
|
||||
**pixel_2,
|
||||
)
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
```
|
||||
|
||||
All pages created in the context above will share the same device parameters.
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`property: Playwright.devices`]
|
||||
- [`method: Browser.newContext`]
|
||||
|
||||
@ -87,7 +93,6 @@ context = browser.new_context(
|
||||
```
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`method: Browser.newContext`]
|
||||
|
||||
<br/>
|
||||
@ -141,11 +146,9 @@ page.set_viewport_size(width=1600, height=1200)
|
||||
context = browser.new_context(
|
||||
viewport={ 'width': 2560, 'height': 1440 },
|
||||
device_scale_factor=2,
|
||||
|
||||
```
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: Page.setViewportSize`]
|
||||
|
||||
@ -178,7 +181,6 @@ context = browser.new_context(
|
||||
```
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`method: Browser.newContext`]
|
||||
|
||||
<br/>
|
||||
@ -248,7 +250,6 @@ context.clear_permissions()
|
||||
```
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: BrowserContext.grantPermissions`]
|
||||
- [`method: BrowserContext.clearPermissions`]
|
||||
@ -256,6 +257,7 @@ context.clear_permissions()
|
||||
<br/>
|
||||
|
||||
## Geolocation
|
||||
|
||||
Create a context with `"geolocation"` permissions granted:
|
||||
|
||||
```js
|
||||
@ -296,7 +298,6 @@ context.set_geolocation({"longitude": 29.979097, "latitude": 31.134256})
|
||||
**Note** you can only change geolocation for all pages in the context.
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: BrowserContext.setGeolocation`]
|
||||
|
||||
@ -304,8 +305,7 @@ context.set_geolocation({"longitude": 29.979097, "latitude": 31.134256})
|
||||
|
||||
## Color scheme and media
|
||||
|
||||
Create a context with dark or light mode. Pages created in this context will
|
||||
follow this color scheme preference.
|
||||
Create a context with dark or light mode. Pages created in this context will follow this color scheme preference.
|
||||
|
||||
```js
|
||||
// Create context with dark mode
|
||||
@ -362,6 +362,5 @@ page.emulate_media(media='print')
|
||||
```
|
||||
|
||||
#### API reference
|
||||
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: Page.emulateMedia`]
|
||||
- [`method: Page.emulateMedia`]
|
@ -10,14 +10,20 @@ title: "Extensibility"
|
||||
Playwright supports custom selector engines, registered with [`method: Selectors.register`].
|
||||
|
||||
Selector engine should have the following properties:
|
||||
|
||||
- `create` function to create a relative selector from `root` (root is either a `Document`, `ShadowRoot` or `Element`) to a `target` element.
|
||||
- `create` function to create a relative selector from `root` (root is either a `Document`, `ShadowRoot` or `Element`)
|
||||
to a `target` element.
|
||||
- `query` function to query first element matching `selector` relative to the `root`.
|
||||
- `queryAll` function to query all elements matching `selector` relative to the `root`.
|
||||
|
||||
By default the engine is run directly in the frame's JavaScript context and, for example, can call an application-defined function. To isolate the engine from any JavaScript in the frame, but leave access to the DOM, register the engine with `{contentScript: true}` option. Content script engine is safer because it is protected from any tampering with the global objects, for example altering `Node.prototype` methods. All built-in selector engines run as content scripts. Note that running as a content script is not guaranteed when the engine is used together with other custom engines.
|
||||
By default the engine is run directly in the frame's JavaScript context and, for example, can call an
|
||||
application-defined function. To isolate the engine from any JavaScript in the frame, but leave access to the DOM,
|
||||
register the engine with `{contentScript: true}` option. Content script engine is safer because it is protected from any
|
||||
tampering with the global objects, for example altering `Node.prototype` methods. All built-in selector engines run as
|
||||
content scripts. Note that running as a content script is not guaranteed when the engine is used together with other
|
||||
custom engines.
|
||||
|
||||
An example of registering selector engine that queries elements based on a tag name:
|
||||
|
||||
```js
|
||||
// Must be a function that evaluates to a selector engine instance.
|
||||
const createTagNameEngine = () => ({
|
||||
@ -44,3 +50,59 @@ await page.click('tag=div >> span >> "Click me"');
|
||||
// We can use it in any methods supporting selectors.
|
||||
const buttonCount = await page.$$eval('tag=button', buttons => buttons.length);
|
||||
```
|
||||
|
||||
```python async
|
||||
tag_selector = """
|
||||
// Must evaluate to a selector engine instance.
|
||||
{
|
||||
// Returns the first element matching given selector in the root's subtree.
|
||||
query(root, selector) {
|
||||
return root.querySelector(selector);
|
||||
},
|
||||
|
||||
// Returns all elements matching given selector in the root's subtree.
|
||||
queryAll(root, selector) {
|
||||
return Array.from(root.querySelectorAll(selector));
|
||||
}
|
||||
}"""
|
||||
|
||||
# register the engine. selectors will be prefixed with "tag=".
|
||||
await playwright.selectors.register("tag", tag_selector)
|
||||
|
||||
# now we can use "tag=" selectors.
|
||||
button = await page.query_selector("tag=button")
|
||||
|
||||
# we can combine it with other selector engines using `>>` combinator.
|
||||
await page.click("tag=div >> span >> "click me"")
|
||||
|
||||
# we can use it in any methods supporting selectors.
|
||||
button_count = await page.eval_on_selector_all("tag=button", buttons => buttons.length)
|
||||
```
|
||||
|
||||
```python sync
|
||||
tag_selector = """
|
||||
// Must evaluate to a selector engine instance.
|
||||
{
|
||||
// Returns the first element matching given selector in the root's subtree.
|
||||
query(root, selector) {
|
||||
return root.querySelector(selector);
|
||||
},
|
||||
|
||||
// Returns all elements matching given selector in the root's subtree.
|
||||
queryAll(root, selector) {
|
||||
return Array.from(root.querySelectorAll(selector));
|
||||
}
|
||||
}"""
|
||||
|
||||
# register the engine. selectors will be prefixed with "tag=".
|
||||
playwright.selectors.register("tag", tag_selector)
|
||||
|
||||
# now we can use "tag=" selectors.
|
||||
button = page.query_selector("tag=button")
|
||||
|
||||
# we can combine it with other selector engines using `>>` combinator.
|
||||
page.click("tag=div >> span >> "click me"")
|
||||
|
||||
# we can use it in any methods supporting selectors.
|
||||
button_count = page.eval_on_selector_all("tag=button", buttons => buttons.length)
|
||||
```
|
||||
|
@ -439,7 +439,6 @@ await page.setInputFiles('input#upload', {
|
||||
```
|
||||
|
||||
```python async
|
||||
from playwright.async_api import FilePayload
|
||||
# Select one file
|
||||
await page.set_input_files('input#upload', 'myfile.pdf')
|
||||
|
||||
@ -452,12 +451,13 @@ await page.set_input_files('input#upload', [])
|
||||
# Upload buffer from memory
|
||||
await page.set_input_files(
|
||||
"input#upload",
|
||||
files=[FilePayload("test.txt", "text/plain", b"this is a test")],
|
||||
files=[
|
||||
{"name": "test.txt", "mimeType": "text/plain", "buffer": b"this is a test"}
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import FilePayload
|
||||
# Select one file
|
||||
page.set_input_files('input#upload', 'myfile.pdf')
|
||||
|
||||
@ -470,7 +470,9 @@ page.set_input_files('input#upload', [])
|
||||
# Upload buffer from memory
|
||||
page.set_input_files(
|
||||
"input#upload",
|
||||
files=[FilePayload("test.txt", "text/plain", b"this is a test")],
|
||||
files=[
|
||||
{"name": "test.txt", "mimeType": "text/plain", "buffer": b"this is a test"}
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -3,16 +3,14 @@ id: multi-pages
|
||||
title: "Multi-page scenarios"
|
||||
---
|
||||
|
||||
Playwright can automate scenarios that span multiple browser contexts or multiple
|
||||
tabs in a browser window.
|
||||
Playwright can automate scenarios that span multiple browser contexts or multiple tabs in a browser window.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
## Multiple contexts
|
||||
|
||||
[Browser contexts](./core-concepts.md#browser-contexts) are isolated environments
|
||||
on a single browser instance. Playwright can create multiple browser contexts
|
||||
within a single scenario. This is useful when you want to test for
|
||||
[Browser contexts](./core-concepts.md#browser-contexts) are isolated environments on a single browser instance.
|
||||
Playwright can create multiple browser contexts within a single scenario. This is useful when you want to test for
|
||||
multi-user functionality, like chat.
|
||||
|
||||
```js
|
||||
@ -30,8 +28,50 @@ await userContext.addCookies(userCookies);
|
||||
await adminContext.addCookies(adminCookies);
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
async def run(playwright):
|
||||
# create a chromium browser instance
|
||||
chromium = playwright.chromium
|
||||
browser = await chromium.launch()
|
||||
|
||||
# create two isolated browser contexts
|
||||
user_context = await browser.new_context()
|
||||
admin_context = await browser.new_context()
|
||||
|
||||
# load user and admin cookies
|
||||
await user_context.add_cookies(user_cookies)
|
||||
await admin_context.add_cookies(admin_cookies)
|
||||
|
||||
async def main():
|
||||
async with async_playwright() as playwright:
|
||||
await run(playwright)
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def run(playwright):
|
||||
# create a chromium browser instance
|
||||
chromium = playwright.chromium
|
||||
browser = chromium.launch()
|
||||
|
||||
# create two isolated browser contexts
|
||||
user_context = browser.new_context()
|
||||
admin_context = browser.new_context()
|
||||
|
||||
# load user and admin cookies
|
||||
user_context.add_cookies(user_cookies)
|
||||
admin_context.add_cookies(admin_cookies)
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [BrowserContext]
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: BrowserContext.addCookies`]
|
||||
@ -39,11 +79,9 @@ await adminContext.addCookies(adminCookies);
|
||||
## Multiple pages
|
||||
|
||||
Each browser context can host multiple pages (tabs).
|
||||
|
||||
* Each page behaves like a focused, active page. Bringing the page to front
|
||||
is not required.
|
||||
* Pages inside a context respect context-level emulation, like viewport sizes,
|
||||
custom network routes or browser locale.
|
||||
* Each page behaves like a focused, active page. Bringing the page to front is not required.
|
||||
* Pages inside a context respect context-level emulation, like viewport sizes, custom network routes or browser
|
||||
locale.
|
||||
|
||||
```js
|
||||
// Create two pages
|
||||
@ -54,17 +92,33 @@ const pageTwo = await context.newPage();
|
||||
const allPages = context.pages();
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# create two pages
|
||||
page_one = await context.new_page()
|
||||
page_two = await context.new_page()
|
||||
|
||||
# get pages of a brower context
|
||||
all_pages = context.pages()
|
||||
```
|
||||
|
||||
```python sync
|
||||
# create two pages
|
||||
page_one = context.new_page()
|
||||
page_two = context.new_page()
|
||||
|
||||
# get pages of a brower context
|
||||
all_pages = context.pages()
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [Page]
|
||||
- [`method: BrowserContext.newPage`]
|
||||
- [`method: BrowserContext.pages`]
|
||||
|
||||
## Handling new pages
|
||||
|
||||
The `page` event on browser contexts can be used to get new pages that are
|
||||
created in the context. This can be used to handle new pages opened by
|
||||
`target="_blank"` links.
|
||||
The `page` event on browser contexts can be used to get new pages that are created in the context. This can be used to
|
||||
handle new pages opened by `target="_blank"` links.
|
||||
|
||||
```js
|
||||
// Get page after a specific action (e.g. clicking a link)
|
||||
@ -76,27 +130,62 @@ await newPage.waitForLoadState();
|
||||
console.log(await newPage.title());
|
||||
```
|
||||
|
||||
```python async
|
||||
# Get page after a specific action (e.g. clicking a link)
|
||||
async with context.expect_page() as new_page_info:
|
||||
await page.click('a[target="_blank"]') # Opens a new tab
|
||||
new_page = await new_page_info.value
|
||||
|
||||
await new_page.wait_for_load_state()
|
||||
print(await new_page.title())
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Get page after a specific action (e.g. clicking a link)
|
||||
with context.expect_page() as new_page_info:
|
||||
page.click('a[target="_blank"]') # Opens a new tab
|
||||
new_page = new_page_info.value
|
||||
|
||||
new_page.wait_for_load_state()
|
||||
print(new_page.title())
|
||||
```
|
||||
|
||||
If the action that triggers the new page is unknown, the following pattern can be used.
|
||||
|
||||
```js
|
||||
// Get all new pages (including popups) in the context
|
||||
context.on('page', async page => {
|
||||
await page.waitForLoadState();
|
||||
await page.title();
|
||||
console.log(await page.title());
|
||||
})
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Get all new pages (including popups) in the context
|
||||
async def handle_page(page):
|
||||
await page.wait_for_load_state()
|
||||
print(await page.title())
|
||||
|
||||
context.on("page", handle_page)
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Get all new pages (including popups) in the context
|
||||
def handle_page(page):
|
||||
page.wait_for_load_state()
|
||||
print(page.title())
|
||||
|
||||
context.on("page", handle_page)
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`event: BrowserContext.page`]
|
||||
|
||||
## Handling popups
|
||||
|
||||
If the page opens a pop-up, you can get a reference to it by listening to the
|
||||
`popup` event on the page.
|
||||
If the page opens a pop-up, you can get a reference to it by listening to the `popup` event on the page.
|
||||
|
||||
This event is emitted in addition to the `browserContext.on('page')` event, but
|
||||
only for popups relevant to this page.
|
||||
This event is emitted in addition to the `browserContext.on('page')` event, but only for popups relevant to this page.
|
||||
|
||||
```js
|
||||
// Get popup after a specific action (e.g., click)
|
||||
@ -105,7 +194,27 @@ const [popup] = await Promise.all([
|
||||
page.click('#open')
|
||||
]);
|
||||
await popup.waitForLoadState();
|
||||
await popup.title();
|
||||
console.log(await popup.title());
|
||||
```
|
||||
|
||||
```python async
|
||||
# Get popup after a specific action (e.g., click)
|
||||
async with page.expect_popup() as popup_info:
|
||||
await page.click("#open")
|
||||
popup = await popup_info.value
|
||||
|
||||
await popup.wait_for_load_state()
|
||||
print(await popup.title())
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Get popup after a specific action (e.g., click)
|
||||
with page.expect_popup() as popup_info:
|
||||
page.click("#open")
|
||||
popup = popup_info.value
|
||||
|
||||
popup.wait_for_load_state()
|
||||
print(popup.title())
|
||||
```
|
||||
|
||||
If the action that triggers the popup is unknown, the following pattern can be used.
|
||||
@ -118,6 +227,23 @@ page.on('popup', async popup => {
|
||||
})
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Get all popups when they open
|
||||
async def handle_popup(popup):
|
||||
await popup.wait_for_load_state()
|
||||
print(await popup.title())
|
||||
|
||||
- [`event: Page.popup`]
|
||||
page.on("popup", handle_popup)
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Get all popups when they open
|
||||
def handle_popup(popup):
|
||||
popup.wait_for_load_state()
|
||||
print(popup.title())
|
||||
|
||||
page.on("popup", handle_popup)
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`event: Page.popup`]
|
@ -3,17 +3,22 @@ id: 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 page interactions. This guide covers common scenarios
|
||||
to wait for page navigations and loading to complete.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
## Navigation lifecycle
|
||||
|
||||
Playwright splits the process of showing a new document in a page into **navigation** and **loading**.
|
||||
|
||||
**Navigations** can be initiated by changing the page URL or by interacting with the page (e.g., clicking a link). Navigation ends when response headers have been parsed and session history is updated. The navigation intent may be canceled, for example, on hitting an unresolved DNS address or transformed into a file download. Only after the navigation succeeds, page starts **loading** the document.
|
||||
|
||||
**Loading** covers getting the remaining response body over the network, parsing, executing the scripts and firing load events:
|
||||
**Navigations** can be initiated by changing the page URL or by interacting with the page (e.g., clicking a link).
|
||||
Navigation ends when response headers have been parsed and session history is updated. The navigation intent may be
|
||||
canceled, for example, on hitting an unresolved DNS address or transformed into a file download. Only after the
|
||||
navigation succeeds, page starts **loading** the document.
|
||||
|
||||
**Loading** covers getting the remaining response body over the network, parsing, executing the scripts and firing load
|
||||
events:
|
||||
- [`method: Page.url`] is set to the new url
|
||||
- document content is loaded over network and parsed
|
||||
- [`event: Page.domcontentloaded`] event is fired
|
||||
@ -23,17 +28,31 @@ Playwright splits the process of showing a new document in a page into **navigat
|
||||
- `networkidle` is fired when no new network requests are made for 500 ms
|
||||
|
||||
## 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`, `page.goto` will auto-wait for the redirected page to fire the `load` event.
|
||||
|
||||
Navigating to a URL auto-waits for the page to fire the `load` event. If the page does a client-side redirect before
|
||||
`load`, `page.goto` will auto-wait for the redirected page to fire the `load` event.
|
||||
|
||||
```js
|
||||
// Navigate the page
|
||||
await page.goto('https://example.com');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Navigate the page
|
||||
await page.goto("https://example.com")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Navigate the page
|
||||
page.goto("https://example.com")
|
||||
```
|
||||
|
||||
### Custom wait
|
||||
|
||||
Override the default behavior to wait until a specific event, like `networkidle`.
|
||||
|
||||
```js
|
||||
@ -41,8 +60,20 @@ Override the default behavior to wait until a specific event, like `networkidle`
|
||||
await page.goto('https://example.com', { waitUntil: 'networkidle' });
|
||||
```
|
||||
|
||||
```python async
|
||||
# Navigate and wait until network is idle
|
||||
await page.goto("https://example.com", wait_until="networkidle")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Navigate and wait until network is idle
|
||||
page.goto("https://example.com", wait_until="networkidle")
|
||||
```
|
||||
|
||||
### Wait for element
|
||||
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Page.waitForSelector`]. Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
|
||||
|
||||
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Page.waitForSelector`].
|
||||
Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
|
||||
|
||||
```js
|
||||
// Navigate and wait for element
|
||||
@ -55,6 +86,28 @@ await page.goto('https://example.com');
|
||||
await page.click('text=Example Domain');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Navigate and wait for element
|
||||
await page.goto("https://example.com")
|
||||
await page.wait_for_selector("text=example domain")
|
||||
|
||||
# Navigate and click element
|
||||
# Click will auto-wait for the element
|
||||
await page.goto("https://example.com")
|
||||
await page.click("text=example domain")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Navigate and wait for element
|
||||
page.goto("https://example.com")
|
||||
page.wait_for_selector("text=example domain")
|
||||
|
||||
# Navigate and click element
|
||||
# Click will auto-wait for the element
|
||||
page.goto("https://example.com")
|
||||
page.click("text=example domain")
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: Page.goto`]
|
||||
- [`method: Page.reload`]
|
||||
@ -62,10 +115,13 @@ await page.click('text=Example Domain');
|
||||
- [`method: Page.goForward`]
|
||||
|
||||
## Scenarios initiated by page interaction
|
||||
In the scenarios below, `page.click` initiates a navigation and then waits for the navigation to complete.
|
||||
|
||||
In the scenarios below, [`method: Page.click`] initiates a navigation and then waits for the navigation to complete.
|
||||
|
||||
### Auto-wait
|
||||
By default, `page.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.
|
||||
|
||||
By default, [`method: Page.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
|
||||
@ -74,7 +130,24 @@ await page.click('text=Login');
|
||||
await page.fill('#username', 'John Doe');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Click will auto-wait for navigation to complete
|
||||
await page.click("text=Login")
|
||||
|
||||
# Fill will auto-wait for element on navigated page
|
||||
await page.fill("#username", "John Doe")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Click will auto-wait for navigation to complete
|
||||
page.click("text=Login")
|
||||
|
||||
# Fill will auto-wait for element on navigated page
|
||||
page.fill("#username", "John Doe")
|
||||
```
|
||||
|
||||
### Custom wait
|
||||
|
||||
`page.click` can be combined with [`method: Page.waitForLoadState`] to wait for a loading event.
|
||||
|
||||
```js
|
||||
@ -82,51 +155,125 @@ await page.click('button'); // Click triggers navigation
|
||||
await page.waitForLoadState('networkidle'); // This resolves after 'networkidle'
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.click("button"); # Click triggers navigation
|
||||
await page.wait_for_load_state("networkidle"); # This waits for the "networkidle"
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.click("button"); # Click triggers navigation
|
||||
page.wait_for_load_state("networkidle"); # This waits for the "networkidle"
|
||||
```
|
||||
|
||||
### Wait for element
|
||||
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Page.waitForSelector`]. Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
|
||||
|
||||
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Page.waitForSelector`].
|
||||
Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
|
||||
|
||||
```js
|
||||
// Click triggers navigation
|
||||
await page.click('text=Login');
|
||||
// Click will auto-wait for the element
|
||||
// Click will auto-wait for the element
|
||||
await page.waitForSelector('#username', 'John Doe');
|
||||
|
||||
// Click triggers navigation
|
||||
await page.click('text=Login');
|
||||
// Fill will auto-wait for element
|
||||
// Fill will auto-wait for element
|
||||
await page.fill('#username', 'John Doe');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Click triggers navigation
|
||||
await page.click("text=Login")
|
||||
# Click will auto-wait for the element
|
||||
await page.wait_for_selector("#username", "John Doe")
|
||||
|
||||
# Click triggers navigation
|
||||
await page.click("text=Login")
|
||||
# Fill will auto-wait for element
|
||||
await page.fill("#username", "John Doe")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Click triggers navigation
|
||||
page.click("text=Login")
|
||||
# Click will auto-wait for the element
|
||||
page.wait_for_selector("#username", "John Doe")
|
||||
|
||||
# Click triggers navigation
|
||||
page.click("text=Login")
|
||||
# Fill will auto-wait for element
|
||||
page.fill("#username", "John Doe")
|
||||
```
|
||||
|
||||
### Asynchronous navigation
|
||||
Clicking an element could trigger asychronous processing before initiating the navigation. In these cases, it is recommended to explicitly call [`method: Page.waitForNavigation`]. For example:
|
||||
|
||||
Clicking an element could trigger asychronous 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
|
||||
// Note that Promise.all prevents a race condition
|
||||
// between clicking and waiting for a navigation.
|
||||
await Promise.all([
|
||||
page.click('a'), // Triggers a navigation after a timeout
|
||||
page.waitForNavigation(), // Waits for the next navigation
|
||||
page.click('a'), // Triggers a navigation after a timeout
|
||||
]);
|
||||
```
|
||||
|
||||
The `Promise.all` pattern prevents a race condition between `page.click` and `page.waitForNavigation` when navigation happens quickly.
|
||||
```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.click("a")
|
||||
```
|
||||
|
||||
```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.click("a")
|
||||
```
|
||||
|
||||
### 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:
|
||||
|
||||
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
|
||||
// Note that Promise.all prevents a race condition
|
||||
// between clicking and waiting for a navigation.
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ url: '**/login' }),
|
||||
page.click('a'), // Triggers a navigation with a script redirect
|
||||
]);
|
||||
```
|
||||
|
||||
The `Promise.all` pattern prevents a race condition between `page.click` and `page.waitForNavigation` when navigation happens quickly.
|
||||
```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.click("a")
|
||||
```
|
||||
|
||||
```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.click("a")
|
||||
```
|
||||
|
||||
### Loading a popup
|
||||
When popup is opened, explicitly calling [`method: Page.waitForLoadState`] ensures that popup is loaded to the desired state.
|
||||
|
||||
When popup is opened, explicitly calling [`method: Page.waitForLoadState`] ensures that popup is loaded to the desired
|
||||
state.
|
||||
|
||||
```js
|
||||
const [ popup ] = await Promise.all([
|
||||
@ -136,6 +283,20 @@ const [ popup ] = await Promise.all([
|
||||
await popup.waitForLoadState('load');
|
||||
```
|
||||
|
||||
```python async
|
||||
async with page.expect_popup() as popup_info:
|
||||
await page.click('a[target="_blank"]') # Opens popup
|
||||
popup = await popup_info.value
|
||||
await popup.wait_for_load_state("load")
|
||||
```
|
||||
|
||||
```python sync
|
||||
with page.expect_popup() as popup_info:
|
||||
page.click('a[target="_blank"]') # Opens popup
|
||||
popup = popup_info.value
|
||||
popup.wait_for_load_state("load")
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: Page.click`]
|
||||
- [`method: Page.waitForLoadState`]
|
||||
@ -144,7 +305,9 @@ await popup.waitForLoadState('load');
|
||||
- [`method: Page.waitForFunction`]
|
||||
|
||||
## Advanced patterns
|
||||
For pages that have complicated loading patterns, [`method: Page.waitForFunction`] is a powerful and extensible approach to define a custom wait criteria.
|
||||
|
||||
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');
|
||||
@ -153,5 +316,20 @@ await page.waitForFunction(() => window.amILoadedYet());
|
||||
await 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
|
||||
# FIXME
|
||||
page.goto("http://example.com")
|
||||
page.wait_for_function("() => window.amILoadedYet()")
|
||||
# Ready to take a screenshot, according to the page itself.
|
||||
page.screenshot()
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: Page.waitForFunction`]
|
||||
- [`method: Page.waitForFunction`]
|
@ -3,8 +3,8 @@ id: network
|
||||
title: "Network"
|
||||
---
|
||||
|
||||
Playwright provides APIs to **monitor** and **modify** network traffic, both HTTP and HTTPS.
|
||||
Any requests that page does, including [XHRs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
|
||||
Playwright provides APIs to **monitor** and **modify** network traffic, both HTTP and HTTPS. Any requests that page
|
||||
does, including [XHRs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
|
||||
[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) requests, can be tracked, modified and handled.
|
||||
|
||||
<!-- TOC -->
|
||||
@ -24,8 +24,23 @@ const page = await context.newPage();
|
||||
await page.goto('https://example.com');
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
context = await browser.new_context(
|
||||
http_credentials={"username": "bill", "password": "pa55w0rd"}
|
||||
)
|
||||
page = await context.new_page()
|
||||
await page.goto("https://example.com")
|
||||
```
|
||||
|
||||
```python sync
|
||||
context = browser.new_context(
|
||||
http_credentials={"username": "bill", "password": "pa55w0rd"}
|
||||
)
|
||||
page = context.new_page()
|
||||
page.goto("https://example.com")
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: Browser.newContext`]
|
||||
|
||||
<br/>
|
||||
@ -35,12 +50,32 @@ await page.goto('https://example.com');
|
||||
```js
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'), // <-- start waiting for the download
|
||||
page.click('button#delayed-download') // <-- perform the action that directly or indirectly initiates it.
|
||||
page.click('button#delayed-download') // <-- perform the action that directly or indirectly initiates it
|
||||
]);
|
||||
const path = await download.path();
|
||||
```
|
||||
|
||||
For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context with the [`option: acceptDownloads`] set, all these attachments are going to be downloaded into a temporary folder. You can obtain the download url, file system path and payload stream using the [Download] object from the event.
|
||||
```python async
|
||||
# Start waiting for the download
|
||||
async with page.expect_download() as download_info:
|
||||
# Perform the action that directly or indirectly initiates it
|
||||
await page.click("button#delayed-download")
|
||||
download = await download_info.value
|
||||
path = await download.path()
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Start waiting for the download
|
||||
with page.expect_download() as download_info:
|
||||
# Perform the action that directly or indirectly initiates it
|
||||
page.click("button#delayed-download")
|
||||
download = download_info.value
|
||||
path = download.path()
|
||||
```
|
||||
|
||||
For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context
|
||||
with the [`option: acceptDownloads`] set, all these attachments are going to be downloaded into a temporary folder. You
|
||||
can obtain the download url, file system path and payload stream using the [Download] object from the event.
|
||||
|
||||
#### Variations
|
||||
|
||||
@ -50,10 +85,20 @@ If you have no idea what initiates the download, you can still handle the event:
|
||||
page.on('download', download => download.path().then(console.log));
|
||||
```
|
||||
|
||||
Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you are downloading a file since your main control flow is not awaiting for this operation to resolve.
|
||||
```python async
|
||||
async def handle_download(download):
|
||||
print(await download.path())
|
||||
page.on("download", handle_download)
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.on("download", lambda download: print(download.path()))
|
||||
```
|
||||
|
||||
Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you
|
||||
are downloading a file since your main control flow is not awaiting for this operation to resolve.
|
||||
|
||||
#### API reference
|
||||
|
||||
- [Download]
|
||||
- [`event: Page.download`]
|
||||
- [`method: Page.waitForEvent`]
|
||||
@ -82,6 +127,43 @@ const { chromium, webkit, firefox } = require('playwright');
|
||||
})();
|
||||
```
|
||||
|
||||
```python async
|
||||
import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
async def run(playwright):
|
||||
chromium = playwright.chromium
|
||||
browser = await chromium.launch()
|
||||
page = await browser.new_page()
|
||||
# Subscribe to "request" and "response" events.
|
||||
page.on("request", lambda request: print(">>", request.method, request.url))
|
||||
page.on("response", lambda response: print("<<", response.status, response.url))
|
||||
await page.goto("https://example.com")
|
||||
await browser.close()
|
||||
|
||||
async def main():
|
||||
async with async_playwright() as playwright:
|
||||
await run(playwright)
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def run(playwright):
|
||||
chromium = playwright.chromium
|
||||
browser = chromium.launch()
|
||||
page = browser.new_page()
|
||||
# Subscribe to "request" and "response" events.
|
||||
page.on("request", lambda request: print(">>", request.method, request.url))
|
||||
page.on("response", lambda response: print("<<", response.status, response.url))
|
||||
page.goto("https://example.com")
|
||||
browser.close()
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
```
|
||||
|
||||
Or wait for a network response after the button click:
|
||||
|
||||
```js
|
||||
@ -92,6 +174,20 @@ const [response] = await Promise.all([
|
||||
]);
|
||||
```
|
||||
|
||||
```python async
|
||||
# Use a glob url pattern
|
||||
async with page.expect_response("**/api/fetch_data") as response_info:
|
||||
await page.click("button#update")
|
||||
response = await response_info.value
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Use a glob url pattern
|
||||
with page.expect_response("**/api/fetch_data") as response_info:
|
||||
page.click("button#update")
|
||||
response = response_info.value
|
||||
```
|
||||
|
||||
#### Variations
|
||||
|
||||
```js
|
||||
@ -108,8 +204,31 @@ const [response] = await Promise.all([
|
||||
]);
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Use a regular expresison
|
||||
async with page.expect_response(re.compile(r"\.jpeg$")) as response_info:
|
||||
await page.click("button#update")
|
||||
response = await response_info.value
|
||||
|
||||
# Use a predicate taking a response object
|
||||
async with page.expect_response(lambda response: token in response.url) as response_info:
|
||||
await page.click("button#update")
|
||||
response = await response_info.value
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Use a regular expresison
|
||||
with page.expect_response(re.compile(r"\.jpeg$")) as response_info:
|
||||
page.click("button#update")
|
||||
response = response_info.value
|
||||
|
||||
# Use a predicate taking a response object
|
||||
with page.expect_response(lambda response: token in response.url) as response_info:
|
||||
page.click("button#update")
|
||||
response = response_info.value
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [Request]
|
||||
- [Response]
|
||||
- [`event: Page.request`]
|
||||
@ -129,6 +248,20 @@ await page.route('**/api/fetch_data', route => route.fulfill({
|
||||
await page.goto('https://example.com');
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.route(
|
||||
"**/api/fetch_data",
|
||||
lambda route: route.fulfill(status=200, body=test_data))
|
||||
await page.goto("https://example.com")
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.route(
|
||||
"**/api/fetch_data",
|
||||
lambda route: route.fulfill(status=200, body=test_data))
|
||||
page.goto("https://example.com")
|
||||
```
|
||||
|
||||
You can mock API endpoints via handling the network quests in your Playwright script.
|
||||
|
||||
#### Variations
|
||||
@ -144,8 +277,25 @@ await browserContext.route('**/api/login', route => route.fulfill({
|
||||
await page.goto('https://example.com');
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Set up route on the entire browser context.
|
||||
# It will apply to popup windows and opened links.
|
||||
await context.route(
|
||||
"**/api/login",
|
||||
lambda route: route.fulfill(status=200, body="accept"))
|
||||
await page.goto("https://example.com")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Set up route on the entire browser context.
|
||||
# It will apply to popup windows and opened links.
|
||||
context.route(
|
||||
"**/api/login",
|
||||
lambda route: route.fulfill(status=200, body="accept"))
|
||||
page.goto("https://example.com")
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: BrowserContext.route`]
|
||||
- [`method: BrowserContext.unroute`]
|
||||
- [`method: Page.route`]
|
||||
@ -168,6 +318,30 @@ await page.route('**/*', route => {
|
||||
await page.route('**/*', route => route.continue({method: 'POST'}));
|
||||
```
|
||||
|
||||
```python async
|
||||
# Delete header
|
||||
async def handle_route(route):
|
||||
headers = route.request.headers
|
||||
del headers["x-secret"]
|
||||
route.continue_(headers=headers)
|
||||
await page.route("**/*", handle_route)
|
||||
|
||||
# Continue requests as POST.
|
||||
await page.route("**/*", lambda route: route.continue_(method="POST"))
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Delete header
|
||||
def handle_route(route):
|
||||
headers = route.request.headers
|
||||
del headers["x-secret"]
|
||||
route.continue_(headers=headers)
|
||||
page.route("**/*", handle_route)
|
||||
|
||||
# Continue requests as POST.
|
||||
page.route("**/*", lambda route: route.continue_(method="POST"))
|
||||
```
|
||||
|
||||
You can continue requests with modifications. Example above removes an HTTP header from the outgoing requests.
|
||||
|
||||
## Abort requests
|
||||
@ -182,10 +356,23 @@ await page.route('**/*', route => {
|
||||
});
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
await page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
|
||||
|
||||
# Abort based on the request type
|
||||
await page.route("**/*", lambda route: route.abort() if route.request.resource_type == "image" else route.continue_())
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
|
||||
|
||||
# Abort based on the request type
|
||||
page.route("**/*", lambda route: route.abort() if route.request.resource_type == "image" else route.continue_())
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: Page.route`]
|
||||
- [`method: BrowserContext.route`]
|
||||
- [`method: Route.abort`]
|
||||
|
||||
<br/>
|
||||
<br/>
|
@ -3,23 +3,23 @@ id: pom
|
||||
title: "Page Object Models"
|
||||
---
|
||||
|
||||
Large test suites can be structured to optimize ease of authoring and maintenance.
|
||||
Page object models are one such approach to structure your test suite.
|
||||
Large test suites can be structured to optimize ease of authoring and maintenance. Page object models are one such
|
||||
approach to structure your test suite.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
## Introduction
|
||||
A page object represents a part of your web application. An e-commerce web
|
||||
application might have a home page, a listings page and a checkout page. Each of
|
||||
them can be represented by page object models.
|
||||
|
||||
Page objects **simplify authoring**. They create a higher-level API which suits
|
||||
your application.
|
||||
A page object represents a part of your web application. An e-commerce web application might have a home page, a
|
||||
listings page and a checkout page. Each of them can be represented by page object models.
|
||||
|
||||
Page objects **simplify maintenance**. They capture element selectors in one place
|
||||
and create reusable code to avoid repetition.
|
||||
Page objects **simplify authoring**. They create a higher-level API which suits your application.
|
||||
|
||||
Page objects **simplify maintenance**. They capture element selectors in one place and create reusable code to avoid
|
||||
repetition.
|
||||
|
||||
## Implementation
|
||||
|
||||
Page object models wrap over a Playwright [Page].
|
||||
|
||||
```js
|
||||
@ -33,12 +33,40 @@ class SearchPage {
|
||||
}
|
||||
async search(text) {
|
||||
await this.page.fill('[aria-label="Enter your search term"]', text);
|
||||
await this.page.keyboard.press('Enter');
|
||||
await this.page.press('[aria-label="Enter your search term"]', 'Enter');
|
||||
}
|
||||
}
|
||||
module.exports = { SearchPage };
|
||||
```
|
||||
|
||||
```python async
|
||||
# models/search.py
|
||||
class SearchPage:
|
||||
def __init__(self, page):
|
||||
self.page = page
|
||||
|
||||
async def navigate(self):
|
||||
await self.page.goto("https://bing.com")
|
||||
|
||||
async def search(self, text):
|
||||
await self.page.fill('[aria-label="Enter your search term"]', text)
|
||||
await self.page.press('[aria-label="Enter your search term"]', "Enter")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# models/search.py
|
||||
class SearchPage:
|
||||
def __init__(self, page):
|
||||
self.page = page
|
||||
|
||||
def navigate(self):
|
||||
self.page.goto("https://bing.com")
|
||||
|
||||
def search(self, text):
|
||||
self.page.fill('[aria-label="Enter your search term"]', text)
|
||||
self.page.press('[aria-label="Enter your search term"]', "Enter")
|
||||
```
|
||||
|
||||
Page objects can then be used inside a test.
|
||||
|
||||
```js
|
||||
@ -52,5 +80,27 @@ await searchPage.navigate();
|
||||
await searchPage.search('search query');
|
||||
```
|
||||
|
||||
```python async
|
||||
# test_search.py
|
||||
from models.search import SearchPage
|
||||
|
||||
# in the test
|
||||
page = await browser.new_page()
|
||||
search_page = SearchPage(page)
|
||||
await search_page.navigate()
|
||||
await search_page.search("search query")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# test_search.py
|
||||
from models.search import SearchPage
|
||||
|
||||
# in the test
|
||||
page = browser.new_page()
|
||||
search_page = SearchPage(page)
|
||||
search_page.navigate()
|
||||
search_page.search("search query")
|
||||
```
|
||||
|
||||
### API reference
|
||||
- [Page]
|
||||
- [Page]
|
@ -59,6 +59,64 @@ const handle = await page.$('div >> ../span');
|
||||
const handle = await divHandle.$('css=span');
|
||||
```
|
||||
|
||||
```python async
|
||||
# queries 'div' css selector
|
||||
handle = await page.query_selector('css=div')
|
||||
|
||||
# queries '//html/body/div' xpath selector
|
||||
handle = await page.query_selector('xpath=//html/body/div')
|
||||
|
||||
# queries '"foo"' text selector
|
||||
handle = await page.query_selector('text="foo"')
|
||||
|
||||
# queries 'span' css selector inside the result of '//html/body/div' xpath selector
|
||||
handle = await page.query_selector('xpath=//html/body/div >> css=span')
|
||||
|
||||
# converted to 'css=div'
|
||||
handle = await page.query_selector('div')
|
||||
|
||||
# converted to 'xpath=//html/body/div'
|
||||
handle = await page.query_selector('//html/body/div')
|
||||
|
||||
# converted to 'text="foo"'
|
||||
handle = await page.query_selector('"foo"')
|
||||
|
||||
# queries '../span' xpath selector starting with the result of 'div' css selector
|
||||
handle = await page.query_selector('div >> ../span')
|
||||
|
||||
# queries 'span' css selector inside the div handle
|
||||
handle = await div_handle.query_selector('css=span')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# queries 'div' css selector
|
||||
handle = page.query_selector('css=div')
|
||||
|
||||
# queries '//html/body/div' xpath selector
|
||||
handle = page.query_selector('xpath=//html/body/div')
|
||||
|
||||
# queries '"foo"' text selector
|
||||
handle = page.query_selector('text="foo"')
|
||||
|
||||
# queries 'span' css selector inside the result of '//html/body/div' xpath selector
|
||||
handle = page.query_selector('xpath=//html/body/div >> css=span')
|
||||
|
||||
# converted to 'css=div'
|
||||
handle = page.query_selector('div')
|
||||
|
||||
# converted to 'xpath=//html/body/div'
|
||||
handle = page.query_selector('//html/body/div')
|
||||
|
||||
# converted to 'text="foo"'
|
||||
handle = page.query_selector('"foo"')
|
||||
|
||||
# queries '../span' xpath selector starting with the result of 'div' css selector
|
||||
handle = page.query_selector('div >> ../span')
|
||||
|
||||
# queries 'span' css selector inside the div handle
|
||||
handle = div_handle.query_selector('css=span')
|
||||
```
|
||||
|
||||
## Syntax
|
||||
Selectors are defined by selector engine name and selector body, `engine=body`.
|
||||
|
||||
@ -127,6 +185,40 @@ await page.click('[aria-label="Close"]'); // short-form
|
||||
await page.click('css=nav >> text=Login');
|
||||
```
|
||||
|
||||
```python async
|
||||
# queries "Login" text selector
|
||||
await page.click('text="Login"')
|
||||
await page.click('"Login"') # short-form
|
||||
|
||||
# queries "Search GitHub" placeholder attribute
|
||||
await page.fill('css=[placeholder="Search GitHub"]')
|
||||
await page.fill('[placeholder="Search GitHub"]') # short-form
|
||||
|
||||
# queries "Close" accessibility label
|
||||
await page.click('css=[aria-label="Close"]')
|
||||
await page.click('[aria-label="Close"]') # short-form
|
||||
|
||||
# combine role and text queries
|
||||
await page.click('css=nav >> text=Login')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# queries "Login" text selector
|
||||
page.click('text="Login"')
|
||||
page.click('"Login"') # short-form
|
||||
|
||||
# queries "Search GitHub" placeholder attribute
|
||||
page.fill('css=[placeholder="Search GitHub"]')
|
||||
page.fill('[placeholder="Search GitHub"]') # short-form
|
||||
|
||||
# queries "Close" accessibility label
|
||||
page.click('css=[aria-label="Close"]')
|
||||
page.click('[aria-label="Close"]') # short-form
|
||||
|
||||
# combine role and text queries
|
||||
page.click('css=nav >> text=Login')
|
||||
```
|
||||
|
||||
### Define explicit contract
|
||||
|
||||
When user-facing attributes change frequently, it is recommended to use explicit test ids, like `data-test-id`. These `data-*` attributes are supported by the [css] and [id selectors][id].
|
||||
@ -144,6 +236,24 @@ await page.click('[data-test-id=directions]'); // short-form
|
||||
await page.click('data-test-id=directions');
|
||||
```
|
||||
|
||||
```python async
|
||||
# queries data-test-id attribute with css
|
||||
await page.click('css=[data-test-id=directions]')
|
||||
await page.click('[data-test-id=directions]') # short-form
|
||||
|
||||
# queries data-test-id with id
|
||||
await page.click('data-test-id=directions')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# queries data-test-id attribute with css
|
||||
page.click('css=[data-test-id=directions]')
|
||||
page.click('[data-test-id=directions]') # short-form
|
||||
|
||||
# queries data-test-id with id
|
||||
page.click('data-test-id=directions')
|
||||
```
|
||||
|
||||
### Avoid selectors tied to implementation
|
||||
[xpath] and [css] can be tied to the DOM structure or implementation. These selectors can break when the DOM structure changes.
|
||||
|
||||
@ -153,6 +263,18 @@ await page.click('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.
|
||||
await page.click('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input');
|
||||
```
|
||||
|
||||
```python async
|
||||
# avoid long css or xpath chains
|
||||
await page.click('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input')
|
||||
await page.click('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# avoid long css or xpath chains
|
||||
page.click('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input')
|
||||
page.click('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input')
|
||||
```
|
||||
|
||||
## CSS selector engine
|
||||
|
||||
`css` is a default engine - any malformed selector not starting with `//` nor starting and ending with a quote is assumed to be a css selector. For example, Playwright converts `'span > button'` to `'css=span > button'`.
|
||||
@ -212,12 +334,24 @@ Consider a page with two buttons, first invisible and second visible.
|
||||
```js
|
||||
await page.click('button');
|
||||
```
|
||||
```python async
|
||||
await page.click("button")
|
||||
```
|
||||
```python sync
|
||||
page.click("button")
|
||||
```
|
||||
|
||||
* This will find a second button, because it is visible, and then click it.
|
||||
|
||||
```js
|
||||
await page.click('button:visible');
|
||||
```
|
||||
```python async
|
||||
await page.click("button:visible")
|
||||
```
|
||||
```python sync
|
||||
page.click("button:visible")
|
||||
```
|
||||
|
||||
Use `:visible` with caution, because it has two major drawbacks:
|
||||
* When elements change their visibility dynamically, `:visible` will give upredictable results based on the timing.
|
||||
@ -238,6 +372,16 @@ The `:text` pseudo-class matches elements that have a text node child with speci
|
||||
await page.click('button:text("Sign in")');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Click a button with text "Sign in".
|
||||
await page.click('button:text("Sign in")')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Click a button with text "Sign in".
|
||||
page.click('button:text("Sign in")')
|
||||
```
|
||||
|
||||
### CSS extension: has
|
||||
|
||||
The `:has()` pseudo-class is an [experimental CSS pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) that is supported by Playwright.
|
||||
@ -247,6 +391,16 @@ The `:has()` pseudo-class is an [experimental CSS pseudo-class](https://develope
|
||||
await page.textContent('article:has(div.promo)');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Returns text content of an <article> element that has a <div class=promo> inside.
|
||||
await page.textContent("article:has(div.promo)")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Returns text content of an <article> element that has a <div class=promo> inside.
|
||||
page.textContent("article:has(div.promo)")
|
||||
```
|
||||
|
||||
### CSS extension: is
|
||||
|
||||
The `:is()` pseudo-class is an [experimental CSS pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:is) that is supported by Playwright.
|
||||
@ -256,6 +410,16 @@ The `:is()` pseudo-class is an [experimental CSS pseudo-class](https://developer
|
||||
await page.click('button:is(:text("Log in"), :text("Sign in"))');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Clicks a <button> that has either a "Log in" or "Sign in" text.
|
||||
await page.click('button:is(:text("Log in"), :text("Sign in"))')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Clicks a <button> that has either a "Log in" or "Sign in" text.
|
||||
page.click('button:is(:text("Log in"), :text("Sign in"))')
|
||||
```
|
||||
|
||||
### CSS extension: light
|
||||
|
||||
`css` engine [pierces shadow](#shadow-piercing) by default. It is possible to disable this behavior by wrapping a selector in `:light` pseudo-class: `:light(section > button.class)` matches in light DOM only.
|
||||
@ -264,6 +428,14 @@ await page.click('button:is(:text("Log in"), :text("Sign in"))');
|
||||
await page.click(':light(.article > .header)');
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.click(":light(.article > .header)")
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.click(":light(.article > .header)")
|
||||
```
|
||||
|
||||
### CSS extension: position
|
||||
|
||||
Playwright provides position selectors based on the page layout. These can be combined with regular CSS for better results, for example `input:right-of(:text("Password"))` matches an input field that is to the right of text "Password".
|
||||
@ -285,6 +457,22 @@ await page.fill('input:right-of(:text("Username"))');
|
||||
await page.click('button:near(.promo-card)');
|
||||
```
|
||||
|
||||
```python async
|
||||
# Fill an input to the right of "Username".
|
||||
await page.fill('input:right-of(:text("Username"))')
|
||||
|
||||
# Click a button near the promo card.
|
||||
await page.click('button:near(.promo-card)')
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Fill an input to the right of "Username".
|
||||
page.fill('input:right-of(:text("Username"))')
|
||||
|
||||
# Click a button near the promo card.
|
||||
page.click('button:near(.promo-card)')
|
||||
```
|
||||
|
||||
## Xpath selector engine
|
||||
|
||||
XPath engine is equivalent to [`Document.evaluate`](https://developer.mozilla.org/en/docs/Web/API/Document/evaluate). Example: `xpath=//html/body`.
|
||||
|
@ -7,7 +7,8 @@ title: "Verification"
|
||||
|
||||
## Videos
|
||||
|
||||
Playwright can record videos for all pages in a [browser context](./core-concepts.md#browser-contexts). Videos are saved upon context closure, so make sure to await [`method: BrowserContext.close`].
|
||||
Playwright can record videos for all pages in a [browser context](./core-concepts.md#browser-contexts). Videos are saved
|
||||
upon context closure, so make sure to await [`method: BrowserContext.close`].
|
||||
|
||||
```js
|
||||
// With browser.newContext()
|
||||
@ -29,8 +30,43 @@ const context = await browser.newContext({
|
||||
});
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# With browser.new_context()
|
||||
context = await browser.new_context(record_video_dir="videos/")
|
||||
# Make sure to await close, so that videos are saved.
|
||||
await context.close()
|
||||
|
||||
# With browser.new_page()
|
||||
page = await browser.new_page(record_video_dir="videos/")
|
||||
# Make sure to await close, so that videos are saved.
|
||||
await page.close()
|
||||
|
||||
# [Optional] specify video size; defaults to viewport size
|
||||
context = await browser.new_context(
|
||||
record_video_dir="videos/",
|
||||
record_video_size={"width": 800, "height": 600}
|
||||
)
|
||||
```
|
||||
|
||||
```python sync
|
||||
# With browser.new_context()
|
||||
context = browser.new_context(record_video_dir="videos/")
|
||||
# Make sure to close, so that videos are saved.
|
||||
context.close()
|
||||
|
||||
# With browser.new_page()
|
||||
page = browser.new_page(record_video_dir="videos/")
|
||||
# Make sure to close, so that videos are saved.
|
||||
page.close()
|
||||
|
||||
# [Optional] specify video size; defaults to viewport size
|
||||
context = browser.new_context(
|
||||
record_video_dir="videos/",
|
||||
record_video_size={"width": 800, "height": 600}
|
||||
)
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [BrowserContext]
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: Browser.newPage`]
|
||||
@ -54,8 +90,39 @@ const elementHandle = await page.$('.header');
|
||||
await elementHandle.screenshot({ path: 'screenshot.png' });
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Save to file
|
||||
await page.screenshot(path="screenshot.png")
|
||||
|
||||
# Capture full page
|
||||
await page.screenshot(path="screenshot.png", full_page=True)
|
||||
|
||||
# Capture into Image
|
||||
screenshot_bytes = await page.screenshot()
|
||||
image = Image.open(io.BytesIO(screenshot_bytes))
|
||||
|
||||
# Capture given element
|
||||
element_handle = await page.query_selector(".header")
|
||||
await element_handle.screenshot(path="screenshot.png")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Save to file
|
||||
page.screenshot(path="screenshot.png")
|
||||
|
||||
# Capture full page
|
||||
page.screenshot(path="screenshot.png", full_page=True)
|
||||
|
||||
# Capture into Image
|
||||
screenshot_bytes = page.screenshot()
|
||||
image = Image.open(io.BytesIO(screenshot_bytes))
|
||||
|
||||
# Capture given element
|
||||
element_handle = page.query_selector(".header")
|
||||
element_handle.screenshot(path="screenshot.png")
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [`method: Page.screenshot`]
|
||||
- [`method: ElementHandle.screenshot`]
|
||||
|
||||
@ -89,8 +156,43 @@ await msg.args[0].jsonValue() // hello
|
||||
await msg.args[1].jsonValue() // 42
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Listen for all console logs
|
||||
page.on("console", msg => print(msg.text))
|
||||
|
||||
# Listen for all console events and handle errors
|
||||
page.on("console", lambda msg: print(f"error: {msg.text}") if msg.type == "error" else None)
|
||||
|
||||
# Get the next console log
|
||||
async with page.expect_console_message() as msg_info:
|
||||
# Issue console.log inside the page
|
||||
await page.evaluate("console.log('hello', 42, { foo: 'bar' })")
|
||||
msg = await msg_info.value
|
||||
|
||||
# Deconstruct print arguments
|
||||
await msg.args[0].json_value() # hello
|
||||
await msg.args[1].json_value() # 42
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Listen for all console logs
|
||||
page.on("console", msg => print(msg.text))
|
||||
|
||||
# Listen for all console events and handle errors
|
||||
page.on("console", lambda msg: print(f"error: {msg.text}") if msg.type == "error" else None)
|
||||
|
||||
# Get the next console log
|
||||
with page.expect_console_message() as msg_info:
|
||||
# Issue console.log inside the page
|
||||
page.evaluate("console.log('hello', 42, { foo: 'bar' })")
|
||||
msg = msg_info.value
|
||||
|
||||
# Deconstruct print arguments
|
||||
msg.args[0].json_value() # hello
|
||||
msg.args[1].json_value() # 42
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [ConsoleMessage]
|
||||
- [Page]
|
||||
- [`event: Page.console`]
|
||||
@ -111,8 +213,23 @@ page.on('pageerror', exception => {
|
||||
await page.goto('data:text/html,<script>throw new Error("Test")</script>');
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
# Log all uncaught errors to the terminal
|
||||
page.on("pageerror", lambda exc: print(f"uncaught exception: {exc}"))
|
||||
|
||||
# Navigate to a page with an exception.
|
||||
await page.goto("data:text/html,<script>throw new Error('test')</script>")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Log all uncaught errors to the terminal
|
||||
page.on("pageerror", lambda exc: print(f"uncaught exception: {exc}"))
|
||||
|
||||
# Navigate to a page with an exception.
|
||||
page.goto("data:text/html,<script>throw new Error('test')</script>")
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [Page]
|
||||
- [`event: Page.pageerror`]
|
||||
|
||||
@ -128,6 +245,10 @@ page.on('requestfailed', request => {
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
page.on("requestfailed", lambda request: print(request.url + " " + request.failure.error_text))
|
||||
```
|
||||
|
||||
#### `"dialog"` - handle alert, confirm, prompt
|
||||
|
||||
```js
|
||||
@ -136,6 +257,10 @@ page.on('dialog', dialog => {
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
page.on("dialog", lambda dialog: dialog.accept())
|
||||
```
|
||||
|
||||
#### `"popup"` - handle popup windows
|
||||
|
||||
```js
|
||||
@ -145,9 +270,20 @@ const [popup] = await Promise.all([
|
||||
]);
|
||||
```
|
||||
|
||||
#### API reference
|
||||
```python async
|
||||
async with page.expect_popup() as popup_info:
|
||||
await page.click("#open")
|
||||
popup = await popup_info.value
|
||||
```
|
||||
|
||||
```python sync
|
||||
with page.expect_popup() as popup_info:
|
||||
page.click("#open")
|
||||
popup = popup_info.value
|
||||
```
|
||||
|
||||
#### API reference
|
||||
- [Page]
|
||||
- [`event: Page.requestfailed`]
|
||||
- [`event: Page.dialog`]
|
||||
- [`event: Page.popup`]
|
||||
- [`event: Page.popup`]
|
18
types/types.d.ts
vendored
18
types/types.d.ts
vendored
@ -28,7 +28,7 @@ type ElementHandleWaitForSelectorOptionsNotHidden = ElementHandleWaitForSelector
|
||||
};
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* - extends: [EventEmitter]
|
||||
*
|
||||
* Page provides methods to interact with a single tab in a [Browser], or an
|
||||
* [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One [Browser]
|
||||
@ -4485,7 +4485,7 @@ export interface Frame {
|
||||
waitForTimeout(timeout: number): Promise<void>;}
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* - extends: [EventEmitter]
|
||||
*
|
||||
* BrowserContexts provide a way to operate multiple independent browser sessions.
|
||||
*
|
||||
@ -6865,7 +6865,7 @@ export interface ChromiumBrowser extends Browser {
|
||||
stopTracing(): Promise<Buffer>;}
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* - extends: [EventEmitter]
|
||||
*
|
||||
* The `CDPSession` instances are used to talk raw Chrome Devtools Protocol:
|
||||
* - protocol methods can be called with `session.send` method.
|
||||
@ -6939,12 +6939,12 @@ class TimeoutError extends Error {}
|
||||
* Accessibility is a very platform-specific thing. On different platforms, there are different screen readers that might
|
||||
* have wildly different output.
|
||||
*
|
||||
* Blink - Chromium's rendering engine - has a concept of "accessibility tree", which is then translated into different
|
||||
* platform-specific APIs. Accessibility namespace gives users access to the Blink Accessibility Tree.
|
||||
* Rendering engines of Chromium, Firefox and Webkit have a concept of "accessibility tree", which is then translated into
|
||||
* different platform-specific APIs. Accessibility namespace gives access to this Accessibility Tree.
|
||||
*
|
||||
* Most of the accessibility tree gets filtered out when converting from Blink AX Tree to Platform-specific AX-Tree or by
|
||||
* assistive technologies themselves. By default, Playwright tries to approximate this filtering, exposing only the
|
||||
* "interesting" nodes of the tree.
|
||||
* Most of the accessibility tree gets filtered out when converting from internal browser AX Tree to Platform-specific
|
||||
* AX-Tree or by assistive technologies themselves. By default, Playwright tries to approximate this filtering, exposing
|
||||
* only the "interesting" nodes of the tree.
|
||||
*/
|
||||
export interface Accessibility {
|
||||
/**
|
||||
@ -7031,7 +7031,7 @@ export {};
|
||||
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
* - extends: [EventEmitter]
|
||||
*
|
||||
* A Browser is created via
|
||||
* [browserType.launch(…)](https://github.com/microsoft/playwright/blob/master/docs/api.md#browsertypelaunch). An example
|
||||
|
Loading…
Reference in New Issue
Block a user