美文网首页
Puppeteer 备忘录

Puppeteer 备忘录

作者: 涅槃快乐是金 | 来源:发表于2024-04-11 10:50 被阅读0次

概述

Puppeteer是由Google开发的Node.js库,用于通过开发者工具协议控制无头 ChromeChromium。它允许您自动化 UI 测试、网页抓取、截图测试等操作。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('<https://example.com>');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();

启动浏览器

启动一个无头浏览器实例:

const browser = await puppeteer.launch();

启动完整版本的 Chrome:

const browser = await puppeteer.launch({
  headless: false
});

使用自定义参数启动浏览器:

const browser = await puppeteer.launch({
  args: ['--start-maximized']
});

自定义启动选项:

puppeteer.launch({
  executablePath: '/path/to/Chrome', // 自定义 Chrome 二进制文件路径
  product: 'firefox' // 启动 Firefox
});

创建页面

创建新页面:

const page = await browser.newPage();

创建匿名页面:

const context = await browser.createIncogniteBrowserContext();
const page = await context.newPage();

访问已有页面:

const pages = await browser.pages();
const page = pages[0];

标签

在标签之间切换/将它们置于前台:

await page1.bringToFront();
await page2.bringToFront();

操作

导航到 URL:

await page.goto('https://example.com');

点击元素:

await page.click('#element');

输入内容:

await page.type('#input', 'Text');

按键盘键:

await page.keyboard.press('Shift');

上传文件:

await page.setInputFiles('#upload', ['/path/to/file1', '/path/to/file2']);

在页面上执行 JavaScript 代码:

const result = await page.evaluate(() => {
  return document.querySelector('#result').textContent;
});

悬停在元素上:

await page.hover('#element');

捕获截图:

await page.screenshot({path: 'screenshot.png'});

模拟移动设备:

await page.emulate(puppeteer.devices['iPhone 6']);

滚动至视图:

await page.evaluate(el => el.scrollIntoView(), await page.$('.item'));

在 iframe 中输入:

const frame = page.frames().find(f => f.name() === 'frame');
await frame.$eval('#input', el => el.value = 'Text');

在移动设备上点击元素:

await page.touchscreen.tap(200, 75);

触发拖放:

await page.mouse.down();
await page.mouse.move(0, 100);
await page.mouse.up();

选择器

通过 CSS 选择器获取元素:

const nav = await page.$('nav');

获取多个元素:

const items = await page.$$('.item');

使用 XPath 选择器:

const button = await page.$x('//*[@id="button"]');

获取文本内容:

const text = await page.textContent('.results');

高级选择器

使用文本选择器:

const link = await page.$('a:text("Next")');

可见性选择器:

const hidden = await page.$('.element:hidden');

属性选择器:

const checkbox = await page.$('input[type="checkbox"]');

XPath 选择器:

const submit = await page.$x('//button[@type="submit"]');

通过文本内容获取:

const p = await page.$eval('p', el => el.innerText === 'Hello');

查询 Shadow DOM:

const shadow = await page.$('.element/shadow-root');
const text = await shadow.$eval('.text', el => el.textContent);

辅助测试

检查问题:

const issues = await page.accessibility.audit({
  runA11yChecks: true
});

expect(issues).toHaveLength(0);

检查颜色对比度:

const contrastratio = await page.$eval('.button', button => {
  const bgColor = window.getComputedStyle(button).backgroundColor;
  // 计算对比度比率
});

expect(contrastratio).toBeGreaterThan(4.5);

选项卡焦点顺序:

await page.keyboard.press('Tab');

const active = await page.evaluate(() => document.activeElement.id);
expect(active).toBe('username');

调试与报告

跟踪控制台错误:

page.on('console', msg => {
  if (msg.type() === 'error') {
    console.error(msg.text());
  }
});

生成 HTML 报告:

const html = '<h1>测试报告</h1>';
fs.writeFileSync('report.html', html);

