chore: add some Python language snippets (#4933)

This commit is contained in:
Max Schmitt 2021-01-08 17:39:33 +01:00 committed by GitHub
parent 07cb5f71e3
commit 77bfcd2c74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 1192 additions and 0 deletions

View file

@ -35,6 +35,42 @@ const checked = await page.getAttribute('input', 'checked');
assert(checked);
```
```python-async
# Assert text content
content = await page.text_content('nav:first-child')
assert content == 'home'
# Assert inner text
text = await page.inner_text('.selected')
assert text == 'value'
# Assert inner HTML
html = await page.inner_html('div.result')
assert html == '<p>Result</p>'
# Assert `checked` attribute
checked = await page.get_attribute('input', 'checked')
assert checked
```
```python-sync
# Assert text content
content = page.text_content('nav:first-child')
assert content == 'home'
# Assert inner text
text = page.inner_text('.selected')
assert text == 'value'
# Assert inner HTML
html = page.inner_html('div.result')
assert html == '<p>Result</p>'
# Assert `checked` attribute
checked = page.get_attribute('input', 'checked')
assert checked
```
#### API reference
- [`method: Page.textContent`]
@ -70,6 +106,32 @@ const classNames = await elementHandle.getAttribute('class');
assert(classNames.includes('highlighted'));
```
```python-async
# Get the element handle
element_handle = page.wait_for_selector('#box')
# Assert bounding box for the element
bounding_box = await element_handle.bounding_box()
assert bounding_box.width == 100
# Assert attribute for the element
class_names = await element_handle.get_attribute('class')
assert 'highlighted' in class_names
```
```python-sync
# Get the element handle
element_handle = page.wait_for_selector('#box')
# Assert bounding box for the element
bounding_box = element_handle.bounding_box()
assert bounding_box.width == 100
# Assert attribute for the element
class_names = element_handle.get_attribute('class')
assert 'highlighted' in class_names
```
#### API reference
- [`method: ElementHandle.textContent`]
@ -109,6 +171,44 @@ const length = await page.$$eval('li.selected', (items) => items.length);
assert(length === 3);
```
```python-async
# Assert local storage value
user_id = page.evaluate("() => window.localStorage.getItem('user_id')")
assert user_id
# Assert value for input element
await page.wait_for_selector('#search')
value = await page.eval_on_selector('#search', 'el => el.value')
assert value == 'query'
# Assert computed style
font_size = await page.eval_on_selector('div', 'el => window.getComputedStyle(el).fontSize')
assert font_size == '16px'
# Assert list length
length = await page.eval_on_selector_all('li.selected', '(items) => items.length')
assert length == 3
```
```python-sync
# Assert local storage value
user_id = page.evaluate("() => window.localStorage.getItem('user_id')")
assert user_id
# Assert value for input element
page.wait_for_selector('#search')
value = page.eval_on_selector('#search', 'el => el.value')
assert value == 'query'
# Assert computed style
font_size = page.eval_on_selector('div', 'el => window.getComputedStyle(el).fontSize')
assert font_size == '16px'
# Assert list length
length = page.eval_on_selector_all('li.selected', '(items) => items.length')
assert length == 3
```
#### API reference
- [`method: Page.evaluate`]

View file

@ -36,6 +36,30 @@ await page.click('text=Submit');
// Verify app is logged in
```
```python-async
page = await context.new_page()
await page.goto('https://github.com/login')
# Interact with login form
await page.click('text=Login')
await page.fill('input[name="login"]', USERNAME)
await page.fill('input[name="password"]', PASSWORD)
await page.click('text=Submit')
# Verify app is logged in
```
```python-sync
page = context.new_page()
page.goto('https://github.com/login')
# Interact with login form
page.click('text=Login')
page.fill('input[name="login"]', USERNAME)
page.fill('input[name="password"]', PASSWORD)
page.click('text=Submit')
# Verify app is logged in
```
These steps can be executed for every browser context. However, redoing login
for every test can slow down test execution. To prevent that, we will reuse
existing authentication state in new browser contexts.
@ -67,6 +91,30 @@ const deserializedCookies = JSON.parse(process.env.COOKIES)
await context.addCookies(deserializedCookies);
```
```python-async
import json
import os
# Get cookies and store as an env variable
cookies = await context.cookies()
os.environ["COOKIES"] = json.dumps(cookies)
# Set cookies in a new context
deserialized_cookies = json.loads(os.environ["COOKIES"])
await context.add_cookies(deserialized_cookies)
```
```python-sync
import json
import os
# Get cookies and store as an env variable
cookies = context.cookies()
os.environ["COOKIES"] = json.dumps(cookies)
# Set cookies in a new context
deserialized_cookies = json.loads(os.environ["COOKIES"])
context.add_cookies(deserialized_cookies)
```
### Local storage
Local storage ([`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage))
is specific to a particular domain.
@ -88,6 +136,44 @@ await context.addInitScript(storage => {
}, localStorage);
```
```python-async
import os
import json
# Get local storage and store as env variable
local_storage = await page.evaluate("() => JSON.stringify(window.localStorage))
os.environ["LOCAL_STORAGE"] = local_storage
# Set local storage in a new context
local_storage = os.environ["LOCAL_STORAGE"]
await context.add_init_script("""storage => {
if (window.location.hostname == 'example.com') {
entries = JSON.parse(storage)
Object.keys(entries).forEach(key => {
window.localStorage.setItem(key, entries[key])
})
}
}""", local_storage)
```
```python-sync
import os
import json
# Get local storage and store as env variable
local_storage = page.evaluate("() => JSON.stringify(window.localStorage)")
os.environ["LOCAL_STORAGE"] = local_storage
# Set local storage in a new context
local_storage = os.environ["LOCAL_STORAGE"]
context.add_init_script("""storage => {
if (window.location.hostname == 'example.com') {
entries = JSON.parse(storage)
Object.keys(entries).forEach(key => {
window.localStorage.setItem(key, entries[key])
})
}
}""", local_storage)
```
### Session storage
Session storage ([`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage))
is specific to a particular domain.
@ -109,6 +195,42 @@ await context.addInitScript(storage => {
}, sessionStorage);
```
```python-async
import os
# Get session storage and store as env variable
session_storage = await page.evaluate("() => JSON.stringify(sessionStorage)")
os.environ["SESSION_STORAGE"] = session_storage
# Set session storage in a new context
session_storage = os.environ["SESSION_STORAGE"]
await context.add_init_script(storage => {
if (window.location.hostname == 'example.com') {
entries = JSON.parse(storage)
Object.keys(entries).forEach(key => {
window.sessionStorage.setItem(key, entries[key])
})
}
}, session_storage)
```
```python-sync
import os
# Get session storage and store as env variable
session_storage = page.evaluate("() => JSON.stringify(sessionStorage)")
os.environ["SESSION_STORAGE"] = session_storage
# Set session storage in a new context
session_storage = os.environ["SESSION_STORAGE"]
context.add_init_script(storage => {
if (window.location.hostname == 'example.com') {
entries = JSON.parse(storage)
Object.keys(entries).forEach(key => {
window.sessionStorage.setItem(key, entries[key])
})
}
}, session_storage)
```
### Lifecycle
Logging in via the UI and then reusing authentication state can be combined to
@ -157,6 +279,28 @@ const context = await chromium.launchPersistentContext(userDataDir, { headless:
// Execute login steps manually in the browser window
```
```python-async
import asyncio
from playwright import async_playwright
async def main():
async with async_playwright() as p:
user_data_dir = '/path/to/directory'
browser = await p.chromium.launch_persistent_context(userDataDir, headless=False)
# Execute login steps manually in the browser window
asyncio.get_event_loop().run_until_complete(main())
```
```python-sync
from playwright import sync_playwright
with sync_playwright() as p:
user_data_dir = '/path/to/directory'
browser = p.chromium.launch_persistent_context(user_data_dir, headless=False)
# Execute login steps manually in the browser window
```
### Lifecycle
1. Create a user data directory on disk

