从 v1.0 到 v5.0:@cypress/angular 的演进史、Angular 支持矩阵与升级迁移指南
@cypress/angular 是 Cypress 官方提供的 Angular 组件测试(Component Testing)适配器,核心能力是让开发者把 Angular 组件直接挂载进 Cypress 浏览器中编写交互与断言。本文以 npm/angular/CHANGELOG.md 为主线,结合本仓库中该包的 README.md、package.json 与 mount 源码,系统梳理从 2022 年首个 1.0.0 到 5.0.0 的版本脉络,回答"每个大版本支持哪些 Angular 版本、引入了哪些破坏性变更、应该怎样升级"这三个核心问题,并下沉到 mount 的实现原理。
包定位:它是做什么的,为什么大多数人不需单独安装
@cypress/angular 在 npm 上发布,向开发者导出 mount 命令及配套类型,用于在 Cypress 测试运行器中挂载 Angular 组件。其定位在 README.md 中有明确说明:该包随 cypress 主包一起分发,一般不需要单独安装。只有当你需要 mount 之类的进阶能力时才应显式安装并从 @cypress/angular 导入。若只是搭建标准 Angular 组件测试项目,官方推荐直接使用 Cypress 自带的脚手架与 Angular Component Testing 文档 中的指引,不必手工处理该适配器。
在当前的仓库快照中,包自身的 package.json 对版本号的描述是 0.0.0-development(monorepo 内各 npm 包统一使用该占位版本号,真正对外版本由发布流程基于 CHANGELOG 生成),并声明:
{
"peerDependencies": {
"@angular/common": ">=21.0.0",
"@angular/core": ">=21.0.0",
"@angular/platform-browser": ">=21.0.0",
"rxjs": ">=7.8.0"
},
"keywords": ["angular", "cypress", "test", "testing", "zoneless"]
}
从 peerDependencies 与 keywords 可以确认:当前主线版本的适配对象是 Angular 21+ 与 zoneless 变更检测,这与 5.0.0 的发布内容完全吻合。
源码布局与构建管线
包体很小、结构清晰,便于源码级阅读:
- src/index.ts:公共入口,仅一行
export * from './mount'; - src/mount.ts:全部挂载逻辑,包括 TestBed 配置、Signals 处理、清理钩子等;
- rollup.config.mjs:Rollup 构建配置,产物为 ESM(
formats: ['es']),并将@angular/*、@angular/core/rxjs-interop等声明为external,不打进包体; - package.json 中
postbuild执行scripts/sync-exported-npm-with-cli.js,把产物同步到cli/angular目录供 Cypress 二进制分发。
与之配套的 AGENTS.md(npm/angular/AGENTS.md)进一步说明:开发期运行 yarn build(Rollup 编译到 dist/ 并同步至 cli/angular)、yarn check-ts(tsc --noEmit 类型检查)、yarn lint(ESLint)。构建前务必先完成编译,因为 Cypress 二进制里实际发布的是 cli/angular 这份拷贝。
Angular 版本支持矩阵:13 到 21 的一次性俯瞰
把 CHANGELOG 与 README 的 Requirements 小节拼合,可以得到完整的"包大版本 × Angular 版本"对应关系:
| @cypress/angular 大版本 | 支持的 Angular | 对应 Cypress | 关键节点 |
|---|---|---|---|
| v1.x(2022-08 起) | Angular 13–16 时代 | Cypress 10/11 时代 | mount 落地、模板/teardown/standalone 支持 |
| v2.x(2022-11 起) | Angular 13–16 | Cypress 12/13 时代 | 多次 mount 清理、Angular Signals CT Harness |
| v3.x(2025-01 起) | Angular 17.2+(含 19) | Cypress 14 | 移除 13–16 支持 |
| v4.x(2025-08 起) | Angular 18、19 | Cypress 15 | 移除 Angular 17 支持 |
| v5.x(2026-08 起) | Angular 21+ | Cypress 16 | 合并 zoneless 上游、移除 18–20 支持 |
其中 README 给出的官方摘要为:
@cypress/angular@2支持 Angular 13–16;@cypress/angular@3支持 Angular 17(更准确地说,v3.0.0 的 BREAKING CHANGES 注明是 17.2 及以上);@cypress/angular@4支持 Angular 18–19;@cypress/angular@5及当前主线要求 Angular>=21.0.0。
这意味着适配器的每个大版本都会主动收窄对老 Angular 的支持范围,升级前务必对照本表核对项目锁定的 Angular 主版本。
逐版本演进记录:2022—2026 的完整时间线
以下按 CHANGELOG 的发布顺序,把 13 个版本(若干版本存在重复的自动生成条目,此处合并表述)的功能、修复与破坏性变更完整归档。
v1.0.0(2022-08):Angular 组件测试的起点
首个正式版本围绕"Angular CT 支持从实验走向可用"展开,集中落地的能力包括:
mount本体:feature "angular mount"(#22858);- 开启 Angular Component Testing 支持(#23089);
- 支持模板字符串直接挂载、teardown(组件销毁)与 standalone 组件(#23117);
- 支持组件挂载容器选择器迁移:将
#__cy_root切换为data-cy-root(#20951),这也是后续断言cy.get选择根容器的事实标准; - 脚手架模板与配置文件路径修正(#19776、#20047)、按 testingType 收敛配置(#20677、#19364)、未迁移配置的报错信息改进(#21467);
- 配置体系演进:从
integrationFolder/componentFolder走向specPattern(#19319),支持.config文件(#18578)、devServer字段(#18962、#20092)与 plugins-on-config(#18798); - 工程侧:支持 webpack-dev-server v4(#17918)、移除
testFiles引用(#20565)、CLI 从run-ct/open-ct全面切换到--ct(#18422); - 依赖修复:rxjs 依赖锁定
>6.6.0(#16676),移除导致 semantic-release 失败的依赖(#23142)。
v1.1.x(2022-08 ~ 2022-10):稳定性修正
- Angular 14.2 下
mount编译报错的修复(#23593); - 补齐测试中缺失的
it.skip支持(#23829); - 挂载后主动调用
ngOnChanges(#23596)——这是纯类语法输入绑定的关键补偿逻辑,详见后文源码解析; - 同时期仓库顺带加入了 Svelte 组件测试支持(#23553,属于旁路功能)。
v2.0.0(2022-11):多次挂载行为收敛 + 服务覆盖
- BREAKING:在同一个测试中重复调用
mount时,会先移除上一次挂载的组件(#24470),避免 DOM 中残留旧组件导致断言错乱; - 支持在 Angular 组件测试中覆盖全局服务(#24394);
- v2.0.1/v2.0.2/v2.0.3 的修复:组件派生信息为空时不再抛错(#24571);根挂载元素从
[data-cy-root](#25807)到"在原始位置挂载 cy-root"(#25965)的进一步修正。
v2.0.4(2024-06)与 v2.1.0(2024-07):Signals 时代到来
- 全仓库 TypeScript 升级到 5(#29568);
- 新增 Angular Signals CT Harness(#29621),面向 Angular 17.2+,允许用户在组件测试里使用 Angular Signals。
v3.0.0 / v3.0.1(2025-01 / 2025-07)
- 支持 Angular 19,并同步更新测试套件(#30675);
- BREAKING(Cypress 14):移除 Angular 13–16 支持,要求 Angular 17.2 及以上;
- v3.0.1:确保
cy.mount具备引用安全性(reference safe),修复 #31238 与 #31983 两个 issue(#31993)。
v4.0.0 / v4.1.0(2025-08 / 2025-12)
- BREAKING(Cypress 15):移除 Angular 17 支持,仅支持 Angular 18 与 19,并同步修正脚手架依赖与系统测试配置(#31446、#31303 等系列提交);
- 保证遗留(legacy)输出 spy 按预期工作(#32158);
- v4.1.0:新增
cypress/angular-zoneless测试装置(testing harness),面向 Angular 21 且兼容 Angular 20(#33025)——这是通往 5.0 的过渡形态。
v5.0.0(2026-08):zoneless 主线化,Angular 21 专属
这是当前仓库快照所对应的大版本,破坏性变更最密集:
- 将
@cypress/angular-zoneless的上游实现合并进@cypress/angular,同时移除对 Angular 18、19、20 的支持;@cypress/schematic仅支持 Cypress 16,且不再脚手架生成 angular-zoneless 的 mount handler;@cypress/angular-zoneless在 npm 上随之废弃(deprecated); - 移除对
@angular/platform-browser-dynamic的依赖,统一改用@angular/platform-browser; - 构建目标(build targets)从
es2020提升到es2022——官方声明这虽是 breaking 但影响应最小。
把 5.0.0 与当前 package.json(peer 依赖 @angular/* >= 21、开发依赖 ^21.0.0、provideZonelessChangeDetection 相关实现)对照即可确认:zone.js 时代彻底结束,zoneless 变更检测成为唯一路径。
从源码看 v5 的挂载实现:zoneless 如何工作
理解 CHANGELOG 中 5.0.0 的变更,最好直接读 src/mount.ts。该文件是整个包的实现核心,其设计能解释大量 changelog 条目背后的动机。
TestBed 初始化与"一次环境、逐测清理"模型
模块加载时即初始化测试环境(文件中最后一次性的环境搭建):
getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting(), { teardown: { destroyAfterEach: false } });- 通过
setupHooks(cleanup)注册 Cypress 测试钩子,使每个用例之间执行cleanup(); cleanup()内部调用非公开的getTestBed().tearDownTestingModule()移除 DOM 中的上一个组件——这正是 v2.0.0 破坏性变更(重复mount先移除旧组件)与后续data-cy-root/cy-root 位置修正的运行时落点;若 Angular 版本过旧导致 teardown 失败,会抛出带https://on.cypress.io/frameworks指引的错误。
模块引导:standalone、CommonModule 与 zoneless
bootstrapModule 对 MountConfig 做三件事:
- 归一化
declarations/imports/providers为空数组; - 注入自定义
CypressAngularErrorHandler(替换默认 ErrorHandler,把组件内异常重新抛出,使 Cypress 能捕获并让用例失败)与provideZonelessChangeDetection()——后者即 5.0.0"合并 zoneless 上游"的代码证据,参见源码注释@see https://angular.dev/guide/zoneless#using-zoneless-in-testbed; - 根据 Angular 组件元数据
ɵcmp.standalone判断组件是否为 standalone:是则放入imports,否则放入declarations;随后无条件并入CommonModule。
MountConfig:一个接口承载"声明、导入、提供者与组件输入"
源码中 MountConfig<T> extends TestModuleMetadata 额外暴露了 componentProperties 字段,允许直接注入组件 @Input(),甚至用 cy.spy() 替换 EventEmitter 型输出:
it('renders a button with Save text', () => {
cy.mount(ButtonComponent, { componentProperties: { text: 'Save' } })
cy.get('button').contains('Save')
})
返回值为 MountResponse<T>,携带 fixture: ComponentFixture<T> 与 component: T,方便在用例内继续操作组件实例。挂载完成后还会通过 Cypress.log 输出 mount 命令日志。
Signals 双向桥接:为何 v2.1 与 v4.1 的"harness"能成立
针对 Angular Signals,源码实现了运行时类型探测与自动订阅(这是 #29731 系列 issue 的产物):
isSignal通过组件属性上的Symbol(SIGNAL)判断;isInputSignal进一步检查函数名为inputValueFn;isModelSignal判断"writable signal 且带 subscribe";registerSignalEventsIfNeeded用toObservable建立信号→组件输入的单向同步,并用fixture.componentRef.setInput推送;- 当检测到组件端为 model signal(
xxxChange约定),还会建立组件→外部信号的反向回写;detectAndRegisterOutputSpyToSignal则让createOutputSpy('countChange')这类 spy 与 signal 输出协同工作; - 所有内部订阅被收集到
activeInternalSubscriptions,在cleanup()时统一unsubscribe(),防止跨用例内存泄漏。
类语法输入为何要手动触发 ngOnChanges
setupComponent 中有一段容易被忽略但很关键的逻辑:由于组件输入是通过把值赋给实例属性完成初始化的,ngOnChanges 生命周期不会自动触发,因此当组件实现 OnChanges 且有 componentProperties 时,代码会手工构造 SimpleChanges 并调用 component.ngOnChanges(...)——这正是 changelog v1.1.1(#23596)"call ngOnChanges after mount"的源码级实现,属于典型的"版本记录与实现一一对应"的细节。
模板字符串挂载与测试容器渲染器
- 传字符串时,
createComponentFixture通过TestBed.overrideTemplate(WrapperComponent, template)把模板字符串塞进内置的cy-wrapper-component(standalone: false),从而支持mount('<app-stepper></app-stepper>', { declarations: [StepperComponent] })这类不传组件类的写法; - 自定义的
CypressTestComponentRenderer继承TestComponentRenderer,把 Angular 的根元素插入由@cypress/mount-utils提供的容器getContainerEl()中,并在每次挂载前清空容器,从而保证 DOM 位置可控。
各版本关键 API 与使用范式对比
CHANGELOG 演进同样反映在 API 使用范式上,下表可作为迁移时的自查清单:
| 关注点 | v1 时期 | v2/v3 | v4 | v5(当前主线) |
|---|---|---|---|---|
| 根容器 | #__cy_root |
data-cy-root |
data-cy-root |
data-cy-root |
| 重复 mount | 残留旧组件 | 自动清理旧组件 | 自动清理 | 自动清理 |
| Signals | 不支持 | v2.1+ 支持(Angular 17.2+) | 支持 | 支持(含 model signal 双向同步) |
| zoneless | 无 | 无 | 通过 @cypress/angular-zoneless(v4.1 起) |
内建于 @cypress/angular |
| 组件输入传递 | componentProperties |
同左 | 同左 + 引用安全 | 同左 + InputSignal/WritableSignal 类型约束 |
其中"引用安全(reference safe)"值得展开:v3.0.1 修复 #31238/#31983 后,cy.mount 不再破坏传入对象/数组的引用;但源码注释也明确指出保留一个例外——用户主动传入 Cypress 输出 spy(EventEmitter 实例)时仍按兼容逻辑直接赋值,从而保证既有测试写法不回归。
升级迁移建议
基于 CHANGELOG 的破坏性变更记录,可以提炼出三条可执行结论:
- 升级顺序不能跳步:Angular 支持面按"13–16 → 17.2+ → 18/19 → 21+"单向收窄,若项目当前使用 Angular 17,应先升级到
@cypress/angular@3(对应 Cypress 14)验证,再考虑进入 18/19(v4,Cypress 15),最终迁到 21+(v5,Cypress 16)。任何一次跨大版本跳跃都可能同时触发适配器与 Cypress 主版本的双重变更。 - v5 迁移要检查三处代码:① 确认 Angular 已升级到 21 及以上,且项目中不再依赖 zone.js 引导路径;② 检查是否仍在 import
@angular/platform-browser-dynamic,如有需改为@angular/platform-browser;③ 若此前使用的是cypress/angular-zoneless入口或由 schematic 生成的 zoneless mount handler,请改为@cypress/angular自带的mount,因为该独立包已在 npm 上废弃、schematic 也不再生成对应 handler。 - 回归验证以组件测试全集为准:升级后优先跑一遍包含以下场景的用例——多次
mount同一测试、componentProperties传对象/数组(验证引用安全)、ngOnChanges生命周期断言、Signals/model 输入输出双向同步、以及模板字符串挂载,这些恰好覆盖了 CHANGELOG 中历次 bug fix 的回归面。
结语
@cypress/angular 的 CHANGELOG 虽然只是版本记录,却完整折射出 Cypress Angular 组件测试四年间的技术决策:从 #__cy_root 到 data-cy-root 的容器标准化,从"重复 mount 不清理"到"自动清理 + 引用安全"的行为收敛,从 zone.js 到 Angular Signals 再到 zoneless 的框架演进。对照 mount 源码 阅读,你能在每一行版本记录背后找到具体的实现落点——这正是把 changelog 从"发生了什么"升级为"为什么这么做"的最佳路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00