跟踪测试覆盖率:

const coverage = await page.coverage.startJSCoverage();

// 运行测试

const results = await coverage.stopJSCoverage();

等待

等待导航:

await page.waitForNavigation();

等待选择器:

await page.waitForSelector('div.loaded');

等待固定时间:

await page.waitFor(1000); // 等待 1 秒

等待函数结果:

await page.waitForFunction(() => window.fetchDone);

等待 XHR 请求:

await page.waitForRequest(request => request.url() === 'data.json');

带超时的导航:

await page.waitForNavigation({timeout: 60000});

元素等待 30 秒:

await page.waitForSelector('.item', {timeout: 30000});

框架

获取页面框架:

const frames = page.mainFrame().childFrames();

设置当前框架:

const frame = page.frames().find(f => f.name() === 'frame');
await frame.evaluate(() => {
  // 在框架内运行代码
});

输入

获取元素的 HTML/文本/属性:

const html = await page.$eval('.item', el => el.outerHTML);

const text = await page.$eval('.item', el => el.innerText);

const class = await page.$eval('.item', el => el.getAttribute('class'));

填写并提交表单:

await page.type('#input', 'Text');
await page.click('#submitButton');

采样

对元素截图:

const el = await

 page.$('.element');
await el.screenshot({path: 'element.png'});

模拟设备和视口:

const devices = puppeteer.devices;
const iPhone = devices['iPhone 6'];

await page.emulate(iPhone);
await page.setViewport(iPhone.viewport);

获取资源定时数据:

const metrics = await page.metrics();
const requests = metrics.requestfinished;

PDF

生成 PDF 报告:

await page.pdf({
  path: 'page.pdf',
  format: 'A4'
});

横向定向 PDF:

await page.pdf({
  path: 'page.pdf',
  landscape: true
});

事件

页面加载事件:

page.once('load', () => {
  // 页面完全加载
});

网络请求失败事件:

page.on('requestfailed', request => {
  console.log(request.url + ' ' + request.failure().errorText);
});

控制台消息事件:

page.on('console', msg => {
  console.log(`${msg.type()} ${msg.text()}`);
});

认证

设置用户代理:

await page.setUserAgent('CustomAgent');

设置自定义标头:

await page.setExtraHTTPHeaders({
  'Accept-Language': 'en-US'
});

设置 cookies:

等待页面设置cookie:

await page.setCookie({name: 'session', value: '1234'});

设置凭据:

await page.authenticate({
  username: 'user',
  password: 'pass'
});

设置绕过 CSP:

await browser.launch({ignoreHTTPSErrors: true});

使用代理服务器:

await page.authenticate({username: 'user', password: 'pass'});

网络

禁用缓存:

await page.setCacheEnabled(false);

设置节流率:

await page.setRequestInterception(true);
page.on('request', request => {
  request.continue({
    throttling: 0.5 // 慢 50%
  });
});

模拟响应:

page.on('request', interceptedRequest => {
  interceptedRequest.respond({
    contentType: 'text/html',
    body: '<html>模拟页面</html>'
  });
});

模拟重定向响应:

await page.route('**/*', route => {
  route.continue({ url: '/mock-page.html' });
});

模拟 404 状态:

page.on('request', route => {
  route.abort('notfound');
});

高级用法

等待更复杂的条件:

// 等待文本内容更改
await page.waitForFunction(selector => {
  return document.querySelector(selector).textContent === 'Updated';
}, {}, selector);

// 等待 500ms 内无网络请求
await page.waitForTimeout(500);

处理弹出窗口和新标签页:

page.on('dialog', dialog => {
  dialog.accept(); // 或 dismiss()
});

const [popup] = await Promise.all([
  new Promise(resolve => browser.once('targetcreated', target => resolve(target.page()))),
  page.click('#open-popup'), // 单击打开弹出窗口的按钮
]);

