尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Cypress 官方 @cypress/puppeteer 插件:在测试中用 Puppeteer API 驱动多标签页与跨页操作

Cypress 官方 @cypress/puppeteer 插件:在测试中用 Puppeteer API 驱动多标签页与跨页操作 Cypress 官方 cypress/puppeteer 插件在测试中用 Puppeteer API 驱动多标签页与跨页操作【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypresscypress/puppeteer是 Cypress 仓库内的官方实验性插件public beta它让你在 Cypress 测试里通过一条cy.puppeteer()命令调用完整的 Puppeteer 浏览器 API从而处理多标签页、新窗口等 Cypress 命令体系难以直接覆盖的场景。读完本文你将掌握它的安装与 TypeScript 配置、浏览器兼容性边界、setup/retry/cy.puppeteer()三组 API 的完整用法以及插件在 Node 侧通过 WebSocket 接管 Cypress 所启动浏览器的底层实现机制并能复现仓库自带的多标签页示例测试。安装与 TypeScript 配置npm / yarn 安装npm install --save-dev cypress/puppeteeryarn add --dev cypress/puppeteer从 package.json 可以看到该包的关键依赖约束peerDependencies声明了cypress 13.6.0低于此版本的 Cypress 无法使用本插件dependencies内置了puppeteer-core当前为^21.2.1你无需额外安装即可使用默认版本发布的产物包括dist与support两个目录见files字段其中support目录就是浏览器侧cy.puppeteer()命令的实现。TypeScript 类型声明如果你使用 TypeScript需要在tsconfig.json中加入{ compilerOptions: { types: [cypress, cypress/puppeteer/support] } }这条声明对应的类型定义在 support/index.d.ts它为Cypress.Chainable接口注入了puppeteer(messageName: string, ...args: any[]): Chainable签名使cy.puppeteer(myHandler)这类调用获得类型提示。兼容性与浏览器支持边界使用cypress/puppeteer前需要满足以下条件Cypress 13.6.0插件依赖after:browser:launch事件获取浏览器的调试端点该事件从 13.6.0 起提供。src/plugin/setup.ts 中注册该事件失败时会明确提示 Ensure you are running Cypress 13.6.0仅支持 Chromium 系浏览器如 Chrome for Testing、Chromium、Electron。从源码看任务处理入口会检查cypressBrowser.family ! chromium并直接报错拒绝非 Chromium 系浏览器Chrome 品牌浏览器 137 的受限由于 Chrome 移除了--load-extension标志标准 Chrome 137 及以上版本在有头模式cypress open或cypress run --headed下无法工作建议使用 Electron、Chrome for Testing 或 Chromium 替代。注意该限制只影响有头模式在cypress run无头模式下任何版本的 Chrome 都正常工作。这一点在 setup.ts 中有硬性拦截当检测到majorVersion 137 name chrome isHeaded时抛出插件错误。工作机制浏览器命令如何驱动 Node 侧的 Puppeteer理解本插件的架构是高效使用它的前提。cy.puppeteer()命令本身运行在浏览器中但真正的 Puppeteer 自动化运行在 Node 进程中模式与cy.task()类似注册阶段你在cypress.config.ts的setupNodeEvents中调用setup()它做两件事——通过on(after:browser:launch)捕获 Cypress 启动浏览器后返回的webSocketDebuggerUrl再通过on(task, ...)注册一个名为__cypressPuppeteer__的内部任务见 setup.ts。调用阶段浏览器侧的cy.puppeteer(messageName, ...args)实现在 src/support/index.ts只是把消息名和参数序列化为{ name, args }经cy.task(__cypressPuppeteer__)投递到 Node 侧所以args 必须 JSON 可序列化。执行阶段Node 侧任务处理函数每次调用都会通过puppeteer.connect({ browserWSEndpoint: debuggerUrl, defaultViewport: null })连接到 Cypress 启动的那个浏览器执行你在onMessage中注册的对应处理函数然后把返回值或错误传回测试断言。收尾阶段处理函数执行完毕后插件会自动执行browser.disconnect()断开连接。对于有头 ChromiumElectron 除外还会先调用activateMainTab把焦点切回 Cypress 主标签页。关于第 4 步值得展开src/plugin/activateMainTab.ts 会向 Cypress 主标签页postMessage一条cypress:extension:activate:main:tab消息由随 Cypress 分发的 Chrome 扩展响应并恢复主标签页的激活状态超时 2000ms。Cypress 自身在浏览器内始终维护对主标签页的焦点如果 Puppeteer 代码把某个新标签页带到前台这条机制保证了后续 Cypress 命令还能继续正常执行——这也是 Troubleshooting 中扩展通信错误的来源。错误处理同样贯穿整条链路Node 侧捕获到的异常会被包装为{ __error__: { name, message, stack } }返回浏览器侧再把它重新抛出为可读的cy.puppeteer() failed with the following error信息见 setup.ts 与 support/index.ts。此外处理函数若返回undefined由于cy.task()对此会报错插件会统一转换为null返回。API 详解setup(options)Cypress 配置侧用于注册消息处理框架setup(options)Optionson必填setupNodeEvents提供的on事件注册函数onMessage必填字符串键到处理函数的对象详见下文puppeteer可选从puppeteer-core导入的puppeteer实例用于覆盖插件默认使用的puppeteer-core版本。onMessage的键即测试中cy.puppeteer(key)调用的目标名。处理函数运行在 Node.js 而非浏览器中因此函数体内不能使用 Cypress 命令和 DOM API。每个处理函数接收browser连接到 Cypress 所启动浏览器的 PuppeteerBrowser实例...args测试中cy.puppeteer()传入的其余参数已反序列化。retry(functionToRetry[, options])Cypress 配置侧Puppeteer 代码常在目标页面尚未打开/加载完成的时机运行retry用于对这类可能初次失败的函数进行重试retry(functionToRetry[, options])functionToRetry必填若抛错则继续按间隔重试若不抛错则返回其结果timeout可选总重试时长毫秒默认4000delayBetweenTries可选两次尝试之间的等待时间毫秒默认200。默认值与行为可从 src/plugin/retry.ts 确认超时后抛出Failed retrying after ${timeout}ms: ${err.message}。cy.puppeteer(messageName[, ...args])Spec 侧cy.puppeteer(messageName[, ...args])messageName必填与配置中onMessage的某个键一致...args可选传入处理函数的值必须 JSON 可序列化。返回值若有即为该命令链的产出可直接接.should()断言。// spec cy.puppeteer(testNewTab, value 1, 42, [true, false]) // Cypress config setup({ on, onMessage: { testNewTab (browser, stringArg, numberArg, arrayOfBooleans) { // stringArg value 1 // numberArg 42 // arrayOfBooleans[0] true / arrayOfBooleans[1] false } } })完整示例多标签页测试以下两个示例与仓库内的真实测试 cypress/e2e/multi-tab.cy.ts 及其配套 cypress.config.ts 一致fixture 页面位于npm/puppeteer/cypress/fixtures/下。示例中虽然使用标签页但同理适用于窗口——在 Puppeteer 看来标签页与窗口都由Page类封装本质相同。示例一切换到 Cypress 测试打开的新标签页演示要点切换到 Cypress 测试动作打开的标签页用retry获取新标签页的Page实例通过 Puppeteer 获取页面引用与内容把内容传回 Cypress 断言。spec.cy.tsit(switches to a new tab, () { cy.visit(/cypress/fixtures/page-1.html) cy.get(input).type(Hello from Page 1) cy.get(button).click() // 触发打开新标签页 cy .puppeteer(switchToTabAndGetContent) .should(equal, You said: Hello from Page 1) })cypress.config.tsimport { defineConfig } from cypress import type { Browser as PuppeteerBrowser, Page } from puppeteer-core import { setup, retry } from cypress/puppeteer export default defineConfig({ e2e: { setupNodeEvents (on) { setup({ on, onMessage: { async switchToTabAndGetContent (browser: PuppeteerBrowser) { // 用 retry 处理页面可能尚未打开/加载的时序问题 const page await retryPromisePage(async () { // 浏览器中最终会有两个标签页Cypress 标签页与新打开的标签页 // 在 Puppeteer 中标签页与窗口都叫 page const pages await browser.pages() const page pages.find((page) page.url().includes(page-2.html)) // 找不到就抛错让 retry 重试 if (!page) throw new Error(Could not find page) // 找到则返回 Page 实例 return page }) // Cypress 始终聚焦主标签页把目标页面带到前台再操作更稳妥 await page.bringToFront() const paragraph (await page.waitForSelector(p))! const paragraphText await page.evaluate((el) el.textContent, paragraph) // 清理 ElementHandle 引用 paragraph.dispose() await page.close() // 返回值即 cy.puppeteer() 链的产出 return paragraphText }, }, }) }, }, })示例二由 Puppeteer 主动创建新标签页演示要点向setup传入自定义的puppeteer实例覆盖默认版本从cy.puppeteer()向消息处理函数传参通过 Puppeteer 创建新标签页并访问页面取回内容在 Cypress 中断言。spec.cy.tsit(creates a new tab, () { cy.visit(/cypress/fixtures/page-3.html) // 从页面取动态值传给 Puppeteer 消息处理函数 cy.get(#message).invoke(text).then((message) { cy.puppeteer(createTabAndGetContent, message) .should(equal, I approve this message: Cypress and Puppeteer make a great combo) }) })cypress.config.tsimport { defineConfig } from cypress import puppeteer, { Browser as PuppeteerBrowser, Page } from puppeteer-core import { setup, retry } from cypress/puppeteer export default defineConfig({ e2e: { setupNodeEvents (on) { setup({ on, // 传入自己的 puppeteer 实例覆盖插件默认版本 puppeteer, onMessage: { async createTabAndGetContent (browser: PuppeteerBrowser, text: string) { // 在 Cypress 启动的浏览器中创建一个新标签页 const page await browser.newPage() // text 来自 spec 中 cy.puppeteer() 的传参 await page.goto(http://localhost:8000/cypress/fixtures/page-4.html?text${text}) const paragraph (await page.waitForSelector(p))! const paragraphText await page.evaluate((el) el.textContent, paragraph) // 清理引用 paragraph.dispose() await page.close() // 返回值即 cy.puppeteer() 链的产出 return paragraphText }, }, }) }, }, })仓库自带的 cypress.config.ts 额外用 Express 在 8000 端口静态托管cypress/目录以提供 fixture 页面你自己的项目中通常已有baseUrl或 dev server直接复用即可。故障排查错误Cannot communicate with the Cypress Chrome extension若命令日志出现 Cannot communicate with the Cypress Chrome extension. Ensure the extension is enabled when using the Puppeteer plugin.说明插件无法与 Cypress 扩展通信。该扩展的作用是在 open 模式有头运行下Puppeteer 命令执行后重新激活 Cypress 主标签页。排查方向如果你在有头模式使用 Chrome 品牌浏览器且版本 137请改用 Chrome for Testing 或 Chromium访问chrome://extensions/确认 Cypress 扩展在 Cypress 启动的那个 Chrome 实例中已启用若企业安全策略拦截了扩展请通过扩展 IDcaljajdfkjjjdehjdoimjkkakekklcck放行。其他从源码可预见的报错Lost the reference to the browser通常发生在 Cypress 配置被热重载但浏览器未重新启动时关闭并重新打开浏览器即可Could not find message handler with the name ...cy.puppeteer()传入的名字与onMessage键不匹配错误信息会列出所有已注册的处理器名Only browsers in the Chromium family are supported当前浏览器不是 Chromium 系。构建、测试与验证本插件目录npm/puppeteer内可直接执行以下命令对应 package.json 的scriptsyarn build # 编译 TypeScript 到 dist/ yarn watch # 监听并增量重建 yarn cypress:open # 打开 Cypress 交互式测试即 multi-tab 示例 yarn cypress:run # 一次性运行 Cypress 测试默认 --browser chrome yarn test # 运行 vitest 单元测试 yarn test-watch # watch 模式运行单元测试单元测试覆盖三个核心模块test/unit/setup.spec.ts参数校验与任务注册行为、test/unit/retry.spec.ts默认 4000ms/200ms 的重试语义、test/unit/activateMainTab.spec.ts扩展激活消息与超时逻辑。该插件目前处于 public beta仓库鼓励使用者反馈以持续改进版本变更记录见 CHANGELOG.md。【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypress创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表