Cypress Runner 包深度解析cypress_runner.js 的打包架构、注入机制与迁移路线【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypresspackages/runner是 Cypress 仓库中负责把packages/driver测试驱动与packages/reporter结果报告器打包成可在测试 iframe 中运行的 webpack 产物的历史包生产环境所依赖的cypress_runner.js仍由它产出。本文将结合 runner 包内说明 与真实源码讲解该包的职责边界、四个 webpack 产物、加载与注入链路、跨域 iframe 通信以及它被packages/app渐进取代的现状帮助你理解 Cypress 运行时 iframe 的组装原理。Runner 包的角色定位与两个遗留职责在 AGENTS.md 中runner 被明确描述为a legacy webpack bundle它把packages/reporter和packages/driver打包到一起供 Cypress 的测试 iframe 使用。目前它正被packages/app渐进式取代但仍然拥有两项不可替代的职责产出生产环境的cypress_runner.jsbundle——测试 iframe 中一切运行逻辑driver reporter的来源维护 runner 级样式——即src/main.scss下聚合的 Legacy Cypress 样式。README.md 进一步列出了它在被完全移除前必须解决的五项遗留工作可作为理解其边界的最权威清单通过 webpack 打包packages/reporter与packages/driver一旦这些包可以被packages/app直接导入runner 即可移除打包packages/reporter的样式在main.scss中加载——理想情况下 reporter 应自持样式包含使用 webpack 专有 loader 的dom.js无法被packages/app的 Vite 开发服务器直接导入包含 Cypress Studio Recorder 相关代码曾在 Cypress 9.x 中标记为实验特性不属于 Cypress 10.x 首发范围代码暂留此处但未被 app 使用包含大部分可被清理的 Legacy Cypress 样式。从上述内容可以得出一个明确的工程信号不要在此包新增功能新功能应迁往packages/app。关键构建命令与产物清单构建入口在 package.json核心命令如下# 开发构建webpack 打包输出到 dist/ yarn workspace packages/runner build # 生产构建NODE_ENVproduction压缩产物 yarn workspace packages/runner build-prod # watch 模式带 --progress yarn workspace packages/runner watch值得注意的脚本细节prebuild会先执行rimraf ./dist确保每次构建从干净目录开始build实际执行的是根目录下的node ../../scripts/run-webpack即由仓库级 webpack 封装脚本驱动打包build-prod通过cross-env注入NODE_ENVproduction后再触发buildpostinstall会提示packages/runner needs: yarn build说明该包源码不随安装生效必须显式构建cypress:open/cypress:run两个脚本被故意改写为输出提示后以退出码 1 终止These tests have been moved to packages/app——从本包直接跑测试会报错因为测试已被迁移到packages/app。在 webpack.config.ts 中可以看到一次完整构建会同时产出4 个 bundle全部输出到dist/webpack entry产物文件名入口源文件用途cypress_runnerdist/cypress_runner.jssrc/index.js主测试 iframe 中运行的 driver reporter 聚合包cypress_cross_origin_runnerdist/cypress_cross_origin_runner.jssrc/cross-origin.js跨域secondary originiframe 中的 driverinjectiondist/injection.jsinjection/main.js注入到主 AUT 文档head的启动脚本injection_cross_origindist/injection_cross_origin.jsinjection/cross-origin.js注入到跨域 AUT 文档的启动脚本此外通过 CopyWebpackPlugin 会把packages/icons提供的favicon.ico一并复制到dist/配置见 webpack.config.ts。dist/被 package.json 的files字段声明为发布产物属于自动生成目录不应手工编辑。四个产物的架构地图与作用主 runnercypress_runner.js的组装入口链为 src/index.js → src/main.jsx →unified-runner.tsx// src/index.js import ./main.scss // runner 级样式含 Legacy Cypress 样式 import ./main.jsx// src/main.jsx import { UnifiedRunner } from ../unified-runner window.UnifiedRunner UnifiedRunner真正的组装发生在 unified-runner.tsx。它从packages/driver取到$Cypress即 Cypress 驱动本体及其$即 jQuery 引用从packages/reporter/src/main取到Reporter并暴露短命令、setReporterDocument、React、MobX 等运行时依赖最终整体挂到window.UnifiedRunnerexport const UnifiedRunner { CypressJQuery: $Cypress.$, CypressDriver: $Cypress, shortcuts, setReporterDocument, React, MobX, ReactDOM: { createRoot }, Reporter, }也就是说cypress_runner.js是 Cypress 运行时 iframe 的内核driver 负责命令执行与浏览器自动化reporter 负责测试结果的界面渲染runner 只是把二者用 webpack 粘合起来并提供共享的 React/MobX 运行环境。该文件由packages/server提供给浏览器详见下文加载链路。跨域 runnercypress_cross_origin_runner.jssrc/cross-origin.js 全文只有一行// this is the entry point for the cross-origin version of the driver import packages/driver/src/cross-origin/cypress它本质上是 driver 的 cross-origin 版本的入口别名当 AUT 存在跨域不同 origin 的iframe 时该 bundle 让 Cypress 在次级 origin 的 iframe 中也能运行命令如跨域cy.visit/ 交互其通信细节由 driver 内的 cross-origin 实现承担。注入脚本injection.js与injection_cross_origin.js这两个 bundle 与前两个不同——它们由 webpack.config.ts 中的getSimpleConfig()即 packages/web-config 提供的轻量配置构建强制mode: production且刻意保持轻量、少依赖因为它们是注入到 AUT 页面head里的脚本由packages/proxy注入见源码注释。主 origin 注入 injection/main.js的工作流通过window.Cypress parent.Cypress从父窗口runner iframe继承 Cypress 全局若缺失则直接抛错调用patchXmlHttpRequest(window)对主 AUT frame 的 XHR 打补丁当Cypress.config(removeSRIAttributes)开启时patchElementIntegrity(window)剥除script/link的 SRI 属性避免被 proxy 改写过的第一方资源被 SRI 拦截在AUT 自身上下文内包裹定时器createTimers()timers.wrap()并用Cypress.on(app:timers:reset / app:timers:pause)订阅父级事件——注释明确解释了为什么要在这里包裹而非在 driver 中若在 driver 里做timer 回调抛出的未捕获错误会被顶层 frame 的error处理器接走而不是 AUT 的最后通过Cypress.action(app:window:before:load, window)通知父级 Cypress 实例。跨域注入 injection/cross-origin.js更为完整涉及跨域桥接的核心机制寻找桥接 framefindCypress()遍历window.parent.frames找到定义了Cypress且与自身 origin 相同的 frame 作为通信桥用 try/catch 吞掉跨域访问抛出的SecurityErrorDOMException监听三类事件通过message事件回显自身location.href通过beforeunload向parent广播cross:origin:before:unload即便对应 spec bridge 尚未建立也要通知通过error事件把未处理异常转发到window.top消息cross:origin:aut:throw:error按需打补丁patchDocumentCookie模拟 cookie 场景、patchFetch/patchXmlHttpRequest跟踪凭据使用且当modifyObstructiveCode开启时用Object.defineProperty(window, frameElement, { get: () null })伪装未被 iframe 包裹规避页面中的反 iframe 检测代码延迟挂接把真正的 Cypress 挂接封装成window.__attachToCypress(Cypress)等待 spec bridge跨域 runner创建完成后由 Cypress 侧调用调用后自毁delete window.__attachToCypress以规避Cypress全局尚未就绪时的竞态代码中同时检查Cypress Cypress.cy。注runner 包内injection/目录还包含 timers、SRI integrity 以及跨域 cookie/fetch/XHR 的具体补丁实现见 injection/patches。运行时加载链路从 iframe 到 cypress_runner.jsrunner 打包出的dist/cypress_runner.js由packages/server提供给浏览器。在 static/index.html 中可以看到典型的 runner iframe 页面结构link relstylesheet href/{{namespace}}/runner/cypress_runner.css div idapp/div script typetext/javascript src/{{namespace}}/runner/cypress_runner.js/script script typetext/javascript // set a global so we know the top window window.__Cypress__ true setTimeout(function () { Runner.start(document.getElementById(app), {{base64Config | safe}}) }, 0) /script要点解读页面以{{projectName}}、{{namespace}}、{{base64Config | safe}}等模板变量注入项目名、命名空间与 base64 编码的运行配置说明该 HTML 由服务端渲染/插值后下发window.__Cypress__ true标记当前窗口为顶层 Cypress 运行窗口这也是 injection 脚本里parent.Cypress能取到驱动的依据之一样式与脚本均以/namespace/runner/为前缀指向 server 托管的 runner 静态资源由此印证 AGENTS.md 中Builtdist/cypress_runner.jsis served bypackages/serverto the browser的集成关系。构建配置细节与 Nx 隐式依赖webpack 构建配置要点webpack.config.ts 展示了若干值得留意的工程细节prismjs 语法高亮从packages/web-config的 commonConfig 中找到 babel-loader向其插件列表追加babel-plugin-prismjs为javascript / typescript / jsx / tsx四类语言启用line-numbers、line-highlight插件且css: false样式由 runner 自身 scss 控制模块别名alias为bluebird与lodash显式设置require.resolve别名避免多实例或版本漂移异步默认导出构建前会await waitUntilIconsBuilt()见 scripts/ensure-icons确保 favicon 等图标资产先构建完成再读取packages/icons路径进行拷贝规避资源竞态。Nx 隐式依赖与缓存失效由于 driver、reporter、config 的源码会被直接打进dist/cypress_runner.jsrunner 必须向 Nx 声明这些隐式依赖否则改动上游源码时缓存不会被正确失效、产物将过期。见 package.jsonnx: { implicitDependencies: [ packages/driver, packages/reporter, packages/config ] }README.md 中的Implicit Dependencies一节也专门解释了这一点正因为这些包的源码被打包进 runner 产物必须让 Nx 感知它们的变化以触发重建。这是理解改 driver/reporter 代码后 runner 为何自动重编的关键。开发者注意事项Gotchas来自 AGENTS.md 的注意事项同样值得展开已弃用本包正被packages/app取代不要新增功能需要新能力请迁移到 packages/app本地无法跑测试cypress:open/cypress:run已被显式禁用脚本直接 exit 1测试已迁至packages/app上游改动触发重建上述三个隐式依赖中任一变化都会使 runner 的 Nx 缓存失效并触发重新打包dom.js与 Vite 不兼容该文件依赖 webpack 专有 loader无法被packages/app的 Vite dev server 直接 import——这正是它至今仍留在 runner、无法迁走的技术原因dist/不可手改目录由 webpack 自动生成任何手工改动都会在下次构建含prebuild的rimraf ./dist中被清除。与packages/app的关系及未来走向理解 runner 最需要把握的一条主线是它的过渡态地位历史形态driver 与 reporter 由 runner 用 webpack 打包后整体塞进测试 iframe形成cypress_runner.js演进方向packages/app是新一代聚合层最终希望直接 import driver/reporter并可用 Vite 开发从而让 runner 这个webpack 粘合层退役现状约束在dom.js摆脱 webpack 专有 loader、reporter 能自持样式、Studio 代码去向明确之前runner 仍承担着生产环境 bundle 与样式职责。因此阅读 runner 相关代码时应把它视作理解 Cypress测试 iframe 运行时的真实产物生成链路的入口同时在概念上接受其渐进退场的定位——这正是 packages/runner 三个说明文档CLAUDE.md、AGENTS.md、README.md反复传达的核心信息。【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypress创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考