await popup.waitForSelector('h1'); // 等待弹出窗口内容

使用自动重试稳定不稳定的测试:

// 自动重试失败步骤最多 4 次
const autoRetry = require('puppeteer-autoretry');
autoRetry.setDefaults({ retries: 4 });

await autoRetry(page).type('#input', 'Text');

触摸交互

在元素上点击:

await page.tap('button');

在移动设备上滚动:

await page.touchscreen.scroll(50, 100);

拖放:

await page.touchscreen.down();
await page.touchscreen.move(50, 100);
await page.touchscreen.up();

地理位置和权限

设置地理位置:

await page.setGeolocation({latitude: 0, longitude: 0});

授予摄像头访问权限:

await page.grantPermissions(['camera']);

高级用例

提交表单和上传文件:

// 提交表单
await page.type('#email', 'test@example.com');
await page.click('#submit');

// 上传文件
const input = await page.$('input#file');
input.uploadFile('/path/to/file.txt');

从网站中抓取内容:

// 从所有 p 元素中提取文本
const texts = await page.$$eval('p', elements => {
  return elements.map(el => el.textContent);
});

跨浏览器视觉测试:

const devices = puppeteer.devices;

for (const browserType of ['chromium', 'firefox', 'webkit']) {
  const browser = await puppeteer.launch({browserType});

  // 模拟设备并进行测试
}

视觉回归测试

比较截图:

const screenshot = await page.screenshot();

const diff = await visualDiff.compare(screenshot, 'baseline.png');
expect(diff.misMatchPercentage).toBeLessThan(0.01);

并行测试

并行测试:

const browser = await puppeteer.launch();
const pagePromises = [
  browser.newPage(),
  browser.newPage()
];

const pages = await Promise.all(pagePromises);

// 并行运行测试
await Promise.all([
  pages[0].goto('url1'),
  pages[1].goto('url2')
])

使用技巧

通过持久化上下文加速执行:

// 持久化浏览器上下文
const browserContext = await browser.createIncogniteBrowserContext();

await browserContext.close();
await browserContext.waitForTarget(page => page.url() === 'about:blank');

// 恢复上下文
const page = await browserContext.newPage();

在测试期间分析 CPU 使用情况:

await page.profiling.start({path: 'trace.json'});

// 运行 CPU 密集任务

await page.profiling.stop();

使用隐私模式上下文:

const context = await browser.createIncogniteBrowserContext();
const page = await context.newPage();

在上下文之间传输 cookie:

const context1 = await browser.createIncogniteBrowserContext();
const context2 = await browser.createIncogniteBrowserContext();

await context1.addCookies([cookieObj1, cookieObj2]);

const transferred = await context2.transferCookies(context1);

使用持久性上下文:

const context = await browser.createPersistentContext();
await context.close();

// 以后恢复
const page = await context.newPage();

测试自动化策略

可重用的页面对象:

class LoginPage {

  constructor(page) {
    this.page = page;
  }

  async login(username, password) {
    await this.page.type('#username', username);
    await this.page.type('#password', password);
    await this.page.click('#submit');
  }

}

// 使用方式:
const loginPage = new LoginPage(page);
await loginPage.login('user1', '123456');

同步测试序列:

const [response] = await Promise.all([
  page.click('#submit'), // 请求发送后返回
  page.waitForNavigation() // 页面加载后解析
]);

// 在导航后断言响应
expect(response.status()).toBe(200);

重试失败的测试用例:

for (let retry = 0; retry < 3; retry++) {
  try {
    await loginPage.login('invalid', 'password');
    break; // 测试通过,因此我们中断
  } catch (error) {
    if (retry === 2) {
      throw error; // 3 次重试后失败
    }
   

 // 否则重试测试
  }
}

相关文章

网友评论

      本文标题:Puppeteer 备忘录

      本文链接:https://www.haomeiwen.com/subject/meicxjtx.html