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})`); } });