mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
docs: expand core-concepts and multi-age (#10161)
This commit is contained in:
parent
3743fe1ef5
commit
1bf7f6d90d
@ -6,7 +6,7 @@ title: "Authentication"
|
|||||||
Playwright can be used to automate scenarios that require authentication.
|
Playwright can be used to automate scenarios that require authentication.
|
||||||
|
|
||||||
Tests written with Playwright execute in isolated clean-slate environments called
|
Tests written with Playwright execute in isolated clean-slate environments called
|
||||||
[browser contexts](./core-concepts.md#browser-contexts). This isolation model
|
[browser contexts](./browser-contexts.md). This isolation model
|
||||||
improves reproducibility and prevents cascading test failures. New browser
|
improves reproducibility and prevents cascading test failures. New browser
|
||||||
contexts can load existing authentication state. This eliminates the need to
|
contexts can load existing authentication state. This eliminates the need to
|
||||||
login in every context and speeds up test execution.
|
login in every context and speeds up test execution.
|
||||||
|
243
docs/src/browser-contexts.md
Normal file
243
docs/src/browser-contexts.md
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
---
|
||||||
|
id: browser-contexts
|
||||||
|
title: "Browser Contexts"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Browser context
|
||||||
|
|
||||||
|
A [BrowserContext] is an isolated incognito-alike session within a browser instance. Browser contexts are fast and
|
||||||
|
cheap to create. We recommend running each test scenario in its own new Browser context, so that
|
||||||
|
the browser state is isolated between the tests. If you are using [Playwright Test](./intro.md), this happens out of the
|
||||||
|
box for each test. Otherwise, you can create browser contexts manually:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
const context = await browser.newContext();
|
||||||
|
const page = await context.newPage();
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
Browser browser = chromium.launch();
|
||||||
|
BrowserContext context = browser.newContext();
|
||||||
|
Page page = context.newPage();
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
browser = await playwright.chromium.launch()
|
||||||
|
context = await browser.new_context()
|
||||||
|
page = await context.new_page()
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
browser = playwright.chromium.launch()
|
||||||
|
context = browser.new_context()
|
||||||
|
page = context.new_page()
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
await using var browser = playwright.Chromium.LaunchAsync();
|
||||||
|
var context = await browser.NewContextAsync();
|
||||||
|
var page = await context.NewPageAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
Browser contexts can also be used to emulate multi-page scenarios involving
|
||||||
|
mobile devices, permissions, locale and color scheme.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { devices } = require('playwright');
|
||||||
|
const iPhone = devices['iPhone 11 Pro'];
|
||||||
|
|
||||||
|
const context = await browser.newContext({
|
||||||
|
...iPhone,
|
||||||
|
permissions: ['geolocation'],
|
||||||
|
geolocation: { latitude: 52.52, longitude: 13.39},
|
||||||
|
colorScheme: 'dark',
|
||||||
|
locale: 'de-DE'
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
```
|
||||||
|
|
||||||
|
```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()
|
||||||
|
.setUserAgent("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")
|
||||||
|
.setViewportSize(375, 812)
|
||||||
|
.setDeviceScaleFactor(3)
|
||||||
|
.setIsMobile(true)
|
||||||
|
.setHasTouch(true)
|
||||||
|
.setPermissions(Arrays.asList("geolocation"))
|
||||||
|
.setGeolocation(52.52, 13.39)
|
||||||
|
.setColorScheme(ColorScheme.DARK)
|
||||||
|
.setLocale("de-DE"));
|
||||||
|
Page page = context.newPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
import asyncio
|
||||||
|
from playwright.async_api 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.new_page()
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
from playwright.sync_api 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']
|
||||||
|
)
|
||||||
|
page = context.new_page()
|
||||||
|
browser.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
class PlaywrightExample
|
||||||
|
{
|
||||||
|
public static async Task Main()
|
||||||
|
{
|
||||||
|
using var playwright = await Playwright.CreateAsync();
|
||||||
|
await using var browser = await playwright.Webkit.LaunchAsync();
|
||||||
|
var options = new BrowserNewContextOptions(playwright.Devices["iPhone 11 Pro"])
|
||||||
|
{
|
||||||
|
Geolocation = new() { Longitude = 12.492507f, Latitude = 41.889938f },
|
||||||
|
Permissions = new[] { "geolocation" },
|
||||||
|
Locale = "de-DE"
|
||||||
|
};
|
||||||
|
|
||||||
|
await using var context = await browser.NewContextAsync(options);
|
||||||
|
var page = await browser.NewPageAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple contexts
|
||||||
|
|
||||||
|
[Browser contexts](./browser-contexts.md) are isolated environments on a single browser instance.
|
||||||
|
Playwright can create multiple browser contexts within a single scenario. This is useful when you want to test for
|
||||||
|
multi-user functionality, like chat.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { chromium } = require('playwright');
|
||||||
|
|
||||||
|
// Create a Chromium browser instance
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
|
||||||
|
// Create two isolated browser contexts
|
||||||
|
const userContext = await browser.newContext();
|
||||||
|
const adminContext = await browser.newContext();
|
||||||
|
|
||||||
|
// Create pages and interact with contexts independently
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.microsoft.playwright.*;
|
||||||
|
|
||||||
|
public class Example {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try (Playwright playwright = Playwright.create()) {
|
||||||
|
BrowserType chromium = playwright.chromium();
|
||||||
|
// Create a Chromium browser instance
|
||||||
|
Browser browser = chromium.launch();
|
||||||
|
// Create two isolated browser contexts
|
||||||
|
BrowserContext userContext = browser.newContext();
|
||||||
|
BrowserContext adminContext = browser.newContext();
|
||||||
|
// Create pages and interact with contexts independently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
import asyncio
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
|
async def run(playwright):
|
||||||
|
# create a chromium browser instance
|
||||||
|
chromium = playwright.chromium
|
||||||
|
browser = await chromium.launch()
|
||||||
|
|
||||||
|
# create two isolated browser contexts
|
||||||
|
user_context = await browser.new_context()
|
||||||
|
admin_context = await browser.new_context()
|
||||||
|
|
||||||
|
# create pages and interact with contexts independently
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
async with async_playwright() as playwright:
|
||||||
|
await run(playwright)
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
def run(playwright):
|
||||||
|
# create a chromium browser instance
|
||||||
|
chromium = playwright.chromium
|
||||||
|
browser = chromium.launch()
|
||||||
|
|
||||||
|
# create two isolated browser contexts
|
||||||
|
user_context = browser.new_context()
|
||||||
|
admin_context = browser.new_context()
|
||||||
|
|
||||||
|
# create pages and interact with contexts independently
|
||||||
|
|
||||||
|
with sync_playwright() as playwright:
|
||||||
|
run(playwright)
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.Playwright;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public static async Task Main()
|
||||||
|
{
|
||||||
|
using var playwright = await Playwright.CreateAsync();
|
||||||
|
// Create a Chromium browser instance
|
||||||
|
await using var browser = await playwright.Chromium.LaunchAsync();
|
||||||
|
await using var userContext = await browser.NewContextAsync();
|
||||||
|
await using var adminContext = await browser.NewContextAsync();
|
||||||
|
// Create pages and interact with contexts independently.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API reference
|
||||||
|
- [BrowserContext]
|
||||||
|
- [`method: Browser.newContext`]
|
||||||
|
- [`method: BrowserContext.addCookies`]
|
File diff suppressed because it is too large
Load Diff
372
docs/src/evaluating.md
Normal file
372
docs/src/evaluating.md
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
---
|
||||||
|
id: evaluating
|
||||||
|
title: "Evaluating JavaScript"
|
||||||
|
---
|
||||||
|
|
||||||
|
Playwright scripts run in your Playwright environment. Your page scripts run in the browser page environment. Those environments don't intersect, they are running in different virtual machines in different processes and even potentially on different computers.
|
||||||
|
|
||||||
|
The [`method: Page.evaluate`] API can run a JavaScript function in the context
|
||||||
|
of the web page and bring results back to the Playwright environment. Browser globals like
|
||||||
|
`window` and `document` can be used in `evaluate`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
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')
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
href = page.evaluate('() => document.location.href')
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var href = await page.EvaluateAsync<string>("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);
|
||||||
|
return response.status;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```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)
|
||||||
|
return response.status
|
||||||
|
}""")
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
status = page.evaluate("""async () => {
|
||||||
|
response = fetch(location.href)
|
||||||
|
return response.status
|
||||||
|
}""")
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
int status = await page.EvaluateAsync<int>(@"async () => {
|
||||||
|
const response = await 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.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 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.
|
||||||
|
const button = await page.$('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.
|
||||||
|
const button1 = await page.$('.button1');
|
||||||
|
const button2 = await page.$('.button2');
|
||||||
|
await page.evaluate(
|
||||||
|
o => o.button1.textContent + o.button2.textContent,
|
||||||
|
{ button1, 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, 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, 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<String, Object> 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<String, ElementHandle> 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<String, ElementHandle> 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<String, Object> 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)
|
||||||
|
|
||||||
|
# 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 })
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// A primitive value.
|
||||||
|
await page.EvaluateAsync<int>("num => num", 42);
|
||||||
|
|
||||||
|
// An array.
|
||||||
|
await page.EvaluateAsync<int[]>("array => array.length", new[] { 1, 2, 3 });
|
||||||
|
|
||||||
|
// An object.
|
||||||
|
await page.EvaluateAsync<object>("object => object.foo", new { foo = "bar" });
|
||||||
|
|
||||||
|
// A single handle.
|
||||||
|
var button = await page.QuerySelectorAsync("button");
|
||||||
|
await page.EvaluateAsync<IJSHandle>("button => button.textContent", button);
|
||||||
|
|
||||||
|
// Alternative notation using elementHandle.EvaluateAsync.
|
||||||
|
await button.EvaluateAsync<string>("(button, from) => button.textContent.substring(from)", 5);
|
||||||
|
|
||||||
|
// Object with multiple handles.
|
||||||
|
var button1 = await page.QuerySelectorAsync(".button1");
|
||||||
|
var button2 = await page.QuerySelectorAsync(".button2");
|
||||||
|
await page.EvaluateAsync("o => o.button1.textContent + o.button2.textContent", new { button1, button2 });
|
||||||
|
|
||||||
|
// Object destructuring works. Note that property names must match
|
||||||
|
// between the destructured object and the argument.
|
||||||
|
// Also note the required parenthesis.
|
||||||
|
await page.EvaluateAsync("({ button1, button2 }) => button1.textContent + button2.textContent", new { button1, button2 });
|
||||||
|
|
||||||
|
// Array works as well. Arbitrary names can be used for destructuring.
|
||||||
|
// Note the required parenthesis.
|
||||||
|
await page.EvaluateAsync("([b1, b2]) => b1.textContent + b2.textContent", new[] { button1, button2 });
|
||||||
|
|
||||||
|
// Any non-cyclic mix of serializables and handles works.
|
||||||
|
await page.EvaluateAsync("x => x.button1.textContent + x.list[0].textContent + String(x.foo)", new { button1, list = new[] { button2 }, foo = null as object });
|
||||||
|
```
|
||||||
|
|
||||||
|
Right:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const data = { text: 'some data', value: 1 };
|
||||||
|
// Pass |data| as a parameter.
|
||||||
|
const result = await page.evaluate(data => {
|
||||||
|
window.myApp.use(data);
|
||||||
|
}, data);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
Map<String, Object> 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.
|
||||||
|
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)
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var data = new { text = "some data", value = 1};
|
||||||
|
// Pass data as a parameter
|
||||||
|
var result = await page.EvaluateAsync("data => { window.myApp.use(data); }", data);
|
||||||
|
```
|
||||||
|
|
||||||
|
Wrong:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const data = { text: 'some data', value: 1 };
|
||||||
|
const result = await page.evaluate(() => {
|
||||||
|
// There is no |data| in the web page.
|
||||||
|
window.myApp.use(data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
Map<String, Object> 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("""() => {
|
||||||
|
# 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)
|
||||||
|
}""")
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var data = new { text = "some data", value = 1};
|
||||||
|
// Pass data as a parameter
|
||||||
|
var result = await page.EvaluateAsync(@"data => {
|
||||||
|
// There is no |data| in the web page.
|
||||||
|
window.myApp.use(data);
|
||||||
|
}");
|
||||||
|
```
|
127
docs/src/frames.md
Normal file
127
docs/src/frames.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
---
|
||||||
|
id: frames
|
||||||
|
title: "Pages and frames"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
|
## Frames
|
||||||
|
|
||||||
|
A [Page] can have one or more [Frame] objects attached to it. Each page has a main frame and page-level interactions
|
||||||
|
(like `click`) are assumed to operate in the main frame.
|
||||||
|
|
||||||
|
A page can have additional frames attached with the `iframe` HTML tag. These frames can be accessed for interactions
|
||||||
|
inside the frame.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Locate element inside frame
|
||||||
|
const username = await page.frameLocator('.frame-class').locator('#username-input');
|
||||||
|
await username.fill('John');
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Locate element inside frame
|
||||||
|
Locator username = page.frameLocator(".frame-class").locator("#username-input");
|
||||||
|
username.fill("John");
|
||||||
|
```
|
||||||
|
|
||||||
|
```python async
|
||||||
|
# Locate element inside frame
|
||||||
|
username = await page.frame_locator('.frame-class').locator('#username-input')
|
||||||
|
await username.fill('John')
|
||||||
|
```
|
||||||
|
|
||||||
|
```python sync
|
||||||
|
# Locate element inside frame
|
||||||
|
# Get frame using any other selector
|
||||||
|
username = page.frame_locator('.frame-class').locator('#username-input')
|
||||||
|
username.fill('John')
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Locate element inside frame
|
||||||
|
var username = await page.FrameLocator(".frame-class").Locator("#username-input");
|
||||||
|
await username.FillAsync("John");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frame objects
|
||||||
|
|
||||||
|
One can access frame objects using the [`method: Page.frame`] API:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Get frame using the frame's name attribute
|
||||||
|
const frame = page.frame('frame-login');
|
||||||
|
|
||||||
|
// Get frame using frame's URL
|
||||||
|
const frame = page.frame({ url: /.*domain.*/ });
|
||||||
|
|
||||||
|
// Get frame using any other selector
|
||||||
|
const frameElementHandle = await page.$('.frame-class');
|
||||||
|
const frame = await frameElementHandle.contentFrame();
|
||||||
|
|
||||||
|
// Interact with the frame
|
||||||
|
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')
|
||||||
|
|
||||||
|
# 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')
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Create a page.
|
||||||
|
var page = await context.NewPageAsync();
|
||||||
|
|
||||||
|
// Get frame using the frame's name attribute
|
||||||
|
var frame = page.Frame("frame-login");
|
||||||
|
|
||||||
|
// Get frame using frame's URL
|
||||||
|
var frame = page.FrameByUrl("*domain.");
|
||||||
|
|
||||||
|
// Get frame using any other selector
|
||||||
|
var frameElementHandle = await page.QuerySelectorAsync(".frame-class");
|
||||||
|
var frame = await frameElementHandle.ContentFrameAsync();
|
||||||
|
|
||||||
|
// Interact with the frame
|
||||||
|
await frame.FillAsync("#username-input", "John");
|
||||||
|
```
|
@ -1,111 +1,88 @@
|
|||||||
---
|
---
|
||||||
id: multi-pages
|
id: pages
|
||||||
title: "Multi-page scenarios"
|
title: "Pages"
|
||||||
---
|
---
|
||||||
|
|
||||||
Playwright can automate scenarios that span multiple browser contexts or multiple tabs in a browser window.
|
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
## Multiple contexts
|
## Pages
|
||||||
|
|
||||||
[Browser contexts](./core-concepts.md#browser-contexts) are isolated environments on a single browser instance.
|
Each [BrowserContext] can have multiple pages. A [Page] refers to a single tab or a popup window within a browser
|
||||||
Playwright can create multiple browser contexts within a single scenario. This is useful when you want to test for
|
context. It should be used to navigate to URLs and interact with the page content.
|
||||||
multi-user functionality, like chat.
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { chromium } = require('playwright');
|
// Create a page.
|
||||||
|
const page = await context.newPage();
|
||||||
|
|
||||||
// Create a Chromium browser instance
|
// Navigate explicitly, similar to entering a URL in the browser.
|
||||||
const browser = await chromium.launch();
|
await page.goto('http://example.com');
|
||||||
|
// Fill an input.
|
||||||
|
await page.locator('#search').fill('query');
|
||||||
|
|
||||||
// Create two isolated browser contexts
|
// Navigate implicitly by clicking a link.
|
||||||
const userContext = await browser.newContext();
|
await page.locator('#submit').click();
|
||||||
const adminContext = await browser.newContext();
|
// Expect a new url.
|
||||||
|
console.log(page.url());
|
||||||
// Create pages and interact with contexts independently
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.microsoft.playwright.*;
|
// Create a page.
|
||||||
|
Page page = context.newPage();
|
||||||
|
|
||||||
public class Example {
|
// Navigate explicitly, similar to entering a URL in the browser.
|
||||||
public static void main(String[] args) {
|
page.navigate("http://example.com");
|
||||||
try (Playwright playwright = Playwright.create()) {
|
// Fill an input.
|
||||||
BrowserType chromium = playwright.chromium();
|
page.locator("#search").fill("query");
|
||||||
// Create a Chromium browser instance
|
|
||||||
Browser browser = chromium.launch();
|
// Navigate implicitly by clicking a link.
|
||||||
// Create two isolated browser contexts
|
page.locator("#submit").click();
|
||||||
BrowserContext userContext = browser.newContext();
|
// Expect a new url.
|
||||||
BrowserContext adminContext = browser.newContext();
|
System.out.println(page.url());
|
||||||
// Create pages and interact with contexts independently
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
import asyncio
|
page = await context.new_page()
|
||||||
from playwright.async_api import async_playwright
|
|
||||||
|
|
||||||
async def run(playwright):
|
# Navigate explicitly, similar to entering a URL in the browser.
|
||||||
# create a chromium browser instance
|
await page.goto('http://example.com')
|
||||||
chromium = playwright.chromium
|
# Fill an input.
|
||||||
browser = await chromium.launch()
|
await page.locator('#search').fill('query')
|
||||||
|
|
||||||
# create two isolated browser contexts
|
# Navigate implicitly by clicking a link.
|
||||||
user_context = await browser.new_context()
|
await page.locator('#submit').click()
|
||||||
admin_context = await browser.new_context()
|
# Expect a new url.
|
||||||
|
print(page.url)
|
||||||
# create pages and interact with contexts independently
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async with async_playwright() as playwright:
|
|
||||||
await run(playwright)
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python sync
|
```python sync
|
||||||
from playwright.sync_api import sync_playwright
|
page = context.new_page()
|
||||||
|
|
||||||
def run(playwright):
|
# Navigate explicitly, similar to entering a URL in the browser.
|
||||||
# create a chromium browser instance
|
page.goto('http://example.com')
|
||||||
chromium = playwright.chromium
|
# Fill an input.
|
||||||
browser = chromium.launch()
|
page.locator('#search').fill('query')
|
||||||
|
|
||||||
# create two isolated browser contexts
|
# Navigate implicitly by clicking a link.
|
||||||
user_context = browser.new_context()
|
page.locator('#submit').click()
|
||||||
admin_context = browser.new_context()
|
# Expect a new url.
|
||||||
|
print(page.url)
|
||||||
# create pages and interact with contexts independently
|
|
||||||
|
|
||||||
with sync_playwright() as playwright:
|
|
||||||
run(playwright)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Microsoft.Playwright;
|
// Create a page.
|
||||||
using System.Threading.Tasks;
|
var page = await context.NewPageAsync();
|
||||||
|
|
||||||
class Program
|
// Navigate explicitly, similar to entering a URL in the browser.
|
||||||
{
|
await page.GotoAsync("http://example.com");
|
||||||
public static async Task Main()
|
// Fill an input.
|
||||||
{
|
await page.Locator("#search").FillAsync("query");
|
||||||
using var playwright = await Playwright.CreateAsync();
|
|
||||||
// Create a Chromium browser instance
|
// Navigate implicitly by clicking a link.
|
||||||
await using var browser = await playwright.Chromium.LaunchAsync();
|
await page.Locator("#submit").ClickAsync();
|
||||||
await using var userContext = await browser.NewContextAsync();
|
// Expect a new url.
|
||||||
await using var adminContext = await browser.NewContextAsync();
|
Console.WriteLine(page.Url);
|
||||||
// Create pages and interact with contexts independently.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### API reference
|
|
||||||
- [BrowserContext]
|
|
||||||
- [`method: Browser.newContext`]
|
|
||||||
- [`method: BrowserContext.addCookies`]
|
|
||||||
|
|
||||||
## Multiple pages
|
## Multiple pages
|
||||||
|
|
||||||
Each browser context can host multiple pages (tabs).
|
Each browser context can host multiple pages (tabs).
|
||||||
@ -158,11 +135,6 @@ var pageTwo = await context.NewPageAsync();
|
|||||||
var allPages = context.Pages;
|
var allPages = context.Pages;
|
||||||
```
|
```
|
||||||
|
|
||||||
### API reference
|
|
||||||
- [Page]
|
|
||||||
- [`method: BrowserContext.newPage`]
|
|
||||||
- [`method: BrowserContext.pages`]
|
|
||||||
|
|
||||||
## Handling new pages
|
## Handling new pages
|
||||||
|
|
||||||
The `page` event on browser contexts can be used to get new pages that are created in the context. This can be used to
|
The `page` event on browser contexts can be used to get new pages that are created in the context. This can be used to
|
||||||
@ -261,8 +233,6 @@ context.Page += async (_, page) => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### API reference
|
|
||||||
- [`event: BrowserContext.page`]
|
|
||||||
|
|
||||||
## Handling popups
|
## Handling popups
|
||||||
|
|
||||||
@ -362,6 +332,3 @@ page.Popup += async (_, popup) => {
|
|||||||
Console.WriteLine(await page.TitleAsync());
|
Console.WriteLine(await page.TitleAsync());
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### API reference
|
|
||||||
- [`event: Page.popup`]
|
|
@ -132,7 +132,7 @@ Once you're on Playwright Test, you get a lot!
|
|||||||
|
|
||||||
- Full zero-configuration TypeScript support
|
- Full zero-configuration TypeScript support
|
||||||
- Run tests across **all web engines** (Chrome, Firefox, Safari) on **any popular operating system** (Windows, MacOS, Ubuntu)
|
- Run tests across **all web engines** (Chrome, Firefox, Safari) on **any popular operating system** (Windows, MacOS, Ubuntu)
|
||||||
- Full support for multiple origins, [(i)frames](./api/class-frame), [tabs and contexts](./multi-pages)
|
- Full support for multiple origins, [(i)frames](./api/class-frame), [tabs and contexts](./pages)
|
||||||
- Run tests in parallel across multiple browsers
|
- Run tests in parallel across multiple browsers
|
||||||
- Built-in test artifact collection: [video recording](./test-configuration#record-video), [screenshots](./test-configuration#automatic-screenshots) and [playwright traces](./test-configuration#record-test-trace)
|
- Built-in test artifact collection: [video recording](./test-configuration#record-video), [screenshots](./test-configuration#automatic-screenshots) and [playwright traces](./test-configuration#record-test-trace)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ title: "Authentication"
|
|||||||
---
|
---
|
||||||
|
|
||||||
Tests written with Playwright execute in isolated clean-slate environments called
|
Tests written with Playwright execute in isolated clean-slate environments called
|
||||||
[browser contexts](./core-concepts.md#browser-contexts). Each test gets a brand
|
[browser contexts](./browser-contexts.md). Each test gets a brand
|
||||||
new page created in a brand new context. This isolation model improves reproducibility
|
new page created in a brand new context. This isolation model improves reproducibility
|
||||||
and prevents cascading test failures.
|
and prevents cascading test failures.
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@ def test_my_app_is_working(fixture_name):
|
|||||||
|
|
||||||
**Function scope**: These fixtures are created when requested in a test function and destroyed when the test ends.
|
**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/python/docs/core-concepts#browser-contexts) for a test.
|
- `context`: New [browser context](https://playwright.dev/python/docs/browser-contexts) for a test.
|
||||||
- `page`: New [browser page](https://playwright.dev/python/docs/core-concepts#pages-and-frames) for a test.
|
- `page`: New [browser page](https://playwright.dev/python/docs/pages) for a test.
|
||||||
|
|
||||||
**Session scope**: These fixtures are created when requested in a test function and destroyed when all tests end.
|
**Session scope**: These fixtures are created when requested in a test function and destroyed when all tests end.
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ title: "Videos"
|
|||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
Playwright can record videos for all pages in a [browser context](./core-concepts.md#browser-contexts). Videos are saved
|
Playwright can record videos for all pages in a [browser context](./browser-contexts.md). Videos are saved
|
||||||
upon context closure, so make sure to await [`method: BrowserContext.close`].
|
upon context closure, so make sure to await [`method: BrowserContext.close`].
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -22,12 +22,12 @@ Playwright enables fast, reliable and capable testing and automation across all
|
|||||||
|
|
||||||
* **Timeout-free automation**. Playwright receives browser signals, like network requests, page navigations and page load events to eliminate the need for sleep timeouts that cause flakiness.
|
* **Timeout-free automation**. Playwright receives browser signals, like network requests, page navigations and page load events to eliminate the need for sleep timeouts that cause flakiness.
|
||||||
|
|
||||||
* **Fast isolation with browser contexts**. Reuse a single browser instance for multiple isolated execution environments with [browser contexts](./core-concepts.md).
|
* **Fast isolation with browser contexts**. Reuse a single browser instance for multiple isolated execution environments with [browser contexts](./browser-contexts.md).
|
||||||
|
|
||||||
* **Resilient element selectors**. Playwright can rely on user-facing strings, like text content and accessibility labels to [select elements](./selectors.md). These strings are more resilient than selectors tightly-coupled to the DOM structure.
|
* **Resilient element selectors**. Playwright can rely on user-facing strings, like text content and accessibility labels to [select elements](./selectors.md). These strings are more resilient than selectors tightly-coupled to the DOM structure.
|
||||||
|
|
||||||
## Powerful automation capabilities
|
## Powerful automation capabilities
|
||||||
* **Multiple domains, pages and frames**. Playwright is an out-of-process automation driver that is not limited by the scope of in-page JavaScript execution and can automate scenarios with [multiple pages](./multi-pages.md).
|
* **Multiple domains, pages and frames**. Playwright is an out-of-process automation driver that is not limited by the scope of in-page JavaScript execution and can automate scenarios with [multiple pages](./pages.md).
|
||||||
|
|
||||||
* **Powerful network control**. Playwright introduces context-wide [network interception](./network.md) to stub and mock network requests.
|
* **Powerful network control**. Playwright introduces context-wide [network interception](./network.md) to stub and mock network requests.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user