Cypress 浏览器扩展(@packages/extension)解析:Chrome MV3 与 Firefox MV2 双包设计

发布时间:2026/9/10 22:47:28
Cypress 浏览器扩展(@packages/extension)解析:Chrome MV3 与 Firefox MV2 双包设计 Cypress 浏览器扩展packages/extension解析Chrome MV3 与 Firefox MV2 双包设计【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypress本指南以 Cypress 仓库中 packages/extension/AGENTS.md 为骨架系统讲解 Cypress 在测试运行期间注入 Chrome / Firefox 的 WebExtension它并非同一份代码的两套构建而是按浏览器各司其职的两套独立 BundleFirefox 走 Manifest V2、Chrome 走 Manifest V3。读完本文你将掌握这套扩展的目录结构、两条 Bundle 各自的职责与消息流、构建与调试命令以及它与packages/server、packages/socket、cypress/puppeteer之间的集成关系。一、定位为什么 Cypress 需要浏览器扩展Cypress 的浏览器自动化以 CDPChrome DevTools Protocol与 WebDriver BiDi 为核心通道但二者并不能覆盖浏览器提供的全部能力。packages/extension正是 Cypress 在测试运行期间加载到浏览器中的 WebExtension它在扩展层面对浏览器进行自动化触达 CDP 与 BiDi 覆盖不到的 API。需要特别强调的核心设计是见 AGENTS.md这两套 Bundle 不是同一功能的两份构建——每一套只被一种浏览器加载且干着完全不同的活。Bundle目标浏览器Manifest职责app/v2/FirefoxV2通过socket.io回连 Cypress server推送 cookie / 下载事件处理reset:browser:stateBiDi 特意把该能力委托到这里app/v3/ChromeV3追踪主 Cypress 标签页 URL 并重新激活该标签页服务于cypress/puppeteer不建立 socket 连接二、整体架构与目录结构packages/extension包顶层按源码 / 编译产物 / 静态资源组织从 packages/extension 目录可直接看到完整布局app/ v2/ Firefox background.ts MV2 background pagecookie/download 事件、reset:browser:state client.ts socket.io 连接封装供 background page 使用 init.ts 页面加载时把 background page 连上 server manifest.json MV2 manifest v3/ Chrome content.ts content script桥接 Cypress 页面与 service worker service-worker.ts MV3 service worker取代 background page manifest.json MV3 manifest newtab.html / popup.html 扩展 UI 页面 lib-dist/ 编译后的 TypeScript 库代码发布物 app-dist/ 编译后的扩展 Bundle发布物 theme/ 扩展图标与 popup 主题资源其中lib/是包的 Node 侧入口输出工具方法查找/加载扩展因被 Node 环境消费而编译为 CommonJSapp/是真正被打包成浏览器扩展的源码app/v2与app/v3的 manifest 分属 MV2 / MV3theme/存放扩展图标与主题资源二者通过packages/icons与 gulp 任务被复制进产物。三、Firefox Bundleapp/v2Manifest V23.1 加载方式与回连链路V2 Bundle 由packages/server的firefox.ts通过utils.writeExtension注入// packages/server/lib/browsers/firefox.ts#L555 utils.writeExtension(browser, options.isTextTerminal, options.proxyUrl, options.socketIoRoute),其底层实现位于 packages/server/lib/browsers/utils.ts关键行为是调用 packages/extension/lib/index.ts 的setHostAndPath(proxyUrl, socketIoRoute)——把写死在源码里的占位符替换为运行时真实的 server 地址与 socket.io 路由// packages/extension/lib/index.ts#L18-L26 export const setHostAndPath async (host: string, path: string) { const src getPathToExtension(background.js) const str await readFile(src, utf8) return str .replace(CHANGE_ME_HOST, host) .replace(CHANGE_ME_PATH, path) }占位符来源于 app/v2/init.tsbackground page 在加载时立即调用automation.connect(CHANGE_ME_HOST, CHANGE_ME_PATH)完成回连之后由 server 端把常量注入成真实地址。3.2 消息协议与事件推送background.tsapp/v2/background.ts 定义了完整的 socket 消息协议核心分三块下行请求分发监听automation:request目前只处理reset:browser:state其余消息会以automation:response返回No handler registered for: msg错误automation:config事件到达后注册各类监听器connect后向 server 发automation:client:connected。cookie 变更推送browser.cookies.onChanged中忽略cause overwrite的变更覆盖写不视为一次真实变化其余通过automation:push:request/change:cookie上报。下载事件推送browser.downloads.onCreated上报create:download含 id、filePath、mime、urlbrowser.downloads.onChanged根据state.current分别上报complete:download与canceled:download。resetBrowserState是对browser.browsingData.remove的封装一次性清空cache、cookies、downloads、formData、history、indexedDB、localStorage、passwords、pluginData、serviceWorkers。代码注释特意注明Firefox 不支持fileSystems与serverBoundCertificates且Chrome 走的是 CDP 自动化路径而非此扩展路径// packages/extension/app/v2/background.ts#L105-L110 resetBrowserState (fn: any) { return browser.browsingData.remove({}, { cache: true, cookies: true, downloads: true, /* ... */ serviceWorkers: true }).then(fn) }3.3 传输封装client.ts与 Manifestapp/v2/client.ts 直接复用packages/socket/browser/client强制transports: [websocket]实现浏览器扩展到 Cypress server 的双向实时通信——这正是 packages/socket 运行时依赖的落点。v2 manifest.json 的关键字段说明其能力边界applications.gecko.id: automation-extensioncypress.ioFirefox 专用扩展标识permissions含cookies、browsingData、downloads与 background.ts 中三块职责一一对应background.scripts: [background.js]MV2 的常驻 background page 模型另有browser_actionpopup、chrome_url_overrides.newtab与图标资源。四、Chrome Bundleapp/v3Manifest V34.1 职责标签页追踪与重新激活V3 Bundle 只解决一个非常具体的问题执行cypress/puppeteer任务时浏览器会切到 Puppeteer 控制任务结束后需要把 Cypress 主标签页重新带到前台。它不建立任何 socket 连接由 app/v3/service-worker.ts 与 app/v3/content.ts 协作完成。完整消息链路为Cypress 主页面通过window.postMessage发送cypress:extension:url:changed携带当前 URL或cypress:extension:activate:main:tabcontent.tscontent script通过chrome.runtime.connect()建立与 service worker 的 port只接收source window的消息把二者转发为url:changed/activate:main:tabservice-worker.ts 收到url:changed后把最新 URL 写入chrome.storage.local的mostRecentUrl收到activate:main:tab后chrome.tabs.query({})找到 URL 匹配的 Cypress 标签页并chrome.tabs.update(id, { active: true })将其置前——注意注释说明这不会让 Chrome 从其他应用抢走焦点激活成功后 service worker 回发main:tab:activatedcontent script 再经postMessage通知 Cypress 页面cypress:extension:main:tab:activated。关于隔离性源码注释content.ts讲得很清楚content script 能访问 DOM、但无法直接调用扩展 API所以要用 postMessage 与页面通信、用 messaging API 与 service worker 通信service worker 则反之有扩展 API 但没有页面访问权。三方各司其职、逐级桥接。4.2 与服务器侧的接线packages/server/lib/browsers/chrome.ts 在模块加载时即取得 V3 产物路径extension.getPathToV3Extension()见 lib/index.ts并在启动浏览器时由_writeExtension同文件第 387 行附近把扩展注入 Chrome 启动参数。因此V3 路径不需要setHostAndPath的占位符替换也不依赖 socket——这与 V2 形成鲜明对照。4.3 ManifestMV3 形态v3 manifest.json 展示了完整的 MV3 声明permissions只需tabs、storage无 cookie / downloads / browsingData因为不干那些活background.service_worker: service-worker.js事件驱动、可随时休眠的 MV3 常驻模型content_scripts匹配http://*/*、https://*/*、all_urls注入content.js沿用actionpopup 与chrome_url_overrides.newtab。五、构建管线与常用命令5.1 关键命令来自 AGENTS.md 的常用命令可直接在仓库根目录执行# 同时构建 V2 与 V3 两套扩展 Bundle yarn workspace packages/extension build # 运行某个具体测试文件 yarn workspace packages/extension test -- path-to-spec # 按 glob 模式运行匹配的测试 yarn workspace packages/extension test -- glob-pattern # 类型检查 yarn workspace packages/extension check-tspackage.json 中还提供一组开发辅助脚本test-unitvitest run、test-watch变更即重跑、test-debugvitest 断点调试、watch先 build再用chokidar监听app/**文件变化自动重建、clean清理app-dist与lib-dist。5.2 两套 Bundle 的差异化构建策略gulpfile.ts 是唯一构建入口build任务串行执行export const build gulp.series( clean, buildAppV2, // 内部执行 yarn build:v2 buildAppV3, // 内部执行 yarn build:v3 gulp.parallel( icons, logos, manifest(v2), manifest(v3), html, css, buildLib, ), )三种编译方式并存这也是 AGENTS.md 特别提醒的 GotchaV2 →webpack-clibuild:v2配置见 webpack.config.mjsV2 依赖webextension-polyfill、packages/socket/browser/client等外部模块需要打包成单一background.jsV3 →tsc -p tsconfig.app.v3.jsonbuild:v3V3没有任何外部运行时依赖可以直接编译为浏览器原生可执行的 ESMlib →tsc -p tsconfig.lib.jsonbuild:lib库代码被 Node 环境消费编译为 CommonJS。gulp 还会把app/**/*.html、app/**/*.css同时复制进app-dist/v2与app-dist/v3把packages/icons提供的图标复制进两套产物的icons/manifest 各自落到对应目录。六、测试、类型检查与运行要求6.1 测试组织测试基于 Vitest见 vitest.config.ts围绕每套 Bundle 的独立职责分层组织test/unit/extension.spec.tslib 侧工具方法的单元测试test/integration/v2/background.spec.ts配合 test/helpers/background.js 验证 Firefox MV2 的 socket 回连与消息处理test/integration/v3/content.spec.ts 与 test/integration/v3/service-worker.spec.ts分别验证 content script 的消息桥接与 service worker 的标签页激活逻辑。check-ts则执行tsc --noEmit与仓库统一的tslint检查。6.2 安装后的额外构建要求与packages/electron类似yarn install之后必须执行一次yarn build因为该包的postinstall脚本只打印一句提醒packages/extension needs: yarn build而不会真正触发构建见 package.json。扩展产物是运行时注入浏览器的跳过这步会导致后续测试加载不到扩展。七、浏览器内调试指南README.md 给出了两套浏览器各自的调试流程与双 Bundle 设计一一对应Chrome调试 V3 service worker打开 Chrome进入chrome://extensions勾选右上角Developer Mode点击左上角Load unpacked extension...选择packages/extension/app-dist/v3目录在 Inspect views 中点击service worker调试service-worker.js修改manifest.json后点击Reload⌘R重新加载。V3 源码注释还补充了一个更直接的调试入口新开标签页访问chrome://inspect左侧选择 Service Workers 并inspect若扩展更新偶发不生效可能需要重启 Chrome 后再次在chrome://extensions下 Reload见 service-worker.ts 顶部注释。Firefox调试 V2 background page通过cypress open启动 Firefox新标签页访问about:debugging左侧点击This Firefox在Temporary Extensions下找到Cypress扩展点击Inspect打开独立控制台窗口随后关闭about:debugging标签页在控制台的Debugger标签中可以看到background.js按需设置断点即可。八、工程化 Gotchas 与集成关系8.1 工程化注意点V2 由webpack-cli产出、V3 由tsc直接产出二者产物形态不同见上文 5.2主buildgulp 任务负责统一编排package.json 中nx.implicitDependencies声明了packages/server与packages/socket二者任一变更都会在 CI 中触发本包重建——因为 server 是扩展的注入方、socket 是 V2 的运行时通信库。8.2 依赖与被依赖关系从 AGENTS.md 与源码可归纳出三层集成运行时依赖packages/socketV2 的浏览器端 socket.io 客户端直接复用 packages/socket/browser/client见 app/v2/client.ts图标依赖packages/iconsgulp 构建时用getPathToIcon/getPathToLogo拉取图标与 logo 复制进两套产物见 gulpfile.ts被packages/server消费V2 经 firefox.ts → utils.ts 的writeExtension注入V3 经 chrome.ts 的getPathToV3Extension拿到产物路径并随启动参数注入。从源码结构看这套扩展层自动化 CDP/BiDi 自动化并存的架构本质是 Cypress 为不同浏览器挑选其最可靠的能力通道能在 CDP/BiDi 层解决的能力不走扩展走不通的Firefox 的历史数据清理、Chrome 多标签页管理才交由 WebExtension 兜底。理解这一点是后续为 Cypress 扩展新增浏览器能力时判断放哪套 Bundle、走哪条消息通道的出发点。九、速查清单定位测试运行期注入浏览器的 WebExtension触达 CDP / BiDi 覆盖不到的 APIV2 FirefoxMV2 background page socket.io 回连管 cookie / downloads 推送与reset:browser:stateV3 ChromeMV3 service worker content script追踪mostRecentUrl并重新激活主标签页供 Puppeteer 任务后恢复上下文构建yarn workspace packages/extension buildV2 走 webpack-cli、V3 走 tsc、lib 走 tsc(CJS)由 gulp 统一编排测试 / 类型testvitest、test-watch、test-debug、check-ts务必注意install 后需手动yarn build改动packages/server/packages/socket会触发本包 CI 重建深入源码双 Bundle 消息流见 app/v2/background.ts、app/v3/service-worker.ts、app/v3/content.ts注入逻辑见 packages/server/lib/browsers/utils.ts 与 packages/server/lib/browsers/chrome.ts。【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypress创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考