从 Puppeteer 到 Playwright(译)

5,765 阅读5分钟

原文

Puppeteer转换到Playwright很容易,不过是否值的这么做呢?在编码层面有哪些具体的变化?有哪些新的特性和功能?这是我们本文关心的主要内容

Puppeteer 与 Playwright 现状

这两个工具有不少相似之处,不过二者近两年来的发展速度不尽相同,Playwright的发展势头可以说是盖过了Puppeteer。这种势头使得不少人转向了Playwright,本文将会介绍具体的迁移步骤以及这种改变带来的新特性。本文虽然长了一点,但内容很容易掌握。

为什么要迁移

之前我们做过CypressSeleniumPlaywrightPuppeteer、的对比测试,详情可见文章链接,简单来说有以下理由:

  • Playwright更新更加活跃,在新特性方面较Puppeteer更为明显
  • Playwright在现实的端到端(E2E)测试中有性能优势(详见文章链接),执行测试用例速度更快
  • Playwright稳定性更佳
  • PlaywrightGitHubTwitterSlack等社区上更火爆,反观Puppeteer社区则略显冷清

变化清单

下面是一张对比表,先阅读几分钟,后面我们会深入介绍

PuppeteerPlaywright
puppeteer.launch(...)playwright.chromium.launch(...)
browser.createIncognitoBrowserContext(...)browser.newContext(...)
page.setViewport(...)page.setViewportSize(...)
page.waitForSelector(selector) page.click(selector);page.click(selector)
page.waitForXPath(XPathSelector)page.waitForSelector(XPathSelector)
page.$x(xpath_selector)page.$(xpath_selector)
page.waitForNetworkIdle(...)page.waitForLoadState({ state: 'networkidle' }})
page.waitForFileChooser(...)Removed, handled differently.
page.waitFor(timeout)page.waitForTimeout(timeout)
page.type(selector, text)page.fill(selector, text)
page.cookies([...urls])browserContext.cookies([urls])
page.deleteCookie(...cookies)browserContext.clearCookies()
page.setCookie(...cookies)browserContext.addCookies(cookies)
page.on('request', ...)Handled through page.route.
elementHandle.uploadFile(...)elementHandle.setInputFiles(...)
Tricky file download.Better support for downloads.

变化详情

包引入

Puppeteer中,脚本的前几行很可能是这样的:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
  // ...

Playwright则是这样的:

const { chromium } = require('playwright');

(async () => {
    const browser = await chromium.launch();
    const page = await browser.newPage();

  // ...

Playwright提供开箱即用的跨浏览器支持,你可以选择具体运行的浏览器,比如const { webkit } = require('playwright');,在Puppeteer中需要通过launch接口的配置来实现:

const browser = await puppeteer.launch({ product: 'firefox' })

浏览器context

浏览器contextPuppeteer中是存在的:

const browser = await puppeteer.launch();
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();

Playwrightcontext更加重要一些,使用起来稍微有一点不同:

const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();

Puppeteer一样,对于基本的使用以及单页流,可以使用默认的context

const browser = await chromium.launch();
const page = await browser.newPage();

wait

Playwright自动等待auto-waiting)机制意味着您可能不需要经常显式地等待。 但是,等待是UI自动化中最棘手的部分之一,您仍然需要知道让脚本显式地等待一个或多个条件满足的不同方法。在这方面,Playwright带来了一些你需要注意的变化:

注意page.waitForTimeout不要在正式的产品版中使用,硬性的等待只是用于测试

设置 viewport

Puppeteerpage.setViewportPlaywright 中变成了 page.setViewportSize

Typing

Puppeteerpage.type 依然可用,还是可以控制细粒度的键盘事件,除此之外 Playwright 增加了page.fill用于填充、清空表单

Cookies

使用Puppeteer时cookie在页面级别处理;使用Playwright时,你可以在BrowserContext级别上操纵它们

之前:

  1. page.cookies([...urls])
  2. page.deleteCookie(...cookies)
  3. page.setCookie(...cookies)

现在:

  1. browserContext.cookies([urls])
  2. browserContext.clearCookies()
  3. browserContext.addCookies(cookies)

请注意这些方法之间的细微差别,以及如何将cookie传递给它们

XPath 选择器

//.. 开头的XPath 选择器会被Playwright自动识别,而在Puppeteer则是有单独的接口

设备模拟

Playwright设备模拟是在浏览器context层级上提供的:

const pixel2 = devices['Pixel 2'];
const context = await browser.newContext({
    ...pixel2,
});

除此之外,还可以控制权限、地理位置和其他设备参数。

文件下载

尝试在无头模式下的Puppeteer下载文件是很棘手的,Playwright会更精简:

const [download] = await Promise.all([
    page.waitForEvent('download'),
    page.click('#orders > ul > li:nth-child(1) > a')
])

const path = await download.path();

这是一个完整的示例

文件上传

Puppeteer的 elementHandle.uploadFile becomes elementHandle.setInputFiles

详见 文件上传示例.

请求拦截

Puppeteer中的请求拦截是通过 page.on('request', ...)实现的:

await page.setRequestInterception(true)

page.on('request', (request) => {
    if (request.resourceType() === 'image') request.abort()
    else request.continue()
})

Playwright可以通过page.route拦截指定模式的url:

await page.route('**/*', (route) => {
  return route.request().resourceType() === 'image'
    ? route.abort()
    : route.continue()
})

详见 完整示例

值得注意的新功能

当从Puppeteer转移到Playwright时,请确保了解Playwright的许多全新功能,因为它们可能为测试或监视设置提供新的解决方案和可能性

新的选择器引擎

Playwright提供了更灵活的UI元素引用方式,除了cssXPath以外还增加了以下方式:

  • Playwright特有的选择器,例如:nth-match(:text("Buy"), 3)
  • 文本选择器,例如text=Add to Car
  • 链选择器,例如css=preview >> text=In stock

除此之外,你好可以创建自己的自定义选择器引擎

关于选择器的更多信息以及用法,可以参见 Working with selectors

保存以及复用状态

Playwright可以方便地保存给定会话的身份验证状态(cookielocalStorage),并在后续脚本运行时使用它以便节约身份验证的时间

定位API

Playwright 定位器API 封装了检索给定元素所需的逻辑,允许使用者轻松地在不同时间点获得脚本中的最新DOM元素

Inspector

PlaywrightInspector 是一个GUI工具,在调试脚本时非常方便。它允许使用者在脚本中逐步执行指令,从而更容易地确定失败的原因。

image.png

Test

PlaywrightTest机制 , 在E2E测试中十分有用,例如开箱即用的并行化、钩子等

追踪视图

PlaywrightTrace Viewer允许您探索使用剧作家测试或BrowserContext跟踪API记录的痕迹。通过跟踪可以最细致地了解脚本的执行情况

image.png

测试生成器

您可以使用PlaywrightTest Generator来记录浏览器中的交互。输出将是一个可以检查和执行的完整脚本

image.png