docs: more python docs and snippets (#5021)

This commit is contained in:
Dmitry Gozman 2021-01-14 15:01:39 -08:00 committed by GitHub
parent 61b0dbb3ef
commit e85f278869
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1079 additions and 132 deletions

View File

@ -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

View File

@ -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]:

View File

@ -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")

View File

@ -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.

View File

@ -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]

View File

@ -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.
```

View File

@ -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 -->

View File

@ -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`]

View File

@ -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)
```

View File

@ -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"}
],
)
```

View File

@ -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`]

View File

@ -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`]

View File

@ -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/>

View File

@ -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]

View File

@ -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`.

View File

@ -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
View File

@ -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