Playwright JavaScript 执行
本章介绍如何在 Playwright 中执行 JavaScript 代码,包括页面上下文执行、参数传递和初始化脚本注入。
page.evaluate() 页面上下文执行
page.evaluate() 在页面的 JavaScript 环境中执行代码,可以访问 DOM 和页面全局变量。
实例
test('获取页面数据', async ({ page }) => {
await page.goto('https://www.runoob.com/');
// 获取页面标题(在页面上下文中执行)
const title = await page.evaluate(() => {
return document.title;
});
console.log('页面标题:', title);
// 获取页面中所有链接数量
const linkCount = await page.evaluate(() => {
return document.querySelectorAll('a').length;
});
console.log('链接数量:', linkCount);
// 获取页面滚动位置
const scrollY = await page.evaluate(() => {
return window.scrollY;
});
});
await page.goto('https://www.runoob.com/');
// 获取页面标题(在页面上下文中执行)
const title = await page.evaluate(() => {
return document.title;
});
console.log('页面标题:', title);
// 获取页面中所有链接数量
const linkCount = await page.evaluate(() => {
return document.querySelectorAll('a').length;
});
console.log('链接数量:', linkCount);
// 获取页面滚动位置
const scrollY = await page.evaluate(() => {
return window.scrollY;
});
});
向 evaluate 传递参数
page.evaluate() 的第二个参数会传递给页面内的函数。
实例
test('传递参数到页面', async ({ page }) => {
await page.goto('https://www.runoob.com/');
// 传递单个参数
const result1 = await page.evaluate((name) => {
return `Hello, ${name}!`;
}, 'RUNOOB');
// 传递对象参数
const userInfo = { name: 'runoob', role: 'admin' };
const result2 = await page.evaluate((user) => {
return `${user.name} 的角色是 ${user.role}`;
}, userInfo);
// 传递多个参数(使用解构)
const result3 = await page.evaluate(
({ x, y }) => {
return x + y;
},
{ x: 10, y: 20 }
);
console.log('10 + 20 =', result3); // 30
});
await page.goto('https://www.runoob.com/');
// 传递单个参数
const result1 = await page.evaluate((name) => {
return `Hello, ${name}!`;
}, 'RUNOOB');
// 传递对象参数
const userInfo = { name: 'runoob', role: 'admin' };
const result2 = await page.evaluate((user) => {
return `${user.name} 的角色是 ${user.role}`;
}, userInfo);
// 传递多个参数(使用解构)
const result3 = await page.evaluate(
({ x, y }) => {
return x + y;
},
{ x: 10, y: 20 }
);
console.log('10 + 20 =', result3); // 30
});
传递的参数会被 JSON 序列化后发送到页面上下文,因此不能传递函数、DOM 元素等无法序列化的对象。
传回的值同样会被序列化。
locator.evaluate() 元素上下文执行
locator.evaluate() 在匹配的元素上下文中执行代码,参数中包含该元素的 DOM 节点引用。
实例
test('获取元素的 DOM 属性', async ({ page }) => {
await page.goto('https://www.runoob.com/');
// 获取特定元素的 textContent
const text = await page
.getByRole('heading', { name: 'RUNOOB' })
.evaluate(node => node.textContent);
// 获取元素的样式属性
const color = await page
.getByRole('button', { name: '搜索' })
.evaluate(node => getComputedStyle(node).backgroundColor);
});
await page.goto('https://www.runoob.com/');
// 获取特定元素的 textContent
const text = await page
.getByRole('heading', { name: 'RUNOOB' })
.evaluate(node => node.textContent);
// 获取元素的样式属性
const color = await page
.getByRole('button', { name: '搜索' })
.evaluate(node => getComputedStyle(node).backgroundColor);
});
locator.evaluateAll() 遍历所有匹配元素
evaluateAll() 对所有匹配元素执行相同的代码,返回数组。
实例
test('获取所有列表项的文本', async ({ page }) => {
await page.goto('https://www.runoob.com/');
// 获取所有菜单项的文本
const menuItems = await page
.getByRole('navigation')
.locator('a')
.evaluateAll(links => links.map(link => link.textContent));
console.log('菜单项:', menuItems);
// ['首页', '教程', '工具', ...]
});
await page.goto('https://www.runoob.com/');
// 获取所有菜单项的文本
const menuItems = await page
.getByRole('navigation')
.locator('a')
.evaluateAll(links => links.map(link => link.textContent));
console.log('菜单项:', menuItems);
// ['首页', '教程', '工具', ...]
});
page.addInitScript() 注入初始脚本
addInitScript() 在页面加载之前注入 JavaScript 代码,可以在页面脚本执行前修改浏览器环境。
实例
test('注入初始脚本', async ({ page }) => {
// 在页面加载前修改 navigator 属性
await page.addInitScript(() => {
// 覆盖 WebDriver 检测
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
});
// Mock window 上的全局变量
window.RUNOOB_CONFIG = {
env: 'test',
version: '1.0.0',
};
});
await page.goto('https://www.runoob.com/');
// 验证注入的变量生效
const config = await page.evaluate(() => window.RUNOOB_CONFIG);
console.log(config); // { env: 'test', version: '1.0.0' }
});
// 在页面加载前修改 navigator 属性
await page.addInitScript(() => {
// 覆盖 WebDriver 检测
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
});
// Mock window 上的全局变量
window.RUNOOB_CONFIG = {
env: 'test',
version: '1.0.0',
};
});
await page.goto('https://www.runoob.com/');
// 验证注入的变量生效
const config = await page.evaluate(() => window.RUNOOB_CONFIG);
console.log(config); // { env: 'test', version: '1.0.0' }
});
addInitScript() 常见用途:
| 用途 | 说明 |
|---|---|
| Mock 浏览器 API | 覆盖 navigator.webdriver、navigator.language 等 |
| 注入自定义配置 | 设置测试环境变量、Mock 数据源等 |
| 禁用功能 | 关闭 Service Worker、禁用日志等 |
| Polyfill | 添加页面依赖但测试环境缺少的功能 |
ElementHandle 的用途与局限
ElementHandle 是 DOM 元素的直接引用,在 Playwright 早期版本中广泛使用。
现在大多数场景应使用 Locator 替代 ElementHandle。
实例
// ElementHandle 方式(旧式,不推荐)
const handle = await page.$('.my-element');
await handle.click();
// Locator 方式(新式,推荐)
await page.locator('.my-element').click();
const handle = await page.$('.my-element');
await handle.click();
// Locator 方式(新式,推荐)
await page.locator('.my-element').click();
ElementHandle 仅在需要获取 boundingBox() 或进行复杂 DOM 操作时使用。
