diff --git a/docs/src/api/python.md b/docs/src/api/python.md
index c64fdc5cfca90..358b12149aba9 100644
--- a/docs/src/api/python.md
+++ b/docs/src/api/python.md
@@ -124,13 +124,13 @@ Performs action and waits for given `event` to fire. If predicate is provided, i
event's value into the `predicate` function and waits for `predicate(event)` to return a truthy value.
Will throw an error if the page is closed before the `event` is fired.
-```python-async
+```python async
async with page.expect_event(event_name) as event_info:
await page.click("button")
value = await event_info.value
```
-```python-sync
+```python sync
with page.expect_event(event_name) as event_info:
page.click("button")
value = event_info.value
@@ -148,13 +148,13 @@ Performs action and waits for given `event` to fire. If predicate is provided, i
event's value into the `predicate` function and waits for `predicate(event)` to return a truthy value.
Will throw an error if browser context is closed before the `event` is fired.
-```python-async
+```python async
async with context.expect_event(event_name) as event_info:
await context.click("button")
value = await event_info.value
```
-```python-sync
+```python sync
with context.expect_event(event_name) as event_info:
context.click("button")
value = event_info.value
@@ -172,13 +172,13 @@ Performs action and waits for given `event` to fire. If predicate is provided, i
event's value into the `predicate` function and waits for `predicate(event)` to return a truthy value.
Will throw an error if the socket is closed before the `event` is fired.
-```python-async
+```python async
async with ws.expect_event(event_name) as event_info:
await ws.click("button")
value = await event_info.value
```
-```python-sync
+```python sync
with ws.expect_event(event_name) as event_info:
ws.click("button")
value = event_info.value
@@ -195,13 +195,13 @@ value = event_info.value
Performs action and waits for the required load state. It resolves when the page reaches a required load state, `load` by default. The navigation must have been committed when this method is called. If current document has
already reached the required state, resolves immediately.
-```python-async
+```python async
async with page.expect_load_state():
await page.click('button') # Click triggers navigation.
# Context manager waits for 'load' event.
```
-```python-sync
+```python sync
with page.expect_load_state():
page.click('button') # Click triggers navigation.
# Context manager waits for 'load' event.
@@ -219,13 +219,13 @@ Shortcut for main frame's [`method: Frame.expectLoadState`].
Performs action and waits for the required load state. It resolves when the page reaches a required load state, `load` by default. The navigation must have been committed when this method is called. If current document has
already reached the required state, resolves immediately.
-```python-async
+```python async
async with frame.expect_load_state():
await frame.click('button') # Click triggers navigation.
# Context manager waits for 'load' event.
```
-```python-sync
+```python sync
with frame.expect_load_state():
frame.click('button') # Click triggers navigation.
# Context manager waits for 'load' event.
@@ -238,7 +238,7 @@ with frame.expect_load_state():
* langs: python
- returns: <[EventContextManager]>
-Performs action and wait for the next navigation. In case of multiple redirects, the navigation will resolve with
+Performs action and waits for the next navigation. In case of multiple redirects, the navigation will resolve with
the response of the last redirect. In case of navigation to a different anchor or navigation due to History API
usage, the navigation will resolve with `null`.
@@ -246,13 +246,13 @@ This resolves when the page navigates to a new URL or reloads. It is useful for
indirectly cause the page to navigate. e.g. The click target has an `onclick` handler that triggers navigation
from a `setTimeout`. Consider this example:
-```python-async
+```python async
async with page.expect_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
+```python sync
with page.expect_navigation():
page.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
# Context manager waited for the navigation to happen.
@@ -271,7 +271,7 @@ Shortcut for main frame's [`method: Frame.expectNavigation`].
* langs: python
- returns: <[EventContextManager]>
-Performs action and wait for the next navigation. In case of multiple redirects, the navigation will resolve with
+Performs action and waits for the next navigation. In case of multiple redirects, the navigation will resolve with
the response of the last redirect. In case of navigation to a different anchor or navigation due to History API
usage, the navigation will resolve with `null`.
@@ -279,13 +279,13 @@ This resolves when the page navigates to a new URL or reloads. It is useful for
indirectly cause the page to navigate. e.g. The click target has an `onclick` handler that triggers navigation
from a `setTimeout`. Consider this example:
-```python-async
+```python async
async with frame.expect_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
+```python sync
with frame.expect_navigation():
frame.click("a.delayed-navigation") # Clicking the link will indirectly cause a navigation
# Context manager waited for the navigation to happen.
diff --git a/docs/src/assertions.md b/docs/src/assertions.md
index 40249b320e165..bd88e6e558a2e 100644
--- a/docs/src/assertions.md
+++ b/docs/src/assertions.md
@@ -35,7 +35,7 @@ const checked = await page.getAttribute('input', 'checked');
assert(checked);
```
-```python-async
+```python async
# Assert text content
content = await page.text_content('nav:first-child')
assert content == 'home'
@@ -53,7 +53,7 @@ checked = await page.get_attribute('input', 'checked')
assert checked
```
-```python-sync
+```python sync
# Assert text content
content = page.text_content('nav:first-child')
assert content == 'home'
@@ -106,7 +106,7 @@ const classNames = await elementHandle.getAttribute('class');
assert(classNames.includes('highlighted'));
```
-```python-async
+```python async
# Get the element handle
element_handle = page.wait_for_selector('#box')
@@ -119,7 +119,7 @@ class_names = await element_handle.get_attribute('class')
assert 'highlighted' in class_names
```
-```python-sync
+```python sync
# Get the element handle
element_handle = page.wait_for_selector('#box')
@@ -171,7 +171,7 @@ const length = await page.$$eval('li.selected', (items) => items.length);
assert(length === 3);
```
-```python-async
+```python async
# Assert local storage value
user_id = page.evaluate("() => window.localStorage.getItem('user_id')")
assert user_id
@@ -190,7 +190,7 @@ length = await page.eval_on_selector_all('li.selected', '(items) => items.length
assert length == 3
```
-```python-sync
+```python sync
# Assert local storage value
user_id = page.evaluate("() => window.localStorage.getItem('user_id')")
assert user_id
diff --git a/docs/src/auth.md b/docs/src/auth.md
index 3823876738075..536d67e1bed8c 100644
--- a/docs/src/auth.md
+++ b/docs/src/auth.md
@@ -36,7 +36,7 @@ await page.click('text=Submit');
// Verify app is logged in
```
-```python-async
+```python async
page = await context.new_page()
await page.goto('https://github.com/login')
@@ -48,7 +48,7 @@ await page.click('text=Submit')
# Verify app is logged in
```
-```python-sync
+```python sync
page = context.new_page()
page.goto('https://github.com/login')
@@ -88,7 +88,7 @@ const storageState = JSON.parse(process.env.STORAGE);
const context = await browser.newContext({ storageState });
```
-```python-async
+```python async
import json
import os
# Save storage state and store as an env variable
@@ -100,7 +100,7 @@ storage_state = json.loads(os.environ["STORAGE"])
context = await browser.new_context(storage_state=storage_state)
```
-```python-sync
+```python sync
import json
import os
# Save storage state and store as an env variable
@@ -134,7 +134,7 @@ await context.addInitScript(storage => {
}, sessionStorage);
```
-```python-async
+```python async
import os
# Get session storage and store as env variable
session_storage = await page.evaluate("() => JSON.stringify(sessionStorage)")
@@ -152,7 +152,7 @@ await context.add_init_script(storage => {
}, session_storage)
```
-```python-sync
+```python sync
import os
# Get session storage and store as env variable
session_storage = page.evaluate("() => JSON.stringify(sessionStorage)")
@@ -217,7 +217,7 @@ const context = await chromium.launchPersistentContext(userDataDir, { headless:
// Execute login steps manually in the browser window
```
-```python-async
+```python async
import asyncio
from playwright import async_playwright
@@ -230,7 +230,7 @@ async def main():
asyncio.get_event_loop().run_until_complete(main())
```
-```python-sync
+```python sync
from playwright import sync_playwright
with sync_playwright() as p:
diff --git a/docs/src/ci.md b/docs/src/ci.md
index 90d5ad37209e9..f6529aaa029b6 100644
--- a/docs/src/ci.md
+++ b/docs/src/ci.md
@@ -50,13 +50,13 @@ Suggested configuration
});
```
- ```python-async
+ ```python async
browser = await playwright.chromium.launch(
args=['--disable-dev-shm-usage']
)
```
- ```python-sync
+ ```python sync
browser = playwright.chromium.launch({
args=['--disable-dev-shm-usage']
})
@@ -203,11 +203,11 @@ const { chromium } = require('playwright');
const browser = await chromium.launch({ chromiumSandbox: false });
```
-```python-async
+```python async
browser = await playwright.chromium.launch(chromiumSandbox=False)
```
-```python-sync
+```python sync
browser = playwright.chromium.launch(chromiumSandbox=False)
```
@@ -287,7 +287,7 @@ const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: false });
```
-```python-async
+```python async
import asyncio
from playwright import async_playwright
@@ -299,7 +299,7 @@ async def main():
asyncio.get_event_loop().run_until_complete(main())
```
-```python-sync
+```python sync
from playwright import sync_playwright
with sync_playwright() as p:
diff --git a/docs/src/core-concepts.md b/docs/src/core-concepts.md
index 7f7b289125ed5..6aa2bdcf36ad9 100644
--- a/docs/src/core-concepts.md
+++ b/docs/src/core-concepts.md
@@ -29,7 +29,7 @@ const browser = await chromium.launch({ headless: false });
await browser.close();
```
-```python-async
+```python async
import asyncio
from playwright import async_playwright
@@ -41,7 +41,7 @@ async def main():
asyncio.get_event_loop().run_until_complete(main())
```
-```python-sync
+```python sync
from playwright import sync_playwright
with sync_playwright() as p:
@@ -69,12 +69,12 @@ const browser = await chromium.launch();
const context = await browser.newContext();
```
-```python-async
+```python async
browser = await playwright.chromium.launch()
context = await browser.new_context()
```
-```python-sync
+```python sync
browser = playwright.chromium.launch()
context = browser.new_context()
```
@@ -95,7 +95,7 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
import asyncio
from playwright import async_playwright
@@ -116,7 +116,7 @@ async def main():
asyncio.get_event_loop().run_until_complete(main())
```
-```python-sync
+```python sync
from playwright import sync_playwright
with sync_playwright() as p:
@@ -161,7 +161,7 @@ console.log(page.url());
window.location.href = 'https://example.com';
```
-```python-async
+```python async
page = await context.new_page()
# Navigate explicitly, similar to entering a URL in the browser.
@@ -178,7 +178,7 @@ print(page.url)
# window.location.href = 'https://example.com'
```
-```python-sync
+```python sync
page = context.new_page()
# Navigate explicitly, similar to entering a URL in the browser.
@@ -219,7 +219,7 @@ const frame = await frameElementHandle.contentFrame();
await frame.fill('#username-input', 'John');
```
-```python-async
+```python async
# Get frame using the frame's name attribute
frame = page.frame('frame-login')
@@ -234,7 +234,7 @@ frame = await frame_element_handle.content_frame()
await frame.fill('#username-input', 'John')
```
-```python-sync
+```python sync
# Get frame using the frame's name attribute
frame = page.frame('frame-login')
@@ -274,12 +274,12 @@ Some examples below:
await page.click('data-test-id=foo');
```
-```python-async
+```python async
# Using data-test-id= selector engine
await page.click('data-test-id=foo')
```
-```python-sync
+```python sync
# Using data-test-id= selector engine
page.click('data-test-id=foo')
```
@@ -290,13 +290,13 @@ await page.click('div');
await page.click('//html/body/div');
```
-```python-async
+```python async
# CSS and XPath selector engines are automatically detected
await page.click('div')
await page.click('//html/body/div')
```
-```python-sync
+```python sync
# CSS and XPath selector engines are automatically detected
page.click('div')
page.click('//html/body/div')
@@ -307,12 +307,12 @@ page.click('//html/body/div')
await page.click('text=Hello w');
```
-```python-async
+```python async
# Find node by text substring
await page.click('text=Hello w')
```
-```python-sync
+```python sync
# Find node by text substring
page.click('text=Hello w')
```
@@ -323,13 +323,13 @@ await page.click('css=div');
await page.click('xpath=//html/body/div');
```
-```python-async
+```python async
# Explicit CSS and XPath notation
await page.click('css=div')
await page.click('xpath=//html/body/div')
```
-```python-sync
+```python sync
# Explicit CSS and XPath notation
page.click('css=div')
page.click('xpath=//html/body/div')
@@ -340,12 +340,12 @@ page.click('xpath=//html/body/div')
await page.click('css:light=div');
```
-```python-async
+```python async
# Only search light DOM, outside WebComponent shadow DOM:
await page.click('css:light=div')
```
-```python-sync
+```python sync
# Only search light DOM, outside WebComponent shadow DOM:
page.click('css:light=div')
```
@@ -357,12 +357,12 @@ Selectors using the same or different engines can be combined using the `>>` sep
await page.click('#free-month-promo >> text=Sign Up');
```
-```python-async
+```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
+```python sync
# Click an element with text 'Sign Up' inside of a #free-month-promo.
page.click('#free-month-promo >> text=Sign Up')
```
@@ -372,12 +372,12 @@ page.click('#free-month-promo >> text=Sign Up')
const sectionText = await page.$eval('*css=section >> text=Selectors', e => e.textContent);
```
-```python-async
+```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
+```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')
```
@@ -401,12 +401,12 @@ and [actionable](./actionability.md). For example, click will:
await page.fill('#search', 'query');
```
-```python-async
+```python async
# Playwright waits for #search element to be in the DOM
await page.fill('#search', 'query')
```
-```python-sync
+```python sync
# Playwright waits for #search element to be in the DOM
page.fill('#search', 'query')
```
@@ -417,13 +417,13 @@ page.fill('#search', 'query')
await page.click('#search');
```
-```python-async
+```python async
# Playwright waits for element to stop animating
# and accept clicks.
await page.click('#search')
```
-```python-sync
+```python sync
# Playwright waits for element to stop animating
# and accept clicks.
page.click('#search')
@@ -438,14 +438,14 @@ await page.waitForSelector('#search', { state: 'attached' });
await page.waitForSelector('#promo');
```
-```python-async
+```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
+```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`.
@@ -461,14 +461,14 @@ await page.waitForSelector('#details', { state: 'hidden' });
await page.waitForSelector('#promo', { state: 'detached' });
```
-```python-async
+```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
+```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.
@@ -495,11 +495,11 @@ 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
+```python async
href = await page.evaluate('() => document.location.href')
```
-```python-sync
+```python sync
href = page.evaluate('() => document.location.href')
```
@@ -512,14 +512,14 @@ const status = await page.evaluate(async () => {
});
```
-```python-async
+```python async
status = await page.evaluate("""async () => {
response = await fetch(location.href)
return response.status
}""")
```
-```python-sync
+```python sync
status = page.evaluate("""async () => {
response = fetch(location.href)
return response.status
@@ -573,7 +573,7 @@ await page.evaluate(
{ button1, list: [button2], foo: null });
```
-```python-async
+```python async
# A primitive value.
await page.evaluate('num => num', 42)
@@ -616,7 +616,7 @@ await page.evaluate("""
{ 'button1': button1, 'list': [button2], 'foo': None })
```
-```python-sync
+```python sync
# A primitive value.
page.evaluate('num => num', 42)
@@ -668,7 +668,7 @@ const result = await page.evaluate(data => {
}, data);
```
-```python-async
+```python async
data = { 'text': 'some data', 'value': 1 }
# Pass |data| as a parameter.
result = await page.evaluate("""data => {
@@ -676,7 +676,7 @@ result = await page.evaluate("""data => {
}""", data)
```
-```python-sync
+```python sync
data = { 'text': 'some data', 'value': 1 }
# Pass |data| as a parameter.
result = page.evaluate("""data => {
@@ -694,7 +694,7 @@ const result = await page.evaluate(() => {
});
```
-```python-async
+```python async
data = { 'text': 'some data', 'value': 1 }
result = await page.evaluate("""() => {
# There is no |data| in the web page.
@@ -702,7 +702,7 @@ result = await page.evaluate("""() => {
}""")
```
-```python-sync
+```python sync
data = { 'text': 'some data', 'value': 1 }
result = page.evaluate("""() => {
# There is no |data| in the web page.
@@ -744,13 +744,13 @@ const ulElementHandle = await page.$('ul');
await ulElementHandle.evaluate(ulElement => getComputedStyle(ulElement).getPropertyValue('display'));
```
-```python-async
+```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
+```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')")
@@ -764,12 +764,12 @@ const ulElementHandle = await page.$('ul');
await page.evaluate(uiElement => getComputedStyle(uiElement).getPropertyValue('display'), uiElement);
```
-```python-async
+```python async
ul_element_handle = await page.query_selector('ul')
await page.evaluate("uiElement => getComputedStyle(uiElement).getPropertyValue('display')", uiElement)
```
-```python-sync
+```python sync
ul_element_handle = page.query_selector('ul')
page.evaluate("uiElement => getComputedStyle(uiElement).getPropertyValue('display')", uiElement)
```
@@ -803,7 +803,7 @@ const newLength = await page.evaluate(() => window.myArray.length);
await myArrayHandle.dispose();
```
-```python-async
+```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("""() => {
@@ -830,7 +830,7 @@ new_length = await page.evaluate("() => window.myArray.length")
await my_array_handle.dispose()
```
-```python-sync
+```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("""() => {
diff --git a/docs/src/debug.md b/docs/src/debug.md
index 8630690e2adf4..0d5718a7a81a3 100644
--- a/docs/src/debug.md
+++ b/docs/src/debug.md
@@ -19,17 +19,17 @@ to slow down execution and follow along while debugging.
await chromium.launch({ headless: false, slowMo: 100 }); // or firefox, webkit
```
-```python-async
+```python async
await chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
```
-```python-sync
+```python sync
chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
```
-## Visual Studio Code debugger
+## Visual Studio Code debugger (Node.JS)
The VS Code debugger can be used to pause and resume execution of Playwright
scripts with breakpoints. The debugger can be configured in two ways.
@@ -40,18 +40,13 @@ Setup [`launch.json` configuration](https://code.visualstudio.com/docs/nodejs/no
for your Node.js project. Once configured launch the scripts with F5 and use
breakpoints.
-### Use the new JavaScript debugger
+### Use the new JavaScript debugging terminal
-VS Code 1.46+ introduces the new JavaScript debugger behind a feature flag. The
-new debugger does not require a `launch.json` configuration. To use this:
-
-1. Enable the preview debugger
- * Open JSON settings and add `"debug.javascript.usePreview": true`
- * Open settings UI and enable the `Debug › JavaScript: Use Preview` setting
+VS Code 1.46+ introduced the new JavaScript debugger that does not require a `launch.json`
+configuration. To use it:
1. Set a breakpoint in VS Code
* Use the `debugger` keyword or set a breakpoint in the VS Code UI
-
1. Run your Node.js script from the terminal
## Browser Developer Tools
@@ -76,12 +71,12 @@ In Chromium, you can also open developer tools through a launch option.
await chromium.launch({ devtools: true });
```
-```python-async
+```python async
await chromium.launch(devtools=True)
```
-```python-sync
+```python sync
chromium.launch(devtools=True)
```
diff --git a/docs/src/docker.md b/docs/src/docker.md
index 92410a6821c76..d93f1a0c5c848 100644
--- a/docs/src/docker.md
+++ b/docs/src/docker.md
@@ -3,7 +3,7 @@ id: docker
title: "Docker"
---
-[Dockerfile.bionic](https://github.com/microsoft/playwright/blob/master/utils/docker/Dockerfile.bionic) and [Dockerfile.focal](https://github.com/microsoft/playwright/blob/master/utils/docker/Dockerfile.focal) can be used to run Playwright scripts in Docker environments. These images includes all the dependencies needed to run browsers in a Docker container, including browsers.
+[Dockerfile.bionic](https://github.com/microsoft/playwright/blob/master/utils/docker/Dockerfile.bionic) and [Dockerfile.focal](https://github.com/microsoft/playwright/blob/master/utils/docker/Dockerfile.focal) can be used to run Playwright scripts in Docker environments. These images includes all the dependencies needed to run browsers in a Docker container, and also include the browsers themselves.
diff --git a/docs/src/emulation.md b/docs/src/emulation.md
index e6368c77b0ff9..691ff5b9c7aad 100644
--- a/docs/src/emulation.md
+++ b/docs/src/emulation.md
@@ -29,7 +29,7 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
import asyncio
from playwright import async_playwright
@@ -44,7 +44,7 @@ async def main():
asyncio.get_event_loop().run_until_complete(main())
```
-```python-sync
+```python sync
from playwright import sync_playwright
with sync_playwright() as p:
@@ -74,13 +74,13 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
context = await browser.new_context(
user_agent='My user agent'
)
```
-```python-sync
+```python sync
context = browser.new_context(
user_agent='My user agent'
)
@@ -112,7 +112,7 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
# Create context with given viewport
context = await browser.new_context(
viewport={ 'width': 1280, 'height': 1024 }
@@ -128,7 +128,7 @@ context = await browser.new_context(
)
```
-```python-sync
+```python sync
# Create context with given viewport
context = browser.new_context(
viewport={ 'width': 1280, 'height': 1024 }
@@ -161,7 +161,7 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
# Emulate locale and time
context = await browser.new_context(
locale='de-DE',
@@ -169,7 +169,7 @@ context = await browser.new_context(
)
```
-```python-sync
+```python sync
# Emulate locale and time
context = browser.new_context(
locale='de-DE',
@@ -193,13 +193,13 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
context = await browser.new_context(
permissions=['notifications'],
)
```
-```python-sync
+```python sync
context = browser.new_context(
permissions=['notifications'],
)
@@ -211,11 +211,11 @@ Grant all pages in the existing context access to current location:
await context.grantPermissions(['geolocation']);
```
-```python-async
+```python async
await context.grant_permissions(['geolocation'])
```
-```python-sync
+```python sync
context.grant_permissions(['geolocation'])
```
@@ -225,11 +225,11 @@ Grant notifications access from a specific domain:
await context.grantPermissions(['notifications'], {origin: 'https://skype.com'} );
```
-```python-async
+```python async
await context.grant_permissions(['notifications'], origin='https://skype.com')
```
-```python-sync
+```python sync
context.grant_permissions(['notifications'], origin='https://skype.com')
```
@@ -239,11 +239,11 @@ Revoke all permissions:
await context.clearPermissions();
```
-```python-async
+```python async
await context.clear_permissions()
```
-```python-sync
+```python sync
context.clear_permissions()
```
@@ -265,14 +265,14 @@ const context = await browser.newContext({
});
```
-```python-async
+```python async
context = await browser.new_context(
geolocation={"longitude": 48.858455, "latitude": 2.294474},
permissions=["geolocation"]
)
```
-```python-sync
+```python sync
context = browser.new_context(
geolocation={"longitude": 48.858455, "latitude": 2.294474},
permissions=["geolocation"]
@@ -285,11 +285,11 @@ Change the location later:
await context.setGeolocation({ longitude: 29.979097, latitude: 31.134256 });
```
-```python-async
+```python async
await context.set_geolocation({"longitude": 29.979097, "latitude": 31.134256})
```
-```python-sync
+```python sync
context.set_geolocation({"longitude": 29.979097, "latitude": 31.134256})
```
@@ -325,7 +325,7 @@ await page.emulateMedia({ colorScheme: 'dark' });
await page.emulateMedia({ media: 'print' });
```
-```python-async
+```python async
# Create context with dark mode
context = await browser.new_context(
color_scheme='dark' # or 'light'
@@ -343,7 +343,7 @@ await page.emulate_media(color_scheme='dark')
await page.emulate_media(media='print')
```
-```python-sync
+```python sync
# Create context with dark mode
context = browser.new_context(
color_scheme='dark' # or 'light'
diff --git a/docs/src/input.md b/docs/src/input.md
index f8640abb7842c..4705a6d14554d 100644
--- a/docs/src/input.md
+++ b/docs/src/input.md
@@ -26,7 +26,7 @@ await page.fill('#local', '2020-03-02T05:15');
await page.fill('text=First Name', 'Peter');
```
-```python-async
+```python async
# Text input
await page.fill('#name', 'Peter')
@@ -43,7 +43,7 @@ await page.fill('#local', '2020-03-02T05:15')
await page.fill('text=First Name', 'Peter')
```
-```python-sync
+```python sync
# Text input
page.fill('#name', 'Peter')
@@ -83,7 +83,7 @@ await page.uncheck('#subscribe-label');
await page.check('text=XL');
```
-```python-async
+```python async
# Check the checkbox
await page.check('#agree')
@@ -94,7 +94,7 @@ await page.uncheck('#subscribe-label')
await page.check('text=XL')
```
-```python-sync
+```python sync
# Check the checkbox
page.check('#agree')
@@ -136,7 +136,7 @@ const option = await page.$('#best-option');
await page.selectOption('select#colors', option);
```
-```python-async
+```python async
# Single selection matching the value
await page.select_option('select#colors', 'blue')
@@ -151,7 +151,7 @@ option = await page.query_selector('#best-option')
await page.select_option('select#colors', option)
```
-```python-sync
+```python sync
# Single selection matching the value
page.select_option('select#colors', 'blue')
@@ -198,7 +198,7 @@ await page.hover('#item');
await page.click('#item', { position: { x: 0, y: 0} });
```
-```python-async
+```python async
# Generic click
await page.click('button#submit')
@@ -218,7 +218,7 @@ await page.hover('#item')
await page.click('#item', position={ 'x': 0, 'y': 0})
```
-```python-sync
+```python sync
# Generic click
page.click('button#submit')
@@ -255,11 +255,11 @@ Sometimes, apps use non-trivial logic where hovering the element overlays it wit
await page.click('button#submit', { force: true });
```
-```python-async
+```python async
await page.click('button#submit', force=True)
```
-```python-sync
+```python sync
page.click('button#submit', force=True)
```
@@ -271,11 +271,11 @@ If you are not interested in testing your app under the real conditions and want
await page.dispatchEvent('button#submit', 'click');
```
-```python-async
+```python async
await page.dispatch_event('button#submit', 'click')
```
-```python-sync
+```python sync
page.dispatch_event('button#submit', 'click')
```
@@ -305,12 +305,12 @@ 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
+```python async
# Type character by character
await page.type('#area', 'Hello World!')
```
-```python-sync
+```python sync
# Type character by character
page.type('#area', 'Hello World!')
```
@@ -341,7 +341,7 @@ await page.press('#name', 'Control+ArrowRight');
await page.press('#value', '$');
```
-```python-async
+```python async
# Hit Enter
await page.press('#submit', 'Enter')
@@ -352,7 +352,7 @@ await page.press('#name', 'Control+ArrowRight')
await page.press('#value', '$')
```
-```python-sync
+```python sync
# Hit Enter
page.press('#submit', 'Enter')
@@ -386,7 +386,7 @@ await page.press('#name', 'Shift+A');
await page.press('#name', 'Shift+ArrowLeft');
```
-```python-async
+```python async
#
await page.press('#name', 'Shift+A')
@@ -394,7 +394,7 @@ await page.press('#name', 'Shift+A')
await page.press('#name', 'Shift+ArrowLeft')
```
-```python-sync
+```python sync
#
page.press('#name', 'Shift+A')
@@ -436,7 +436,7 @@ await page.setInputFiles('input#upload', {
});
```
-```python-async
+```python async
from playwright.async_api import FilePayload
# Select one file
await page.set_input_files('input#upload', 'myfile.pdf')
@@ -454,7 +454,7 @@ await page.set_input_files(
)
```
-```python-sync
+```python sync
from playwright.sync_api import FilePayload
# Select one file
page.set_input_files('input#upload', 'myfile.pdf')
@@ -494,11 +494,11 @@ For the dynamic pages that handle focus events, you can focus the given element.
await page.focus('input#name');
```
-```python-async
+```python async
await page.focus('input#name')
```
-```python-sync
+```python sync
page.focus('input#name')
```
diff --git a/docs/src/installation.md b/docs/src/installation.md
index eb63d6aa697ec..310137febe62d 100644
--- a/docs/src/installation.md
+++ b/docs/src/installation.md
@@ -1,6 +1,6 @@
---
id: installation
-title: "Advanced installation"
+title: "Installation"
---
diff --git a/docs/src/intro-python.md b/docs/src/intro-python.md
new file mode 100644
index 0000000000000..6cf8ae390c37e
--- /dev/null
+++ b/docs/src/intro-python.md
@@ -0,0 +1,86 @@
+---
+id: intro-python
+title: "Getting Started"
+---
+
+
+
+## Installation
+
+Use pip to install Playwright in your Python project. See [system requirements](#system-requirements).
+
+```sh
+pip install playwright
+python -m playwright install
+```
+
+These commands download the Playwright package and install browser binaries for Chromium, Firefox and WebKit. To modify this behavior see [installation parameters](./installation.md).
+
+## Usage
+
+Once installed, you can `import` Playwright in a Python script, and launch any of the 3 browsers (`chromium`, `firefox` and `webkit`).
+
+```python
+from playwright import sync_playwright
+
+with sync_playwright() as p:
+ browser = p.chromium.launch()
+ page = browser.new_page()
+ # interact with UI elements, assert values
+ browser.close()
+```
+
+Playwright supports two variations of the API: synchronous are asynchronous. If your modern project uses
+[asyncio](https://docs.python.org/3/library/asyncio.html), you should use async API:
+
+```python
+from playwright import async_playwright
+
+with async_playwright() as p:
+ browser = await p.chromium.launch()
+ page = await browser.new_page()
+ # interact with UI elements, assert values
+ await browser.close()
+```
+
+## First script
+
+In our first script, we will navigate to `whatsmyuseragent.org` and take a screenshot in WebKit.
+
+```python
+from playwright import sync_playwright
+
+with sync_playwright() as p:
+ browser = p.webkit.launch()
+ page = await browser.new_page()
+ page.goto("http://whatsmyuseragent.org/")
+ page.screenshot(path="example.png")
+ browser.close()
+```
+
+By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless=False` flag while launching the browser. You can also use `slowMo` to slow down execution. Learn more in the debugging tools [section](./debug.md).
+
+```python
+firefox.launch(headless=False, slowMo=50)
+```
+
+## Record scripts
+
+Command Line Interface [CLI](./cli.md) can be used to record user interactions and generate Python code.
+
+```sh
+python -m playwright codegen wikipedia.org
+```
+
+## System requirements
+
+Playwright requires Python version 3.7 or above. The browser binaries for Chromium,
+Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
+
+* **Windows**: Works with Windows and Windows Subsystem for Linux (WSL).
+* **macOS**: Requires 10.14 or above.
+* **Linux**: Depending on your Linux distribution, you might need to install additional
+ dependencies to run the browsers.
+ * Firefox requires Ubuntu 18.04+
+ * For Ubuntu 18.04, the additional dependencies are defined in [our Docker image](https://github.com/microsoft/playwright/blob/master/utils/docker/Dockerfile.bionic),
+ which is based on Ubuntu.
diff --git a/docs/src/intro.md b/docs/src/intro.md
index ed090beb76a16..5f4f9d3fff085 100644
--- a/docs/src/intro.md
+++ b/docs/src/intro.md
@@ -54,7 +54,7 @@ const { webkit } = require('playwright');
})();
```
-By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless: false` flag while launching the browser. You can also use `slowMo` to slow down execution.
+By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless: false` flag while launching the browser. You can also use `slowMo` to slow down execution. Learn more in the debugging tools [section](./debug.md).
```js
firefox.launch({ headless: false, slowMo: 50 });
@@ -62,7 +62,7 @@ firefox.launch({ headless: false, slowMo: 50 });
## Record scripts
-[Playwright CLI](./cli.md) can be used to record user interactions and generate JavaScript code.
+Command Line Interface [CLI](./cli.md) can be used to record user interactions and generate JavaScript code.
```sh
npx playwright codegen wikipedia.org
diff --git a/docs/src/test-runners-python.md b/docs/src/test-runners-python.md
new file mode 100644
index 0000000000000..b9f5c99bb3f22
--- /dev/null
+++ b/docs/src/test-runners-python.md
@@ -0,0 +1,210 @@
+---
+id: test-runners-python
+title: "Test Runners"
+---
+
+You can use our [Pytest integration](https://github.com/microsoft/playwright-pytest) to write end-to-end tests
+in Python.
+
+
+
+## Usage
+
+```sh
+pip install pytest-playwright
+```
+
+Use the `page` fixture to write a basic test. See [more examples](#examples).
+
+```py
+def test_example_is_working(page):
+ page.goto("https://example.com")
+ assert page.innerText('h1') == 'Example Domain'
+ page.click("text=More information")
+```
+
+To run your tests, use pytest CLI.
+
+```sh
+# Run tests (Chromium and headless by default)
+pytest
+
+# Run tests in headful mode
+pytest --headful
+
+# Run tests in a different browser (chromium, firefox, webkit)
+pytest --browser firefox
+
+# Run tests in multiple browsers
+pytest --browser chromium --browser webkit
+```
+
+If you want to add the CLI arguments automatically without specifying them, you can use the [pytest.ini](https://docs.pytest.org/en/stable/reference.html#ini-options-ref) file:
+
+```ini
+# content of pytest.ini
+[pytest]
+# Run firefox with UI
+addopts = --headful --browser firefox
+
+```
+
+## Fixtures
+
+This plugin configures Playwright-specific [fixtures for pytest](https://docs.pytest.org/en/latest/fixture.html). To use these fixtures, use the fixture name as an argument to the test function.
+
+```py
+def test_my_app_is_working(fixture_name):
+ # Test using fixture_name
+ # ...
+```
+
+**Function scope**: These fixtures are created when requested in a test function and destroyed when the test ends.
+
+- `context`: New [browser context](https://playwright.dev/#path=docs%2Fcore-concepts.md&q=browser-contexts) for a test.
+- `page`: New [browser page](https://playwright.dev/#path=docs%2Fcore-concepts.md&q=pages-and-frames) for a test.
+
+**Session scope**: These fixtures are created when requested in a test function and destroyed when all tests end.
+
+- `browser`: Browser instance launched by Playwright.
+- `browser_name`: Browser name as string.
+- `is_chromium`, `is_webkit`, `is_firefox`: Booleans for the respective browser types.
+
+**Customizing fixture options**: For `browser` and `context` fixtures, use the the following fixtures to define custom launch options.
+
+- `browser_type_launch_args`: Override launch arguments for [`browserType.launch()`](https://playwright.dev/#path=docs%2Fapi.md&q=browsertypelaunchoptions). It should return a Dict.
+- `browser_context_args`: Override the options for [`browser.newContext()`](https://playwright.dev/#path=docs%2Fapi.md&q=browsernewcontextoptions). It should return a Dict.
+
+## Examples
+
+### Configure Mypy typings for auto-completion
+
+```py
+from playwright.sync_api import Page
+
+def test_visit_admin_dashboard(page: Page):
+ page.goto("/admin")
+ # ...
+```
+
+### Skip test by browser
+
+```py
+import pytest
+
+@pytest.mark.skip_browser("firefox")
+def test_visit_example(page):
+ page.goto("https://example.com")
+ # ...
+```
+
+### Run on a specific browser
+
+```py
+import pytest
+
+@pytest.mark.only_browser("chromium")
+def test_visit_example(page):
+ page.goto("https://example.com")
+ # ...
+```
+
+### Configure base-url
+
+Start Pytest with the `base-url` argument.
+
+```sh
+pytest --base-url http://localhost:8080
+```
+
+```py
+def test_visit_example(page):
+ page.goto("/admin")
+ # -> Will result in http://localhost:8080/admin
+```
+
+### Ignore HTTPS errors
+
+conftest.py
+
+```py
+import pytest
+
+@pytest.fixture(scope="session")
+def browser_context_args(browser_context_args):
+ return {
+ **browser_context_args,
+ "ignoreHTTPSErrors": True
+ }
+```
+
+### Use custom viewport size
+
+conftest.py
+
+```py
+import pytest
+
+@pytest.fixture(scope="session")
+def browser_context_args(browser_context_args):
+ return {
+ **browser_context_args,
+ "viewport": {
+ "width": 1920,
+ "height": 1080,
+ }
+ }
+```
+
+### Device emulation
+
+conftest.py
+
+```py
+import pytest
+
+@pytest.fixture(scope="session")
+def browser_context_args(browser_context_args, playwright):
+ iphone_11 = playwright.devices['iPhone 11 Pro']
+ return {
+ **browser_context_args,
+ **iphone_11,
+ }
+```
+
+## Debugging
+
+### Use with pdb
+
+Use the `breakpoint()` statement in your test code to pause execution and get a [pdb](https://docs.python.org/3/library/pdb.html) REPL.
+
+```py
+def test_bing_is_working(page):
+ page.goto("https://bing.com")
+ breakpoint()
+ # ...
+```
+
+### Screenshot on test failure
+
+You can capture screenshots for failed tests with a [pytest runtest hook](https://docs.pytest.org/en/6.1.0/reference.html?highlight=pytest_runtest_makereport#test-running-runtest-hooks). Add this to your `conftest.py` file.
+
+Note that this snippet uses `slugify` to convert test names to file paths, which can be installed with `pip install python-slugify`.
+
+```py
+# In conftest.py
+from slugify import slugify
+from pathlib import Path
+
+def pytest_runtest_makereport(item, call) -> None:
+ if call.when == "call":
+ if call.excinfo is not None and "page" in item.funcargs:
+ page = item.funcargs["page"]
+ screenshot_dir = Path(".playwright-screenshots")
+ screenshot_dir.mkdir(exist_ok=True)
+ page.screenshot(path=str(screenshot_dir / f"{slugify(item.nodeid)}.png"))
+```
+
+## Deploy to CI
+
+Use the [Playwright GitHub Action](https://github.com/microsoft/playwright-github-action) or [guides for other CI providers](https://playwright.dev/#path=docs%2Fci.md&q=) to deploy your tests to CI/CD
diff --git a/docs/src/why-playwright.md b/docs/src/why-playwright.md
index 296a8d224d50b..3f5084f49f4e0 100644
--- a/docs/src/why-playwright.md
+++ b/docs/src/why-playwright.md
@@ -41,7 +41,8 @@ Playwright enables fast, reliable and capable automation across all modern brows
* **Debugging tools**. Playwright works with the [editor debugger and browser developer tools](./debug.md) to pause execution and inspect the web page.
-* **Language bindings**. Playwright is also available in [Python](https://github.com/microsoft/playwright-python) and [C#](https://github.com/microsoft/playwright-sharp). Learn about [supported languages](./languages.md).
+* **Language bindings**. Playwright is available for [Node.js](https://github.com/microsoft/playwright) [Python](https://github.com/microsoft/playwright-python), [C#](https://github.com/microsoft/playwright-sharp) and
+[Java](https://github.com/microsoft/playwright-java). Learn more about [supported languages](./languages.md).
* **Deploy tests to CI**. First-party [Docker image](./docker.md) and [GitHub Actions](https://github.com/microsoft/playwright-github-action) to deploy tests to [your preferred CI/CD provider](./ci.md).
diff --git a/utils/markdown.js b/utils/markdown.js
index fb092fbb1b31a..c2bd5034aa90f 100644
--- a/utils/markdown.js
+++ b/utils/markdown.js
@@ -342,7 +342,7 @@ function generateToc(nodes) {
if (node.type === 'h1' || node.type === 'h2') {
let link = node.text.toLowerCase();
link = link.replace(/[ ]+/g, '-');
- link = link.replace(/[^\w-]/g, '');
+ link = link.replace(/[^\w-_]/g, '');
result.push(`${' '.repeat(depth * 2)}- [${node.text}](#${link})`);
}
});