View file

@ -50,6 +50,18 @@ Suggested configuration
});
```
```python-async
browser = await playwright.chromium.launch(
args=['--disable-dev-shm-usage']
)
```
```python-sync
browser = playwright.chromium.launch({
args=['--disable-dev-shm-usage']
})
```
This will write shared memory files into `/tmp` instead of `/dev/shm`. See
[crbug.com/736452](https://bugs.chromium.org/p/chromium/issues/detail?id=736452) for more details.
1. Using `--ipc=host` is also recommended when using Chromium—without it Chromium can run out of memory
@ -191,6 +203,14 @@ const { chromium } = require('playwright');
const browser = await chromium.launch({ chromiumSandbox: false });
```
```python-async
browser = await playwright.chromium.launch(chromiumSandbox=False)
```
```python-sync
browser = playwright.chromium.launch(chromiumSandbox=False)
```
### GitLab CI
To run Playwright tests on GitLab, use our public Docker image ([see Dockerfile](./docker.md)).
@ -267,6 +287,26 @@ const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: false });
```
```python-async
import asyncio
from playwright import async_playwright
async def main():
async with async_playwright() as p:
# Works across chromium, firefox and webkit
browser = await p.chromium.launch(headless=False)
asyncio.get_event_loop().run_until_complete(main())
```
```python-sync
from playwright import sync_playwright
with sync_playwright() as p:
# Works across chromium, firefox and webkit
browser = p.chromium.launch(headless=False)
```
On Linux agents, headful execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](./docker.md) and GitHub Action have Xvfb pre-installed. To run browsers in headful mode with Xvfb, add `xvfb-run` before the Node.js command.
```

