From 9a4d690408508bbcdbb193e980a89f2d2d6b790d Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 1 Mar 2021 12:05:20 -0800 Subject: [PATCH] cherry-pick(release-1.9): add java snippets to the examples in guides (#5638) (#5660) * docs: more clarity in the attribute selectors (#5621) * docs: add java snippets to the examples in guides (#5638) * docs: docs typos (#5658) --- docs/src/assertions.md | 64 +++++++- docs/src/auth.md | 58 ++++++- docs/src/ci.md | 32 ++++ docs/src/cli.md | 21 +++ docs/src/core-concepts.md | 222 ++++++++++++++++++++++++++ docs/src/debug.md | 28 ++++ docs/src/dialogs.md | 18 +++ docs/src/downloads.md | 4 + docs/src/emulation.md | 70 ++++++++ docs/src/extensibility.md | 27 ++++ docs/src/handles.md | 44 +++++ docs/src/input.md | 124 ++++++++++++++ docs/src/inspector.md | 5 + docs/src/multi-pages.md | 64 ++++++++ docs/src/navigations.md | 90 ++++++++++- docs/src/network.md | 82 ++++++++++ docs/src/pom.md | 36 +++++ docs/src/screenshots.md | 16 ++ docs/src/selectors.md | 183 ++++++++++++++++++++- docs/src/verification.md | 49 ++++++ docs/src/videos.md | 29 +++- utils/doclint/generateJavaSnippets.js | 4 +- 22 files changed, 1245 insertions(+), 25 deletions(-) diff --git a/docs/src/assertions.md b/docs/src/assertions.md index b41806b350d5a..5d04de2492dd6 100644 --- a/docs/src/assertions.md +++ b/docs/src/assertions.md @@ -8,7 +8,6 @@ text content of an element. These APIs can be used in your test assertions. - ## Text content ```js @@ -16,6 +15,11 @@ const content = await page.textContent('nav:first-child'); expect(content).toBe('home'); ``` +```java +String content = page.textContent("nav:first-child"); +assertEquals("home", content); +``` + ```python async content = await page.text_content("nav:first-child") assert content == "home" @@ -37,6 +41,11 @@ const text = await page.innerText('.selected'); expect(text).toBe('value'); ``` +```java +String text = page.innerText(".selected"); +assertEquals("value", text); +``` + ```python async text = await page.inner_text(".selected") assert text == "value" @@ -58,6 +67,11 @@ const alt = await page.getAttribute('input', 'alt'); expect(alt).toBe('Text'); ``` +```java +String alt = page.getAttribute("input", "alt"); +assertEquals("Text", alt); +``` + ```python async checked = await page.get_attribute("input", "alt") assert alt == "Text" @@ -75,6 +89,11 @@ const checked = await page.isChecked('input'); expect(checked).toBeTruthy(); ``` +```java +boolean checked = page.isChecked("input"); +assertTrue(checked); +``` + ```python async checked = await page.is_checked("input") assert checked @@ -96,6 +115,11 @@ const content = await page.$eval('nav:first-child', e => e.textContent); expect(content).toBe('home'); ``` +```java +Object content = page.evalOnSelector("nav:first-child", "e => e.textContent"); +assertEquals("home", content); +``` + ```python async content = await page.eval_on_selector("nav:first-child", "e => e.textContent") assert content == "home" @@ -117,6 +141,11 @@ const html = await page.innerHTML('div.result'); expect(html).toBe('

Result

'); ``` +```java +String html = page.innerHTML("div.result"); +assertEquals("

Result

", html); +``` + ```python async html = await page.inner_html("div.result") assert html == "

Result

" @@ -138,6 +167,11 @@ const visible = await page.isVisible('input'); expect(visible).toBeTruthy(); ``` +```java +boolean visible = page.isVisible("input"); +assertTrue(visible); +``` + ```python async visible = await page.is_visible("input") assert visible @@ -156,7 +190,12 @@ assert visible ```js const enabled = await page.isEnabled('input'); -expect(visible).toBeTruthy(); +expect(enabled).toBeTruthy(); +``` + +```java +boolean enabled = page.isEnabled("input"); +assertTrue(enabled); ``` ```python async @@ -198,6 +237,25 @@ const length = await page.$$eval('li.selected', (items) => items.length); expect(length === 3).toBeTruthy(); ``` +```java +// Assert local storage value +Object userId = page.evaluate("() => window.localStorage.getItem('userId')"); +assertNotNull(userId); + +// Assert value for input element +page.waitForSelector("#search"); +Object value = page.evalOnSelector("#search", "el => el.value"); +assertEquals("query", value); + +// Assert computed style +Object fontSize = page.evalOnSelector("div", "el => window.getComputedStyle(el).fontSize"); +assertEquals("16px", fontSize); + +// Assert list length +Object length = page.evalOnSelectorAll("li.selected", "items => items.length"); +assertEquals(3, length); +``` + ```python async # Assert local storage value user_id = page.evaluate("() => window.localStorage.getItem('user_id')") @@ -245,4 +303,4 @@ assert length == 3 - [`method: Frame.evalOnSelectorAll`] - [`method: ElementHandle.evalOnSelector`] - [`method: ElementHandle.evalOnSelectorAll`] -- [EvaluationArgument] +- [EvaluationArgument] \ No newline at end of file diff --git a/docs/src/auth.md b/docs/src/auth.md index 95b689ecefa27..cb1156440aa69 100644 --- a/docs/src/auth.md +++ b/docs/src/auth.md @@ -36,6 +36,17 @@ await page.click('text=Submit'); // Verify app is logged in ``` +```java +Page page = context.newPage(); +page.navigate("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 +``` + ```python async page = await context.new_page() await page.goto('https://github.com/login') @@ -88,6 +99,16 @@ const storageState = JSON.parse(process.env.STORAGE); const context = await browser.newContext({ storageState }); ``` +```java +// Save storage state and store as an env variable +String storage = context.storageState(); +System.getenv().put("STORAGE", storage); + +// Create a new context with the saved storage state +BrowserContext context = browser.newContext( + new Browser.NewContextOptions().withStorageState(storage)); +``` + ```python async import json import os @@ -150,6 +171,23 @@ await context.addInitScript(storage => { }, sessionStorage); ``` +```java +// Get session storage and store as env variable +String sessionStorage = (String) page.evaluate("() => JSON.stringify(sessionStorage"); +System.getenv().put("SESSION_STORAGE", sessionStorage); + +// Set session storage in a new context +String sessionStorage = System.getenv("SESSION_STORAGE"); +context.addInitScript("(storage => {\n" + + " if (window.location.hostname === 'example.com') {\n" + + " const entries = JSON.parse(storage);\n" + + " Object.keys(entries).forEach(key => {\n" + + " window.sessionStorage.setItem(key, entries[key]);\n" + + " });\n" + + " }\n" + + "})(" + sessionStorage + ")"); +``` + ```python async import os # Get session storage and store as env variable @@ -199,8 +237,6 @@ manual intervention. Persistent authentication can be used to partially automate MFA scenarios. ### Persistent authentication -Web browsers use a directory on disk to store user history, cookies, IndexedDB -and other local state. This disk location is called the [User data directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md). Note that persistent authentication is not suited for CI environments since it relies on a disk location. User data directories are specific to browser types @@ -216,6 +252,22 @@ const context = await chromium.launchPersistentContext(userDataDir, { headless: // Execute login steps manually in the browser window ``` +```java +import com.microsoft.playwright.*; + +public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType chromium = playwright.chromium(); + Path userDataDir = Paths.get("/path/to/directory"); + BrowserContext context = chromium.launchPersistentContext(userDataDir, + new BrowserType.LaunchPersistentContextOptions().withHeadless(false)); + // Execute login steps manually in the browser window + } + } +} +``` + ```python async import asyncio from playwright.async_api import async_playwright @@ -246,4 +298,4 @@ with sync_playwright() as p: ### API reference - [BrowserContext] -- [`method: BrowserType.launchPersistentContext`] +- [`method: BrowserType.launchPersistentContext`] \ No newline at end of file diff --git a/docs/src/ci.md b/docs/src/ci.md index 34cbd738c028a..65076bdcd23d1 100644 --- a/docs/src/ci.md +++ b/docs/src/ci.md @@ -83,6 +83,11 @@ Suggested configuration }); ``` + ```java + Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions() + .withArgs(Arrays.asList("--disable-dev-shm-usage"))); + ``` + ```python async browser = await playwright.chromium.launch( args=['--disable-dev-shm-usage'] @@ -233,6 +238,19 @@ const { chromium } = require('playwright'); const browser = await chromium.launch({ chromiumSandbox: false }); ``` +```java +import com.microsoft.playwright.*; + +public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType chromium = playwright.chromium(); + Browser browser = chromium.launch(new BrowserType.LaunchOptions().withChromiumSandbox(false)); + } + } +} +``` + ```python async browser = await playwright.chromium.launch(chromiumSandbox=False) ``` @@ -319,6 +337,20 @@ const { chromium } = require('playwright'); const browser = await chromium.launch({ headless: false }); ``` +```java +// Works across chromium, firefox and webkit +import com.microsoft.playwright.*; + +public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType chromium = playwright.chromium(); + Browser browser = chromium.launch(new BrowserType.LaunchOptions().withHeadless(false)); + } + } +} +``` + ```python async import asyncio from playwright.async_api import async_playwright diff --git a/docs/src/cli.md b/docs/src/cli.md index 2da5c5f529670..9b8fce65d811c 100644 --- a/docs/src/cli.md +++ b/docs/src/cli.md @@ -91,6 +91,27 @@ const { chromium } = require('playwright'); })(); ``` +```java +// FIXME +import com.microsoft.playwright.*; + +public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType chromium = playwright.chromium(); + // Make sure to run headed. + Browser browser = chromium.launch(new BrowserType.LaunchOptions().withHeadless(false)); + // Setup context however you like. + BrowserContext context = browser.newContext(/* pass any options */); + context.route("**/*", route -> route.resume()); + // Pause the page, and start recording manually. + Page page = context.newPage(); + page.pause(); + } + } +} +``` + ```python async import asyncio from playwright.async_api import async_playwright diff --git a/docs/src/core-concepts.md b/docs/src/core-concepts.md index cd0eaff7ca744..a30fddd35be29 100644 --- a/docs/src/core-concepts.md +++ b/docs/src/core-concepts.md @@ -29,6 +29,20 @@ const browser = await chromium.launch({ headless: false }); await browser.close(); ``` +```java +import com.microsoft.playwright.*; + +public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType chromium = playwright.chromium(); + Browser browser = chromium.launch(new BrowserType.LaunchOptions().withHeadless(false)); + browser.close(); + } + } +} +``` + ```python async import asyncio from playwright.async_api import async_playwright @@ -69,6 +83,11 @@ const browser = await chromium.launch(); const context = await browser.newContext(); ``` +```java +Browser browser = chromium.launch(); +BrowserContext context = browser.newContext(); +``` + ```python async browser = await playwright.chromium.launch() context = await browser.new_context() @@ -95,6 +114,29 @@ const context = await browser.newContext({ }); ``` +```java +// FIXME +import com.microsoft.playwright.*; + +public class Example { + public static void main(String[] args) { + try (Playwright playwright = Playwright.create()) { + BrowserType devices = playwright.devices(); + BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1") + .withViewportSize(375, 812) + .withDeviceScaleFactor(3) + .withIsMobile(true) + .withHasTouch(true) + .withPermissions(Arrays.asList("geolocation")) + .withGeolocation(52.52, 13.39) + .withColorScheme(ColorScheme.DARK) + .withLocale("de-DE")); + } + } +} +``` + ```python async import asyncio from playwright.async_api import async_playwright @@ -161,6 +203,24 @@ console.log(page.url()); window.location.href = 'https://example.com'; ``` +```java +// Create a page. +Page page = context.newPage(); + +// Navigate explicitly, similar to entering a URL in the browser. +page.navigate("http://example.com"); +// Fill an input. +page.fill("#search", "query"); + +// Navigate implicitly by clicking a link. +page.click("#submit"); +// Expect a new url. +System.out.println(page.url()); + +// Page can navigate from the script - this will be picked up by Playwright. +// window.location.href = "https://example.com"; +``` + ```python async page = await context.new_page() @@ -219,6 +279,21 @@ const frame = await frameElementHandle.contentFrame(); await frame.fill('#username-input', 'John'); ``` +```java +// Get frame using the frame"s name attribute +Frame frame = page.frame("frame-login"); + +// Get frame using frame"s URL +Frame frame = page.frameByUrl(Pattern.compile(".*domain.*")); + +// Get frame using any other selector +ElementHandle frameElementHandle = page.querySelector(".frame-class"); +Frame frame = frameElementHandle.contentFrame(); + +// Interact with the frame +frame.fill("#username-input", "John"); +``` + ```python async # Get frame using the frame's name attribute frame = page.frame('frame-login') @@ -274,6 +349,11 @@ Some examples below: await page.click('data-test-id=foo'); ``` +```java +// Using data-test-id= selector engine +page.click("data-test-id=foo"); +``` + ```python async # Using data-test-id= selector engine await page.click('data-test-id=foo') @@ -290,6 +370,12 @@ await page.click('div'); await page.click('//html/body/div'); ``` +```java +// CSS and XPath selector engines are automatically detected +page.click("div"); +page.click("//html/body/div"); +``` + ```python async # CSS and XPath selector engines are automatically detected await page.click('div') @@ -307,6 +393,11 @@ page.click('//html/body/div') await page.click('text=Hello w'); ``` +```java +// Find node by text substring +page.click("text=Hello w"); +``` + ```python async # Find node by text substring await page.click('text=Hello w') @@ -323,6 +414,12 @@ await page.click('css=div'); await page.click('xpath=//html/body/div'); ``` +```java +// Explicit CSS and XPath notation +page.click("css=div"); +page.click("xpath=//html/body/div"); +``` + ```python async # Explicit CSS and XPath notation await page.click('css=div') @@ -340,6 +437,11 @@ page.click('xpath=//html/body/div') await page.click('css:light=div'); ``` +```java +// Only search light DOM, outside WebComponent shadow DOM: +page.click("css:light=div"); +``` + ```python async # Only search light DOM, outside WebComponent shadow DOM: await page.click('css:light=div') @@ -357,6 +459,11 @@ Selectors using the same or different engines can be combined using the `>>` sep await page.click('#free-month-promo >> text=Sign Up'); ``` +```java +// Click an element with text "Sign Up" inside of a #free-month-promo. +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') @@ -372,6 +479,11 @@ page.click('#free-month-promo >> text=Sign Up') const sectionText = await page.$eval('*css=section >> text=Selectors', e => e.textContent); ``` +```java +// Capture textContent of a section that contains an element with text "Selectors". +String sectionText = (String) page.evalOnSelector("*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') @@ -401,6 +513,11 @@ and [actionable](./actionability.md). For example, click will: await page.fill('#search', 'query'); ``` +```java +// Playwright waits for #search element to be in the DOM +page.fill("#search", "query"); +``` + ```python async # Playwright waits for #search element to be in the DOM await page.fill('#search', 'query') @@ -417,6 +534,12 @@ page.fill('#search', 'query') await page.click('#search'); ``` +```java +// Playwright waits for element to stop animating +// and accept clicks. +page.click("#search"); +``` + ```python async # Playwright waits for element to stop animating # and accept clicks. @@ -438,6 +561,14 @@ await page.waitForSelector('#search', { state: 'attached' }); await page.waitForSelector('#promo'); ``` +```java +// Wait for #search to appear in the DOM. +page.waitForSelector("#search", new Page.WaitForSelectorOptions() + .withState(WaitForSelectorState.ATTACHED)); +// Wait for #promo to become visible, for example with "visibility:visible". +page.waitForSelector("#promo"); +``` + ```python async # Wait for #search to appear in the DOM. await page.wait_for_selector('#search', state='attached') @@ -461,6 +592,15 @@ await page.waitForSelector('#details', { state: 'hidden' }); await page.waitForSelector('#promo', { state: 'detached' }); ``` +```java +// Wait for #details to become hidden, for example with "display:none". +page.waitForSelector("#details", new Page.WaitForSelectorOptions() + .withState(WaitForSelectorState.HIDDEN)); +// Wait for #promo to be removed from the DOM. +page.waitForSelector("#promo", new Page.WaitForSelectorOptions() + .withState(WaitForSelectorState.DETACHED)); +``` + ```python async # Wait for #details to become hidden, for example with `display:none`. await page.wait_for_selector('#details', state='hidden') @@ -495,6 +635,10 @@ of the web page and bring results back to the Playwright environment. Browser gl const href = await page.evaluate(() => document.location.href); ``` +```java +String href = (String) page.evaluate("document.location.href"); +``` + ```python async href = await page.evaluate('() => document.location.href') ``` @@ -512,6 +656,13 @@ const status = await page.evaluate(async () => { }); ``` +```java +int status = (int) page.evaluate("async () => {\n" + + " const response = await fetch(location.href);\n" + + " return response.status;\n" + + "}"); +``` + ```python async status = await page.evaluate("""async () => { response = await fetch(location.href) @@ -573,6 +724,57 @@ await page.evaluate( { button1, list: [button2], foo: null }); ``` +```java +// A primitive value. +page.evaluate("num => num", 42); + +// An array. +page.evaluate("array => array.length", Arrays.asList(1, 2, 3)); + +// An object. +Map obj = new HashMap<>(); +obj.put("foo", "bar"); +page.evaluate("object => object.foo", obj); + +// A single handle. +ElementHandle button = page.querySelector("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. +ElementHandle button1 = page.querySelector(".button1"); +ElementHandle button2 = page.querySelector(".button2"); +Map arg = new HashMap<>(); +arg.put("button1", button1); +arg.put("button2", button2); +page.evaluate("o => o.button1.textContent + o.button2.textContent", arg); + +// Object destructuring works. Note that property names must match +// between the destructured object and the argument. +// Also note the required parenthesis. +Map arg = new HashMap<>(); +arg.put("button1", button1); +arg.put("button2", button2); +page.evaluate("({ button1, button2 }) => button1.textContent + button2.textContent", arg); + +// Array works as well. Arbitrary names can be used for destructuring. +// Note the required parenthesis. +page.evaluate( + "([b1, b2]) => b1.textContent + b2.textContent", + Arrays.asList(button1, button2)); + +// Any non-cyclic mix of serializables and handles works. +Map arg = new HashMap<>(); +arg.put("button1", button1); +arg.put("list", Arrays.asList(button2)); +arg.put("foo", 0); +page.evaluate( + "x => x.button1.textContent + x.list[0].textContent + String(x.foo)", + arg); +``` + ```python async # A primitive value. await page.evaluate('num => num', 42) @@ -668,6 +870,16 @@ const result = await page.evaluate(data => { }, data); ``` +```java +Map data = new HashMap<>(); +data.put("text", "some data"); +data.put("value", 1); +// Pass |data| as a parameter. +Object result = page.evaluate("data => {\n" + + " window.myApp.use(data);\n" + + "}", data); +``` + ```python async data = { 'text': 'some data', 'value': 1 } # Pass |data| as a parameter. @@ -694,6 +906,16 @@ const result = await page.evaluate(() => { }); ``` +```java +Map data = new HashMap<>(); +data.put("text", "some data"); +data.put("value", 1); +Object result = page.evaluate("() => {\n" + + " // There is no |data| in the web page.\n" + + " window.myApp.use(data);\n" + + "}"); +``` + ```python async data = { 'text': 'some data', 'value': 1 } result = await page.evaluate("""() => { diff --git a/docs/src/debug.md b/docs/src/debug.md index 5a7a968234c01..f21e51676c301 100644 --- a/docs/src/debug.md +++ b/docs/src/debug.md @@ -26,6 +26,12 @@ to slow down execution and follow along while debugging. await chromium.launch({ headless: false, slowMo: 100 }); // or firefox, webkit ``` +```java +chromium.launch(new BrowserType.LaunchOptions() // or firefox, webkit + .withHeadless(false) + .withSlowMo(100)); +``` + ```python async await chromium.launch(headless=False, slow_mo=100) # or firefox, webkit @@ -75,6 +81,10 @@ In Chromium, you can also open developer tools through a launch option. await chromium.launch({ devtools: true }); ``` +```java +chromium.launch(new BrowserType.LaunchOptions().withDevtools(true)); +``` + ```python async await chromium.launch(devtools=True) @@ -98,6 +108,15 @@ $ set PWDEBUG=1 $ npm run test ``` +```sh java +# Linux/macOS +$ PWDEBUG=1 mvn test + +# Windows +$ set PWDEBUG=1 +$ mvn test +``` + ```sh python # Linux/macOS $ PWDEBUG=1 pytest -s @@ -152,6 +171,15 @@ $ set DEBUG=pw:api $ npm run test ``` +```sh java +# Linux/macOS +$ DEBUG=pw:api mvn test + +# Windows +$ set DEBUG=pw:api +$ mvn test +``` + ```sh python # Linux/macOS $ DEBUG=pw:api pytest -s diff --git a/docs/src/dialogs.md b/docs/src/dialogs.md index f85924aecdaf4..624edf0f569f7 100644 --- a/docs/src/dialogs.md +++ b/docs/src/dialogs.md @@ -16,6 +16,11 @@ page.on('dialog', dialog => dialog.accept()); await page.click('button'); ``` +```java +page.onDialog(dialog -> dialog.accept()); +page.click("button"); +``` + ```python async page.on("dialog", lambda dialog: dialog.accept()) await page.click("button") @@ -41,6 +46,11 @@ page.on('dialog', dialog => console.log(dialog.message())); await page.click('button'); // Will hang here ``` +```java +page.onDialog(dialog -> System.out.println(dialog.message())); +page.click("button"); // Will hang here +``` + ```python async page.on("dialog", lambda dialog: print(dialog.message)) await page.click("button") # Will hang here @@ -75,6 +85,14 @@ page.on('dialog', async dialog => { await page.close({runBeforeUnload: true}); ``` +```java +page.onDialog(dialog -> { + assertEquals("beforeunload", dialog.type()); + dialog.dismiss(); +}); +page.close(new Page.CloseOptions().withRunBeforeUnload(true)); +``` + ```python async async def handle_dialog(dialog): assert dialog.type == 'beforeunload' diff --git a/docs/src/downloads.md b/docs/src/downloads.md index 01bfe065f03ce..6bc2305bc1776 100644 --- a/docs/src/downloads.md +++ b/docs/src/downloads.md @@ -58,6 +58,10 @@ If you have no idea what initiates the download, you can still handle the event: page.on('download', download => download.path().then(console.log)); ``` +```java +page.onDownload(download -> System.out.println(download.path())); +``` + ```python async async def handle_download(download): print(await download.path()) diff --git a/docs/src/emulation.md b/docs/src/emulation.md index 18cc93756c036..3476a7495b9d5 100644 --- a/docs/src/emulation.md +++ b/docs/src/emulation.md @@ -80,6 +80,11 @@ const context = await browser.newContext({ }); ``` +```java +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withUserAgent("My user agent")); +``` + ```python async context = await browser.new_context( user_agent='My user agent' @@ -117,6 +122,20 @@ const context = await browser.newContext({ }); ``` +```java +// Create context with given viewport +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withViewportSize(1280, 1024)); + +// Resize viewport for individual page +page.setViewportSize(1600, 1200); + +// Emulate high-DPI +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withViewportSize(2560, 1440) + .withDeviceScaleFactor(2); +``` + ```python async # Create context with given viewport context = await browser.new_context( @@ -164,6 +183,13 @@ const context = await browser.newContext({ }); ``` +```java +// Emulate locale and time +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withLocale("de-DE") + .withTimezoneId("Europe/Berlin")); +``` + ```python async # Emulate locale and time context = await browser.new_context( @@ -195,6 +221,11 @@ const context = await browser.newContext({ }); ``` +```java +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withPermissions(Arrays.asList("notifications")); +``` + ```python async context = await browser.new_context( permissions=['notifications'], @@ -213,6 +244,10 @@ Grant all pages in the existing context access to current location: await context.grantPermissions(['geolocation']); ``` +```java +context.grantPermissions(Arrays.asList("geolocation")); +``` + ```python async await context.grant_permissions(['geolocation']) ``` @@ -227,6 +262,11 @@ Grant notifications access from a specific domain: await context.grantPermissions(['notifications'], {origin: 'https://skype.com'} ); ``` +```java +context.grantPermissions(Arrays.asList("notifications"), + new BrowserContext.GrantPermissionsOptions().withOrigin("https://skype.com")); +``` + ```python async await context.grant_permissions(['notifications'], origin='https://skype.com') ``` @@ -241,6 +281,10 @@ Revoke all permissions: await context.clearPermissions(); ``` +```java +context.clearPermissions(); +``` + ```python async await context.clear_permissions() ``` @@ -267,6 +311,12 @@ const context = await browser.newContext({ }); ``` +```java +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withGeolocation(48.858455, 2.294474) + .withPermissions(Arrays.asList("geolocation"))); +``` + ```python async context = await browser.new_context( geolocation={"longitude": 48.858455, "latitude": 2.294474}, @@ -287,6 +337,10 @@ Change the location later: await context.setGeolocation({ longitude: 29.979097, latitude: 31.134256 }); ``` +```java +context.setGeolocation(new Geolocation(29.979097, 31.134256)); +``` + ```python async await context.set_geolocation({"longitude": 29.979097, "latitude": 31.134256}) ``` @@ -325,6 +379,22 @@ await page.emulateMedia({ colorScheme: 'dark' }); await page.emulateMedia({ media: 'print' }); ``` +```java +// Create context with dark mode +BrowserContext context = browser.newContext(new Browser.NewContextOptions() + .withColorScheme(ColorScheme.DARK)); // or "light" + +// Create page with dark mode +Page page = browser.newPage(new Browser.NewPageOptions() + .withColorScheme(ColorScheme.DARK)); // or "light" + +// Change color scheme for the page +page.emulateMedia(new Page.EmulateMediaOptions().withColorScheme(ColorScheme.DARK)); + +// Change media for page +page.emulateMedia(new Page.EmulateMediaOptions().withMedia(Media.PRINT)); +``` + ```python async # Create context with dark mode context = await browser.new_context( diff --git a/docs/src/extensibility.md b/docs/src/extensibility.md index 1963d17ed6f10..21510b50d3604 100644 --- a/docs/src/extensibility.md +++ b/docs/src/extensibility.md @@ -51,6 +51,33 @@ await page.click('tag=div >> span >> "Click me"'); const buttonCount = await page.$$eval('tag=button', buttons => buttons.length); ``` +```java +// Must be a script that evaluates to a selector engine instance. +String createTagNameEngine = "{\n" + + " // Returns the first element matching given selector in the root's subtree.\n" + + " query(root, selector) {\n" + + " return root.querySelector(selector);\n" + + " },\n" + + "\n" + + " // Returns all elements matching given selector in the root's subtree.\n" + + " queryAll(root, selector) {\n" + + " return Array.from(root.querySelectorAll(selector));\n" + + " }\n" + + "}"; + +// Register the engine. Selectors will be prefixed with "tag=". +playwright.selectors().register("tag", createTagNameEngine); + +// Now we can use "tag=" selectors. +ElementHandle button = page.querySelector("tag=button"); + +// We can combine it with other selector engines using ">>" combinator. +page.click("tag=div >> span >> \"Click me\""); + +// We can use it in any methods supporting selectors. +int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length"); +``` + ```python async tag_selector = """ // Must evaluate to a selector engine instance. diff --git a/docs/src/handles.md b/docs/src/handles.md index 20532f2b2f097..c937e4ad866b0 100644 --- a/docs/src/handles.md +++ b/docs/src/handles.md @@ -28,6 +28,11 @@ const jsHandle = await page.evaluateHandle('window'); // Use jsHandle for evaluations. ``` +```java +JSHandle jsHandle = page.evaluateHandle("window"); +// Use jsHandle for evaluations. +``` + ```python async js_handle = await page.evaluate_handle('window') # Use jsHandle for evaluations. @@ -43,6 +48,11 @@ const ulElementHandle = await page.waitForSelector('ul'); // Use ulElementHandle for actions and evaluation. ``` +```java +ElementHandle ulElementHandle = page.waitForSelector("ul"); +// Use ulElementHandle for actions and evaluation. +``` + ```python async ul_element_handle = await page.wait_for_selector('ul') # Use ul_element_handle for actions and evaluation. @@ -76,6 +86,20 @@ const classNames = await elementHandle.getAttribute('class'); expect(classNames.includes('highlighted')).toBeTruthy(); ``` +```java +// Get the element handle +JSHandle jsHandle = page.waitForSelector("#box"); +ElementHandle elementHandle = jsHandle.asElement(); + +// Assert bounding box for the element +BoundingBox boundingBox = elementHandle.boundingBox(); +assertEquals(100, boundingBox.width); + +// Assert attribute for the element +String classNames = elementHandle.getAttribute("class"); +assertTrue(classNames.contains("highlighted")); +``` + ```python async # Get the element handle element_handle = page.wait_for_selector('#box') @@ -129,6 +153,26 @@ await page.evaluate(arg => arg.myArray.push(arg.newElement), { await myArrayHandle.dispose(); ``` +```java +// Create new array in page. +JSHandle myArrayHandle = page.evaluateHandle("() => {\n" + + " window.myArray = [1];\n" + + " return myArray;\n" + + "}"); + +// Get the length of the array. +int length = (int) page.evaluate("a => a.length", myArrayHandle); + +// Add one more element to the array using the handle +Map arg = new HashMap<>(); +arg.put("myArray", myArrayHandle); +arg.put("newElement", 2); +page.evaluate("arg => arg.myArray.add(arg.newElement)", arg); + +// Release the object when it"s no longer needed. +myArrayHandle.dispose(); +``` + ```python async # Create new array in page. my_array_handle = await page.evaluate_handle("""() => { diff --git a/docs/src/input.md b/docs/src/input.md index 3780df181cc44..a6a5c73e860cc 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -26,6 +26,23 @@ await page.fill('#local', '2020-03-02T05:15'); await page.fill('text=First Name', 'Peter'); ``` +```java +// 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"); +``` + ```python async # Text input await page.fill('#name', 'Peter') @@ -86,6 +103,20 @@ await page.uncheck('#subscribe-label'); await page.check('text=XL'); ``` +```java +// Check the checkbox +page.check("#agree"); + +// Assert the checked state +assertTrue(page.isChecked("#agree")); + +// Uncheck by input