docs(evaluate): improve the guide (#32222)

This commit is contained in:
Dmitry Gozman 2024-08-21 01:31:41 -07:00 коммит произвёл GitHub
Родитель 109cab66f1
Коммит b66cb6caaa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
1 изменённых файлов: 142 добавлений и 80 удалений

Просмотреть файл

@ -68,9 +68,95 @@ int status = await page.EvaluateAsync<int>(@"async () => {
}");
```
## Different environments
Evaluated scripts run in the browser environment, while your test runs in a testing environments. This means you cannot use variables from your test in the page and vice versa. Instead, you should pass them explicitly as an argument.
The following snippet is **WRONG** because it uses the variable directly:
```js
const data = 'some data';
const result = await page.evaluate(() => {
// WRONG: there is no "data" in the web page.
window.myApp.use(data);
});
```
```java
String data = "some data";
Object result = page.evaluate("() => {\n" +
" // WRONG: there is no 'data' in the web page.\n" +
" window.myApp.use(data);\n" +
"}");
```
```python async
data = "some data"
result = await page.evaluate("""() => {
// WRONG: there is no "data" in the web page.
window.myApp.use(data)
}""")
```
```python sync
data = "some data"
result = page.evaluate("""() => {
// WRONG: there is no "data" in the web page.
window.myApp.use(data)
}""")
```
```csharp
var data = "some data";
var result = await page.EvaluateAsync(@"() => {
// WRONG: there is no 'data' in the web page.
window.myApp.use(data);
}");
```
The following snippet is **CORRECT** because it passes the value explicitly as an argument:
```js
const data = 'some data';
// Pass |data| as a parameter.
const result = await page.evaluate(data => {
window.myApp.use(data);
}, data);
```
```java
String data = "some data";
// Pass |data| as a parameter.
Object result = page.evaluate("data => {\n" +
" window.myApp.use(data);\n" +
"}", data);
```
```python async
data = "some data"
# Pass |data| as a parameter.
result = await page.evaluate("""data => {
window.myApp.use(data)
}""", data)
```
```python sync
data = "some data"
# Pass |data| as a parameter.
result = page.evaluate("""data => {
window.myApp.use(data)
}""", data)
```
```csharp
var data = "some data";
// Pass |data| as a parameter.
var result = await page.EvaluateAsync("data => { window.myApp.use(data); }", data);
```
## 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.
Playwright evaluation methods like [`method: Page.evaluate`] take a single optional argument. This argument can be a mix of [Serializable] values and [JSHandle] instances. Handles are automatically converted to the value they represent.
```js
// A primitive value.
@ -86,7 +172,7 @@ await page.evaluate(object => object.foo, { foo: 'bar' });
const button = await page.evaluateHandle('window.button');
await page.evaluate(button => button.textContent, button);
// Alternative notation using elementHandle.evaluate.
// Alternative notation using JSHandle.evaluate.
await button.evaluate((button, from) => button.textContent.substring(from), 5);
// Object with multiple handles.
@ -109,7 +195,7 @@ await page.evaluate(
([b1, b2]) => b1.textContent + b2.textContent,
[button1, button2]);
// Any non-cyclic mix of serializables and handles works.
// Any 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 });
@ -131,7 +217,7 @@ page.evaluate("object => object.foo", obj);
ElementHandle button = page.evaluateHandle("window.button");
page.evaluate("button => button.textContent", button);
// Alternative notation using elementHandle.evaluate.
// Alternative notation using JSHandle.evaluate.
button.evaluate("(button, from) => button.textContent.substring(from)", 5);
// Object with multiple handles.
@ -156,7 +242,7 @@ page.evaluate(
"([b1, b2]) => b1.textContent + b2.textContent",
Arrays.asList(button1, button2));
// Any non-cyclic mix of serializables and handles works.
// Any mix of serializables and handles works.
Map<String, Object> arg = new HashMap<>();
arg.put("button1", button1);
arg.put("list", Arrays.asList(button2));
@ -180,7 +266,7 @@ await page.evaluate('object => object.foo', { 'foo': 'bar' })
button = await page.evaluate_handle('button')
await page.evaluate('button => button.textContent', button)
# Alternative notation using elementHandle.evaluate.
# Alternative notation using JSHandle.evaluate.
await button.evaluate('(button, from) => button.textContent.substring(from)', 5)
# Object with multiple handles.
@ -203,7 +289,7 @@ await page.evaluate("""
([b1, b2]) => b1.textContent + b2.textContent""",
[button1, button2])
# Any non-cyclic mix of serializables and handles works.
# Any 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 })
@ -223,7 +309,7 @@ page.evaluate('object => object.foo', { 'foo': 'bar' })
button = page.evaluate_handle('window.button')
page.evaluate('button => button.textContent', button)
# Alternative notation using elementHandle.evaluate.
# Alternative notation using JSHandle.evaluate.
button.evaluate('(button, from) => button.textContent.substring(from)', 5)
# Object with multiple handles.
@ -245,7 +331,7 @@ page.evaluate("""
([b1, b2]) => b1.textContent + b2.textContent""",
[button1, button2])
# Any non-cyclic mix of serializables and handles works.
# Any 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 })
@ -265,7 +351,7 @@ await page.EvaluateAsync<object>("object => object.foo", new { foo = "bar" });
var button = await page.EvaluateHandleAsync("window.button");
await page.EvaluateAsync<IJSHandle>("button => button.textContent", button);
// Alternative notation using elementHandle.EvaluateAsync.
// Alternative notation using JSHandle.EvaluateAsync.
await button.EvaluateAsync<string>("(button, from) => button.textContent.substring(from)", 5);
// Object with multiple handles.
@ -282,93 +368,69 @@ await page.EvaluateAsync("({ button1, button2 }) => button1.textContent + button
// 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.
// Any 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:
## Init scripts
Sometimes it is convenient to evaluate something in the page before it starts loading. For example, you might want to setup some mocks or test data.
In this case, use [`method: Page.addInitScript`] or [`method: BrowserContext.addInitScript`]. In the example below, we will replace `Math.random()` with a constant value.
First, create a `preload.js` file that contains the mock.
```js browser
// preload.js
Math.random = () => 42;
```
Next, add init script to the page.
```js
const data = { text: 'some data', value: 1 };
// Pass |data| as a parameter.
const result = await page.evaluate(data => {
window.myApp.use(data);
}, data);
```
import { test, expect } from '@playwright/test';
import path from 'path';
```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);
test.beforeEach(async ({ page }) => {
// Add script for every test in the beforeEach hook.
// Make sure to correctly resolve the script path.
await page.addInitScript({ path: path.resolve(__dirname, '../mocks/preload.js') });
});
```
```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" +
"}");
// In your test, assuming the "preload.js" file is in the "mocks" directory.
page.addInitScript(Paths.get("mocks/preload.js"));
```
```python async
data = { 'text': 'some data', 'value': 1 }
result = await page.evaluate("""() => {
// There is no |data| in the web page.
window.myApp.use(data)
}""")
# In your test, assuming the "preload.js" file is in the "mocks" directory.
await page.add_init_script(path="mocks/preload.js")
```
```python sync
data = { 'text': 'some data', 'value': 1 }
result = page.evaluate("""() => {
// There is no |data| in the web page.
window.myApp.use(data)
}""")
# In your test, assuming the "preload.js" file is in the "mocks" directory.
page.add_init_script(path="mocks/preload.js")
```
```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);
}");
// In your test, assuming the "preload.js" file is in the "mocks" directory.
await Page.AddInitScriptAsync(scriptPath: "mocks/preload.js");
```
######
* langs: js
Alternatively, you can pass a function instead of creating a preload script file. This is more convenient for short or one-off scripts. You can also pass an argument this way.
```js
import { test, expect } from '@playwright/test';
// Add script for every test in the beforeEach hook.
test.beforeEach(async ({ page }) => {
const value = 42;
await page.addInitScript(value => {
Math.random = () => value;
}, value);
});
```