View file

@ -29,6 +29,26 @@ const browser = await chromium.launch({ headless: false });
await browser.close();
```
```python-async
import asyncio
from playwright import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
```
```python-sync
from playwright import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
browser.close()
```
Launching a browser instance can be expensive, and Playwright is designed to
maximize what a single instance can do through multiple browser contexts.
@ -49,6 +69,16 @@ const browser = await chromium.launch();
const context = await browser.newContext();
```
```python-async
browser = await playwright.chromium.launch()
context = await browser.new_context()
```
```python-sync
browser = playwright.chromium.launch()
context = browser.new_context()
```
Browser contexts can also be used to emulate multi-page scenarios involving
mobile devices, permissions, locale and color scheme.
@ -65,6 +95,42 @@ const context = await browser.newContext({
});
```
```python-async
import asyncio
from playwright import async_playwright
async def main():
async with async_playwright() as p:
iphone_11 = p.devices['iPhone 11 Pro']
browser = await p.chromium.launch()
context = await browser.new_context(
**iphone_11,
locale='de-DE',
geolocation={ 'longitude': 12.492507, 'latitude': 41.889938 },
permissions=['geolocation'],
color_scheme='dark',
)
page = await browser.newPage()
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
```
```python-sync
from playwright import sync_playwright
with sync_playwright() as p:
iphone_11 = p.devices['iPhone 11 Pro']
browser = p.webkit.launch(headless=False)
context = browser.new_context(
**iphone_11,
locale='de-DE',
geolocation={ 'longitude': 12.492507, 'latitude': 41.889938 },
permissions=['geolocation']
)
browser.close()
```
#### API reference
- [BrowserContext]
@ -95,6 +161,40 @@ console.log(page.url());
window.location.href = 'https://example.com';
```
```python-async
page = await context.new_page()
# Navigate explicitly, similar to entering a URL in the browser.
await page.goto('http://example.com')
# Fill an input.
await page.fill('#search', 'query')
# Navigate implicitly by clicking a link.
await page.click('#submit')
# Expect a new url.
print(page.url)
# Page can navigate from the script - this will be picked up by Playwright.
# window.location.href = 'https://example.com'
```
```python-sync
page = context.new_page()
# Navigate explicitly, similar to entering a URL in the browser.
page.goto('http://example.com')
# Fill an input.
page.fill('#search', 'query')
# Navigate implicitly by clicking a link.
page.click('#submit')
# Expect a new url.
print(page.url)
# Page can navigate from the script - this will be picked up by Playwright.
# window.location.href = 'https://example.com'
```
> Read more on [page navigation and loading](./navigations.md).
A page can have one or more [Frame] objects attached to
@ -119,6 +219,36 @@ const frame = await frameElementHandle.contentFrame();
await frame.fill('#username-input', 'John');
```
```python-async
# Get frame using the frame's name attribute
frame = page.frame('frame-login')
# Get frame using frame's URL
frame = page.frame(url=r'.*domain.*')
# Get frame using any other selector
frame_element_handle = await page.query_selector('.frame-class')
frame = await frame_element_handle.content_frame()
# Interact with the frame
await frame.fill('#username-input', 'John')
```
```python-sync
# Get frame using the frame's name attribute
frame = page.frame('frame-login')
# Get frame using frame's URL
frame = page.frame(url=r'.*domain.*')
# Get frame using any other selector
frame_element_handle = page.query_selector('.frame-class')
frame = frame_element_handle.content_frame()
# Interact with the frame
frame.fill('#username-input', 'John')
```
#### API reference
- [Page]
@ -144,28 +274,82 @@ Some examples below:
await page.click('data-test-id=foo');
```
```python-async
# Using data-test-id= selector engine
await page.click('data-test-id=foo')
```
```python-sync
# Using data-test-id= selector engine
page.click('data-test-id=foo')
```
```js
// CSS and XPath selector engines are automatically detected
await page.click('div');
await page.click('//html/body/div');
```
```python-async
# CSS and XPath selector engines are automatically detected
await page.click('div')
await page.click('//html/body/div')
```
```python-sync
# CSS and XPath selector engines are automatically detected
page.click('div')
page.click('//html/body/div')
```
```js
// Find node by text substring
await page.click('text=Hello w');
```
```python-async
# Find node by text substring
await page.click('text=Hello w')
```
```python-sync
# Find node by text substring
page.click('text=Hello w')
```
```js
// Explicit CSS and XPath notation
await page.click('css=div');
await page.click('xpath=//html/body/div');
```
```python-async
# Explicit CSS and XPath notation
await page.click('css=div')
await page.click('xpath=//html/body/div')
```
```python-sync
# Explicit CSS and XPath notation
page.click('css=div')
page.click('xpath=//html/body/div')
```
```js
// Only search light DOM, outside WebComponent shadow DOM:
await page.click('css:light=div');
```
```python-async
# Only search light DOM, outside WebComponent shadow DOM:
await page.click('css:light=div')
```
```python-sync
# Only search light DOM, outside WebComponent shadow DOM:
page.click('css:light=div')
```
Selectors using the same or different engines can be combined using the `>>` separator. For example,
```js
@ -173,11 +357,31 @@ Selectors using the same or different engines can be combined using the `>>` sep
await page.click('#free-month-promo >> text=Sign Up');
```
```python-async
# Click an element with text 'Sign Up' inside of a #free-month-promo.
await page.click('#free-month-promo >> text=Sign Up')
```
```python-sync
# Click an element with text 'Sign Up' inside of a #free-month-promo.
page.click('#free-month-promo >> text=Sign Up')
```
```js
// Capture textContent of a section that contains an element with text 'Selectors'.
const sectionText = await page.$eval('*css=section >> text=Selectors', e => e.textContent);
```
```python-async
# Capture textContent of a section that contains an element with text 'Selectors'.
section_text = await page.eval_on_selector('*css=section >> text=Selectors', 'e => e.textContent')
```
```python-sync
# Capture textContent of a section that contains an element with text 'Selectors'.
section_text = page.eval_on_selector('*css=section >> text=Selectors', 'e => e.textContent')
```
<br/>
## Auto-waiting
@ -196,12 +400,35 @@ and [actionable](./actionability.md). For example, click will:
// Playwright waits for #search element to be in the DOM
await page.fill('#search', 'query');
```
```python-async
# Playwright waits for #search element to be in the DOM
await page.fill('#search', 'query')
```
```python-sync
# Playwright waits for #search element to be in the DOM
page.fill('#search', 'query')
```
```js
// Playwright waits for element to stop animating
// and accept clicks.
await page.click('#search');
```
```python-async
# Playwright waits for element to stop animating
# and accept clicks.
await page.click('#search')
```
```python-sync
# Playwright waits for element to stop animating
# and accept clicks.
page.click('#search')
```
You can explicitly wait for an element to appear in the DOM or to become visible:
```js
@ -211,6 +438,20 @@ await page.waitForSelector('#search', { state: 'attached' });
await page.waitForSelector('#promo');
```
```python-async
# Wait for #search to appear in the DOM.
await page.wait_for_selector('#search', state='attached')
# Wait for #promo to become visible, for example with `visibility:visible`.
await page.wait_for_selector('#promo')
```
```python-sync
# Wait for #search to appear in the DOM.
page.wait_for_selector('#search', state='attached')
# Wait for #promo to become visible, for example with `visibility:visible`.
page.wait_for_selector('#promo')
```
... or to become hidden or detached
```js
@ -220,6 +461,20 @@ await page.waitForSelector('#details', { state: 'hidden' });
await page.waitForSelector('#promo', { state: 'detached' });
```
```python-async
# Wait for #details to become hidden, for example with `display:none`.
await page.wait_for_selector('#details', state='hidden')
# Wait for #promo to be removed from the DOM.
await page.wait_for_selector('#promo', state='detached')
```
```python-sync
# Wait for #details to become hidden, for example with `display:none`.
page.wait_for_selector('#details', state='hidden')
# Wait for #promo to be removed from the DOM.
page.wait_for_selector('#promo', state='detached')
```
#### API reference
- [`method: Page.click`]
@ -240,7 +495,16 @@ of the web page and bring results back to the Node.js environment. Browser globa
const href = await page.evaluate(() => document.location.href);
```
```python-async
href = await page.evaluate('() => document.location.href')
```
```python-sync
href = page.evaluate('() => document.location.href')
```
If the result is a Promise or if the function is asynchronous evaluate will automatically wait until it's resolved:
```js
const status = await page.evaluate(async () => {
const response = await fetch(location.href);
@ -248,6 +512,20 @@ const status = await page.evaluate(async () => {
});
```
```python-async
status = await page.evaluate("""async () => {
response = await fetch(location.href)
return response.status
}""")
```
```python-sync
status = page.evaluate("""async () => {
response = fetch(location.href)
return response.status
}""")
```
## Evaluation Argument
Playwright evaluation methods like [`method: Page.evaluate`] take a single optional argument. This argument can be a mix of [Serializable] values and [JSHandle] or [ElementHandle] instances. Handles are automatically converted to the value they represent.
@ -295,6 +573,90 @@ await page.evaluate(
{ button1, list: [button2], foo: null });
```
```python-async
# A primitive value.
await page.evaluate('num => num', 42)
# An array.
await page.evaluate('array => array.length', [1, 2, 3])
# An object.
await page.evaluate('object => object.foo', { 'foo': 'bar' })
# A single handle.
button = await page.query_selctor('button')
await page.evaluate('button => button.textContent', button)
# Alternative notation using elementHandle.evaluate.
await button.evaluate('(button, from) => button.textContent.substring(from)', 5)
# Object with multiple handles.
button1 = await page.query_selector('.button1')
button2 = await page.query_selector('.button2')
await page.evaluate("""
o => o.button1.textContent + o.button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Object destructuring works. Note that property names must match
# between the destructured object and the argument.
# Also note the required parenthesis.
await page.evaluate("""
({ button1, button2 }) => button1.textContent + button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Array works as well. Arbitrary names can be used for destructuring.
# Note the required parenthesis.
await page.evaluate("""
([b1, b2]) => b1.textContent + b2.textContent""",
[button1, button2])
# Any non-cyclic mix of serializables and handles works.
await page.evaluate("""
x => x.button1.textContent + x.list[0].textContent + String(x.foo)""",
{ 'button1': button1, 'list': [button2], 'foo': None })
```
```python-sync
# A primitive value.
page.evaluate('num => num', 42)
# An array.
page.evaluate('array => array.length', [1, 2, 3])
# An object.
page.evaluate('object => object.foo', { 'foo': 'bar' })
# A single handle.
button = page.query_selector('button')
page.evaluate('button => button.textContent', button)
# Alternative notation using elementHandle.evaluate.
button.evaluate('(button, from) => button.textContent.substring(from)', 5)
# Object with multiple handles.
button1 = page.query_selector('.button1')
button2 = page.query_selector('.button2')
page.evaluate("""o => o.button1.textContent + o.button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Object destructuring works. Note that property names must match
# between the destructured object and the argument.
# Also note the required parenthesis.
page.evaluate("""
({ button1, button2 }) => button1.textContent + button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Array works as well. Arbitrary names can be used for destructuring.
# Note the required parenthesis.
page.evaluate("""
([b1, b2]) => b1.textContent + b2.textContent""",
[button1, button2])
# Any non-cyclic mix of serializables and handles works.
page.evaluate("""
x => x.button1.textContent + x.list[0].textContent + String(x.foo)""",
{ 'button1': button1, 'list': [button2], 'foo': None })
```
Right:
@ -306,6 +668,22 @@ const result = await page.evaluate(data => {
}, data);
```
```python-async
data = { 'text': 'some data', 'value': 1 }
# Pass |data| as a parameter.
result = await page.evaluate("""data => {
window.myApp.use(data)
}""", data)
```
```python-sync
data = { 'text': 'some data', 'value': 1 }
# Pass |data| as a parameter.
result = page.evaluate("""data => {
window.myApp.use(data)
}""", data)
```
Wrong:
```js
@ -316,6 +694,22 @@ const result = await page.evaluate(() => {
});
```
```python-async
data = { 'text': 'some data', 'value': 1 }
result = await page.evaluate("""() => {
# There is no |data| in the web page.
window.myApp.use(data)
}""")
```
```python-sync
data = { 'text': 'some data', 'value': 1 }
result = page.evaluate("""() => {
# There is no |data| in the web page.
window.myApp.use(data)
}""")
```
#### API reference
- [`method: Page.evaluate`]
@ -350,6 +744,18 @@ const ulElementHandle = await page.$('ul');
await ulElementHandle.evaluate(ulElement => getComputedStyle(ulElement).getPropertyValue('display'));
```
```python-async
# The first parameter of the elementHandle.evaluate callback is the element handle points to.
ul_element_handle = await page.query_selector('ul')
await ul_element_handle.evaluate("ulElement => getComputedStyle(ulElement).getPropertyValue('display')")
```
```python-sync
# The first parameter of the elementHandle.evaluate callback is the element handle points to.
ul_element_handle = page.query_selector('ul')
ul_element_handle.evaluate("ulElement => getComputedStyle(ulElement).getPropertyValue('display')")
```
Handles can also be passed as arguments to [`method: Page.evaluate`] function:
```js
@ -358,6 +764,16 @@ const ulElementHandle = await page.$('ul');
await page.evaluate(uiElement => getComputedStyle(uiElement).getPropertyValue('display'), uiElement);
```
```python-async
ul_element_handle = await page.query_selector('ul')
await page.evaluate("uiElement => getComputedStyle(uiElement).getPropertyValue('display')", uiElement)
```
```python-sync
ul_element_handle = page.query_selector('ul')
page.evaluate("uiElement => getComputedStyle(uiElement).getPropertyValue('display')", uiElement)
```
### Example: JSHandle
```js
@ -387,6 +803,60 @@ const newLength = await page.evaluate(() => window.myArray.length);
await myArrayHandle.dispose();
```
```python-async
# Create a new array in the page, write a reference to it in
# window.myArray and get a handle to it.
my_array_handle = await page.evaluate_handle("""() => {
window.myArray = [1]
return myArray
}""")
# Get current length of the array using the handle.
length = await page.evaluate("""
(arg) => arg.myArray.length""",
{ 'myArray': my_array_handle }
)
# Add one more element to the array using the handle
await page.evaluate("(arg) => arg.myArray.push(arg.newElement)", {
'myArray': my_array_handle,
'newElement': 2
})
# Get current length of the array using window.myArray reference.
new_length = await page.evaluate("() => window.myArray.length")
# Release the object when it's no longer needed.
await my_array_handle.dispose()
```
```python-sync
# Create a new array in the page, write a reference to it in
# window.myArray and get a handle to it.
my_array_handle = page.evaluate_handle("""() => {
window.myArray = [1]
return myArray
}""")
# Get current length of the array using the handle.
length = page.evaluate("""
(arg) => arg.myArray.length""",
{ 'myArray': my_array_handle }
)
# Add one more element to the array using the handle
page.evaluate("(arg) => arg.myArray.push(arg.newElement)", {
'myArray': my_array_handle,
'newElement': 2
})
# Get current length of the array using window.myArray reference.
new_length = page.evaluate("() => window.myArray.length")
# Release the object when it's no longer needed.
my_array_handle.dispose()
```
#### API reference
- [JSHandle]
- [ElementHandle]

View file

@ -19,6 +19,16 @@ to slow down execution and follow along while debugging.
await chromium.launch({ headless: false, slowMo: 100 }); // or firefox, webkit
```
```python-async
await chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
```
```python-sync
chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
```
## Visual Studio Code debugger
The VS Code debugger can be used to pause and resume execution of Playwright
@ -66,6 +76,15 @@ In Chromium, you can also open developer tools through a launch option.
await chromium.launch({ devtools: true });
```
```python-async
await chromium.launch(devtools=True)
```
```python-sync
chromium.launch(devtools=True)
```
## Run in Debug Mode
Set the `PWDEBUG` environment variable to run your scripts in debug mode. This

View file

@ -29,6 +29,32 @@ const context = await browser.newContext({
});
```
```python-async
import asyncio
from playwright 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,
)
asyncio.get_event_loop().run_until_complete(main())
```
```python-sync
from playwright import sync_playwright
with sync_playwright() as p:
pixel_2 = p.devices['Pixel 2']
browser = p.webkit.launch(headless=False)
context = browser.new_context(
**pixel_2,
)
```
All pages created in the context above will share the same device parameters.
#### API reference
@ -48,6 +74,18 @@ const context = await browser.newContext({
});
```
```python-async
context = await browser.new_context(
user_agent='My user agent'
)
```
```python-sync
context = browser.new_context(
user_agent='My user agent'
)
```
#### API reference
- [`method: Browser.newContext`]
@ -74,6 +112,38 @@ const context = await browser.newContext({
});
```
```python-async
# Create context with given viewport
context = await browser.new_context(
viewport={ 'width': 1280, 'height': 1024 }
)
# Resize viewport for individual page
await page.set_viewport_size(width=1600, height=1200)
# Emulate high-DPI
context = await browser.new_context(
viewport={ 'width': 2560, 'height': 1440 },
device_scale_factor=2,
)
```
```python-sync
# Create context with given viewport
context = browser.new_context(
viewport={ 'width': 1280, 'height': 1024 }
)
# Resize viewport for individual page
page.set_viewport_size(width=1600, height=1200)
# Emulate high-DPI
context = browser.new_context(
viewport={ 'width': 2560, 'height': 1440 },
device_scale_factor=2,
```
#### API reference
- [`method: Browser.newContext`]
@ -91,6 +161,22 @@ const context = await browser.newContext({
});
```
```python-async
# Emulate locale and time
context = await browser.new_context(
locale='de-DE',
timezone_id='Europe/Berlin',
)
```
```python-sync
# Emulate locale and time
context = browser.new_context(
locale='de-DE',
timezone_id='Europe/Berlin',
)
```
#### API reference
- [`method: Browser.newContext`]
@ -100,27 +186,67 @@ const context = await browser.newContext({
## Permissions
Allow all pages in the context to show system notifications:
```js
const context = await browser.newContext({
permissions: ['notifications'],
});
```
```python-async
context = await browser.new_context(
permissions=['notifications'],
)
```
```python-sync
context = browser.new_context(
permissions=['notifications'],
)
```
Grant all pages in the existing context access to current location:
```js
await context.grantPermissions(['geolocation']);
```
```python-async
await context.grant_permissions(['geolocation'])
```
```python-sync
context.grant_permissions(['geolocation'])
```
Grant notifications access from a specific domain:
```js
await context.grantPermissions(['notifications'], {origin: 'https://skype.com'} );
```
```python-async
await context.grant_permissions(['notifications'], origin='https://skype.com')
```
```python-sync
context.grant_permissions(['notifications'], origin='https://skype.com')
```
Revoke all permissions:
```js
await context.clearPermissions();
```
```python-async
await context.clear_permissions()
```
```python-sync
context.clear_permissions()
```
#### API reference
- [`method: Browser.newContext`]
@ -131,18 +257,42 @@ await context.clearPermissions();
## Geolocation
Create a context with `"geolocation"` permissions granted:
```js
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
```
```python-async
context = await browser.new_context(
geolocation={ 'longitude': 48.858455, 'latitude': 2.294474 },
permissions=['geolocation']
)
```
```python-sync
context = browser.new_context(
geolocation={ 'longitude': 48.858455, 'latitude': 2.294474 },
permissions=['geolocation']
)
```
Change the location later:
```js
await context.setGeolocation({ longitude: 29.979097, latitude: 31.134256 });
```
```python-async
await context.set_geolocation(longitude=29.979097, latitude=31.134256)
```
```python-sync
context.set_geolocation(longitude=29.979097, latitude=31.134256)
```
**Note** you can only change geolocation for all pages in the context.
#### API reference
@ -175,6 +325,42 @@ await page.emulateMedia({ colorScheme: 'dark' });
await page.emulateMedia({ media: 'print' });
```
```python-async
# Create context with dark mode
context = await browser.new_context(
color_scheme='dark' # or 'light'
)
# Create page with dark mode
page = await browser.new_page(
color_scheme='dark' # or 'light'
)
# Change color scheme for the page
await page.emulate_media(color_scheme='dark')
# Change media for page
await page.emulate_media(media='print')
```
```python-sync
# Create context with dark mode
context = browser.new_context(
color_scheme='dark' # or 'light'
)
# Create page with dark mode
page = browser.new_page(
color_scheme='dark' # or 'light'
)
# Change color scheme for the page
page.emulate_media(color_scheme='dark')
# Change media for page
page.emulate_media(media='print')
```
#### API reference
- [`method: Browser.newContext`]

View file

@ -26,6 +26,40 @@ await page.fill('#local', '2020-03-02T05:15');
await page.fill('text=First Name', 'Peter');
```
```python-async
# Text input
await page.fill('#name', 'Peter')
# Date input
await page.fill('#date', '2020-02-02')
# Time input
await page.fill('#time', '13-15')
# Local datetime input
await page.fill('#local', '2020-03-02T05:15')
# Input through label
await page.fill('text=First Name', 'Peter')
```
```python-sync
# Text input
page.fill('#name', 'Peter')
# Date input
page.fill('#date', '2020-02-02')
# Time input
page.fill('#time', '13-15')
# Local datetime input
page.fill('#local', '2020-03-02T05:15')
# Input through label
page.fill('text=First Name', 'Peter')
```
#### API reference
- [`method: Page.fill`]
@ -49,6 +83,28 @@ await page.uncheck('#subscribe-label');
await page.check('text=XL');
```
```python-async
# Check the checkbox
await page.check('#agree')
# Uncheck by input <label>.
await page.uncheck('#subscribe-label')
# Select the radio button
await page.check('text=XL')
```
```python-sync
# Check the checkbox
page.check('#agree')
# Uncheck by input <label>.
page.uncheck('#subscribe-label')
# Select the radio button
page.check('text=XL')
```
#### API reference
- [`method: Page.check`]
@ -80,6 +136,36 @@ const option = await page.$('#best-option');
await page.selectOption('select#colors', option);
```
```python-async
# Single selection matching the value
await page.select_option('select#colors', 'blue')
# Single selection matching the label
await page.select_option('select#colors', label='Blue')
# Multiple selected items
await page.select_option('select#colors', ['red', 'green', 'blue'])
# Select the option via element handle
option = await page.query_selector('#best-option')
await page.select_option('select#colors', option)
```
```python-sync
# Single selection matching the value
page.select_option('select#colors', 'blue')
# Single selection matching the label
page.select_option('select#colors', label='Blue')
# Multiple selected items
page.select_option('select#colors', ['red', 'green', 'blue'])
# Select the option via element handle
option = page.query_selector('#best-option')
page.select_option('select#colors', option)
```
#### API reference
- [`method: Page.selectOption`]
@ -112,6 +198,46 @@ await page.hover('#item');
await page.click('#item', { position: { x: 0, y: 0} });
```
```python-async
# Generic click
await page.click('button#submit')
# Double click
await page.dblclick('#item')
# Right click
await page.click('#item', button='right')
# Shift + click
await page.click('#item', modifiers=['Shift'])
# Hover over element
await page.hover('#item')
# Click the top left corner
await page.click('#item', position={ 'x': 0, 'y': 0})
```
```python-sync
# Generic click
page.click('button#submit')
# Double click
page.dblclick('#item')
# Right click
page.click('#item', button='right')
# Shift + click
page.click('#item', modifiers=['Shift'])
# Hover over element
page.hover('#item')
# Click the top left corner
page.click('#item', position={ 'x': 0, 'y': 0})
```
Under the hood, this and other pointer-related methods:
- wait for element with given selector to be in DOM
@ -129,6 +255,14 @@ Sometimes, apps use non-trivial logic where hovering the element overlays it wit
await page.click('button#submit', { force: true });
```
```python-async
await page.click('button#submit', force=True)
```
```python-sync
page.click('button#submit', force=True)
```
#### Programmatic click
If you are not interested in testing your app under the real conditions and want to simulate the click by any means possible, you can trigger the [`HTMLElement.click()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click) behavior via simply dispatching a click event on the element:
@ -137,6 +271,13 @@ If you are not interested in testing your app under the real conditions and want
await page.dispatchEvent('button#submit', 'click');
```
```python-async
await page.dispatch_event('button#submit', 'click')
```
```python-sync
page.dispatch_event('button#submit', 'click')
```
#### API reference
@ -164,6 +305,16 @@ Type into the field character by character, as if it was a user with a real keyb
await page.type('#area', 'Hello World!');
```
```python-async
# Type character by character
await page.type('#area', 'Hello World!')
```
```python-sync
# Type character by character
page.type('#area', 'Hello World!')
```
This method will emit all the necessary keyboard events, with all the `keydown`, `keyup`, `keypress` events in place. You can even specify the optional `delay` between the key presses to simulate real user behavior.
> **NOTE** that most of the time, [`method: Page.fill`] will just work. You only need to type characters if there is special keyboard handling on the page.
@ -190,6 +341,28 @@ await page.press('#name', 'Control+ArrowRight');
await page.press('#value', '$');
```
```python-async
# Hit Enter
await page.press('#submit', 'Enter')
# Dispatch Control+Right
await page.press('#name', 'Control+ArrowRight')
# Press $ sign on keyboard
await page.press('#value', '$')
```
```python-sync
# Hit Enter
page.press('#submit', 'Enter')
# Dispatch Control+Right
page.press('#name', 'Control+ArrowRight')
# Press $ sign on keyboard
page.press('#value', '$')
```
This method focuses the selected element and produces a single keystroke. It accepts the logical key names that are emitted in the [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) property of the keyboard events:
```
@ -213,6 +386,22 @@ await page.press('#name', 'Shift+A');
await page.press('#name', 'Shift+ArrowLeft');
```
```python-async
# <input id=name>
await page.press('#name', 'Shift+A')
# <input id=name>
await page.press('#name', 'Shift+ArrowLeft')
```
```python-sync
# <input id=name>
page.press('#name', 'Shift+A')
# <input id=name>
page.press('#name', 'Shift+ArrowLeft')
```
Shortcuts such as `"Control+o"` or `"Control+Shift+T"` are supported as well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
Note that you still need to specify the capital `A` in `Shift-A` to produce the capital character. `Shift-a` produces a lower-case one as if you had the `CapsLock` toggled.
@ -247,6 +436,42 @@ 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')
# Select multiple files
await page.set_input_files('input#upload', ['file1.txt', 'file2.txt'])
# Remove all the selected files
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")],
)
```
```python-sync
from playwright.sync_api import FilePayload
# Select one file
page.set_input_files('input#upload', 'myfile.pdf')
# Select multiple files
page.set_input_files('input#upload', ['file1.txt', 'file2.txt'])
# Remove all the selected files
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")],
)
```
You can select input files for upload using the [`method: Page.setInputFiles`] method. It expects first argument to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) with the type `"file"`. Multiple files can be passed in the array. If some of the file paths are relative, they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd). Empty array clears the selected files.
#### Example
@ -269,6 +494,14 @@ For the dynamic pages that handle focus events, you can focus the given element.
await page.focus('input#name');
```
```python-async
await page.focus('input#name')
```
```python-sync
page.focus('input#name')
```
#### API reference
- [`method: Page.focus`]