From 77bfcd2c7457367cb9614180f0442485b0d50a95 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Fri, 8 Jan 2021 17:39:33 +0100 Subject: [PATCH] chore: add some Python language snippets (#4933) --- docs/src/assertions.md | 100 ++++++++ docs/src/auth.md | 144 ++++++++++++ docs/src/ci.md | 40 ++++ docs/src/core-concepts.md | 470 ++++++++++++++++++++++++++++++++++++++ docs/src/debug.md | 19 ++ docs/src/emulation.md | 186 +++++++++++++++ docs/src/input.md | 233 +++++++++++++++++++ 7 files changed, 1192 insertions(+) diff --git a/docs/src/assertions.md b/docs/src/assertions.md index 3e9c01950c..40249b320e 100644 --- a/docs/src/assertions.md +++ b/docs/src/assertions.md @@ -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 == '

Result

' + +# 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 == '

Result

' + +# 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`] diff --git a/docs/src/auth.md b/docs/src/auth.md index 9ffb70ee8f..37325af1ab 100644 --- a/docs/src/auth.md +++ b/docs/src/auth.md @@ -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 diff --git a/docs/src/ci.md b/docs/src/ci.md index 4f70afe259..790d83ef8f 100644 --- a/docs/src/ci.md +++ b/docs/src/ci.md @@ -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. ``` diff --git a/docs/src/core-concepts.md b/docs/src/core-concepts.md index 129e8b3a76..7f7b289125 100644 --- a/docs/src/core-concepts.md +++ b/docs/src/core-concepts.md @@ -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') +``` +
## 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] diff --git a/docs/src/debug.md b/docs/src/debug.md index 4a7ff06972..8630690e2a 100644 --- a/docs/src/debug.md +++ b/docs/src/debug.md @@ -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 diff --git a/docs/src/emulation.md b/docs/src/emulation.md index 5f2bf49a24..fc06228e4a 100644 --- a/docs/src/emulation.md +++ b/docs/src/emulation.md @@ -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`] diff --git a/docs/src/input.md b/docs/src/input.md index 5519934bc3..f8640abb78 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -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