概述
Puppeteer
是由Google
开发的Node.js
库,用于通过开发者工具协议控制无头 Chrome
和 Chromium
。它允许您自动化 UI 测试、网页抓取、截图测试等操作。
1 | const puppeteer = require('puppeteer'); |
启动浏览器
启动一个无头浏览器实例:
1 | const browser = await puppeteer.launch(); |
启动完整版本的 Chrome:
1 | const browser = await puppeteer.launch({ |
使用自定义参数启动浏览器:
1 | const browser = await puppeteer.launch({ |
自定义启动选项:
1 | puppeteer.launch({ |
创建页面
创建新页面:
1 | const page = await browser.newPage(); |
创建匿名页面:
1 | const context = await browser.createIncogniteBrowserContext(); |
访问已有页面:
1 | const pages = await browser.pages(); |
标签
在标签之间切换/将它们置于前台:
1 | await page1.bringToFront(); |
操作
导航到 URL:
1 | await page.goto('https://example.com'); |
点击元素:
1 | await page.click('#element'); |
输入内容:
1 | await page.type('#input', 'Text'); |
按键盘键:
1 | await page.keyboard.press('Shift'); |
上传文件:
1 | await page.setInputFiles('#upload', ['/path/to/file1', '/path/to/file2']); |
在页面上执行 JavaScript 代码:
1 | const result = await page.evaluate(() => { |
悬停在元素上:
1 | await page.hover('#element'); |
捕获截图:
1 | await page.screenshot({path: 'screenshot.png'}); |
模拟移动设备:
1 | await page.emulate(puppeteer.devices['iPhone 6']); |
滚动至视图:
1 | await page.evaluate(el => el.scrollIntoView(), await page.$('.item')); |
在 iframe 中输入:
1 | const frame = page.frames().find(f => f.name() === 'frame'); |
在移动设备上点击元素:
1 | await page.touchscreen.tap(200, 75); |
触发拖放:
1 | await page.mouse.down(); |
选择器
通过 CSS 选择器获取元素:
1 | const nav = await page.$('nav'); |
获取多个元素:
1 | const items = await page.$$('.item'); |
使用 XPath 选择器:
1 | const button = await page.$x('//*[@id="button"]'); |
获取文本内容:
1 | const text = await page.textContent('.results'); |
高级选择器
使用文本选择器:
1 | const link = await page.$('a:text("Next")'); |
可见性选择器:
1 | const hidden = await page.$('.element:hidden'); |
属性选择器:
1 | const checkbox = await page.$('input[type="checkbox"]'); |
XPath 选择器:
1 | const submit = await page.$x('//button[@type="submit"]'); |
通过文本内容获取:
1 | const p = await page.$eval('p', el => el.innerText === 'Hello'); |
查询 Shadow DOM:
1 | const shadow = await page.$('.element/shadow-root'); |
辅助测试
检查问题:
1 | const issues = await page.accessibility.audit({ |
检查颜色对比度:
1 | const contrastratio = await page.$eval('.button', button => { |
选项卡焦点顺序:
1 | await page.keyboard.press('Tab'); |
调试与报告
跟踪控制台错误:
1 | page.on('console', msg => { |
生成 HTML 报告:
1 | const html = '<h1>测试报告</h1>'; |
跟踪测试覆盖率:
1 | const coverage = await page.coverage.startJSCoverage(); |
等待
等待导航:
1 | await page.waitForNavigation(); |
等待选择器:
1 | await page.waitForSelector('div.loaded'); |
等待固定时间:
1 | await page.waitFor(1000); // 等待 1 秒 |
等待函数结果:
1 | await page.waitForFunction(() => window.fetchDone); |
等待 XHR 请求:
1 | await page.waitForRequest(request => request.url() === 'data.json'); |
带超时的导航:
1 | await page.waitForNavigation({timeout: 60000}); |
元素等待 30 秒:
1 | await page.waitForSelector('.item', {timeout: 30000}); |
框架
获取页面框架:
1 | const frames = page.mainFrame().childFrames(); |
设置当前框架:
1 | const frame = page.frames().find(f => f.name() === 'frame'); |
输入
获取元素的 HTML/文本/属性:
1 | const html = await page.$eval('.item', el => el.outerHTML); |
填写并提交表单:
1 | await page.type('#input', 'Text'); |
采样
对元素截图:
1 | const el = await |
模拟设备和视口:
1 | const devices = puppeteer.devices; |
获取资源定时数据:
1 | const metrics = await page.metrics(); |
生成 PDF 报告:
1 | await page.pdf({ |
横向定向 PDF:
1 | await page.pdf({ |
事件
页面加载事件:
1 | page.once('load', () => { |
网络请求失败事件:
1 | page.on('requestfailed', request => { |
控制台消息事件:
1 | page.on('console', msg => { |
认证
设置用户代理:
1 | await page.setUserAgent('CustomAgent'); |
设置自定义标头:
1 | await page.setExtraHTTPHeaders({ |
设置 cookies:
等待页面设置 cookie
:
1 | await page.setCookie({name: 'session', value: '1234'}); |
设置凭据:
1 | await page.authenticate({ |
设置绕过 CSP:
1 | await browser.launch({ignoreHTTPSErrors: true}); |
使用代理服务器:
1 | await page.authenticate({username: 'user', password: 'pass'}); |
网络
禁用缓存:
1 | await page.setCacheEnabled(false); |
设置节流率:
1 | await page.setRequestInterception(true); |
模拟响应:
1 | page.on('request', interceptedRequest => { |
模拟重定向响应:
1 | await page.route('**/*', route => { |
模拟 404 状态:
1 | page.on('request', route => { |
高级用法
等待更复杂的条件:
1 | // 等待文本内容更改 |
处理弹出窗口和新标签页:
1 | page.on('dialog', dialog => { |
使用自动重试稳定不稳定的测试:
1 | // 自动重试失败步骤最多 4 次 |
触摸交互
在元素上点击:
1 | await page.tap('button'); |
在移动设备上滚动:
1 | await page.touchscreen.scroll(50, 100); |
拖放:
1 | await page.touchscreen.down(); |
地理位置和权限
设置地理位置:
1 | await page.setGeolocation({latitude: 0, longitude: 0}); |
授予摄像头访问权限:
1 | await page.grantPermissions(['camera']); |
高级用例
提交表单和上传文件:
1 | // 提交表单 |
从网站中抓取内容:
1 | // 从所有 p 元素中提取文本 |
跨浏览器视觉测试:
1 | const devices = puppeteer.devices; |
视觉回归测试
比较截图:
1 | const screenshot = await page.screenshot(); |
并行测试
并行测试:
1 | const browser = await puppeteer.launch(); |
使用技巧
通过持久化上下文加速执行:
1 | // 持久化浏览器上下文 |
在测试期间分析 CPU 使用情况:
1 | await page.profiling.start({path: 'trace.json'}); |
使用隐私模式上下文:
1 | const context = await browser.createIncogniteBrowserContext(); |
在上下文之间传输 cookie:
1 | const context1 = await browser.createIncogniteBrowserContext(); |
使用持久性上下文:
1 | const context = await browser.createPersistentContext(); |
测试自动化策略
可重用的页面对象:
1 | class LoginPage { |
同步测试序列:
1 | const [response] = await Promise.all([ |
重试失败的测试用例:
1 | for (let retry = 0; retry < 3; retry++) { |