首页
/ Angular 仓库 AI Agent 协作规范:AGENTS.md 环境约定、Zoneless 测试模式与 Bazel 开发实践

Angular 仓库 AI Agent 协作规范:AGENTS.md 环境约定、Zoneless 测试模式与 Bazel 开发实践

2026-09-05 19:32:49作者:瞿蔚英Wynne

本文以 Angular 框架源码仓库根目录的 AGENTS.md 为核心,完整解读这份面向 AI Agent 的仓库协作指南:从 pnpm + Bazel 的环境约定,到 "Zoneless & Async-First" 测试范式中的 Act/Wait/Assert 三步法,再到提交信息与 PR 流程规范。读完之后,你能掌握在该仓库中运行目标、编写符合框架测试风格规范的单元测试、以及产出符合提交格式要求的变更所需的完整工作流。

环境与基础约定

AGENTS.md 开头明确了仓库定位——这是 Angular 框架的源码仓库("This is the source code for the Angular framework"),并给出了两条最基础的环境约定:

  • 包管理统一使用 pnpm,而不是 npm 或 yarn;
  • 运行测试统一使用 pnpm bazel test //target,其中 //target 为 Bazel 目标路径。

这两条约定与仓库配套文档完全一致。building-and-testing-angular.md 说明了开发环境的完整前置条件:Git、Node.js(版本由 .nvmrc 指定)、pnpm(版本由 package.jsonengines 字段指定);安装依赖只需 pnpm install,构建产物通过 pnpm build 输出到 dist/packages-dist

Bazel 的使用方式在 building-with-bazel.md 中有更细的说明:Angular 通过 npm 包 @bazel/bazelisk 安装 Bazel 本体(而非让贡献者自行安装),以保证所有人使用同一版本,统一通过 pnpm bazel 调用。常用命令模式为:

# 构建单个包
pnpm bazel build packages/core
# 构建全部包
pnpm bazel build packages/...
# 在 Node 中测试某个包的测试目标
pnpm test packages/core/test:test
# 通过 karma 在浏览器中测试
pnpm test packages/core/test:test_web
# 测试所有包
pnpm test packages/...

其中 ... 是 Bazel 的通配符,表示执行指定路径下的所有测试;首次构建通常比后续构建慢很多,因为 Bazel 会非常有效地缓存构建结果。

关键文档地图

AGENTS.md 用 "Key Documentation" 一节将三份文档指定为权威依据,这也是本仓库贡献流程的三大支柱:

文档 职责
Building and Testing 运行各类构建/测试目标的权威指南
Coding Standards TypeScript 等文件的风格规范
Commit Guidelines 提交信息与 PR 标题的格式

这三份文档均位于 contributing-docs/ 目录下,下面分别展开其核心内容。

构建与测试:格式、Lint 与本地验证

building-and-testing-angular.md 中的几项硬约束值得特别关注,因为 CI 会强制执行:

  • 提交 PR 前必须执行全量测试套件pnpm test //packages/...。即使忘记运行部分测试,CI 也会执行受影响测试并报错,但 PR 只有在代码格式正确且所有测试通过时才能合并。
  • 格式化使用 prettier,源码未正确格式化会导致 CI 失败。可用命令:
    • pnpm ng-dev format changed [shaOrRef]:仅格式化自指定 sha/ref 以来的改动文件(默认相对 main);
    • pnpm ng-dev format all:格式化全部源码;
    • pnpm ng-dev format files <files..>:仅格式化指定文件。
  • 风格校验pnpm lint 一次性检查格式与编码风格。
  • 本地库联动验证:用 pnpm ng-dev misc build-and-link <path-to-local-project-root> 可以本地构建 Angular 并 pnpm link 到本地项目验证变更;此时需 ng cache disable 关闭 CLI 磁盘缓存,且启动 CLI 时必须带 --preserve-symlinks 标志(否则符号链接被解析为真实路径会导致模块解析失败)。

building-with-bazel.md 进一步补充了调试手段:--config=debug 以调试模式运行测试;Node 测试可通过 pnpm bazel test packages/core/test:test --config=debug 配合 Chrome DevTools(chrome://inspect 的 "Open dedicated DevTools for Node")或 VSCode 的 Attach 配置(端口 9229)调试;karma 测试则在目标名后追加 _debug 后访问 http://localhost:9876/debug.html

编码标准:与框架本体同源的代码风格

coding-standards.md 明确其适用范围是 Angular 框架本身的开发(而非用 Angular 构建应用),以 Google JavaScript Style Guide 为基础,并用 prettier 强制自动格式化。其中与测试工作最相关的几条规范:

  • 注释要解释 "why" 而非 "what":说明代码为何如此实现(例如某个 tabindex 处理是为了防止 ngAria 过度添加)的注释远比复述代码行为的注释有价值;TypeScript 中公共 API 使用 JSDoc,其他说明用 //
  • 测试命名要有描述性:jasmine 测试名理想情况下应读起来像一句话,常见形式是 "it should...":
// 推荐:完整描述被测场景
describe('Router', () => {
  describe('with the default route reuse strategy', () => {
    it('should not reuse routes upon location change', () => { ... });
  });
});

// 避免:无法说明被测场景
it('should work', () => { ... });
  • 测试类同样要有描述性命名(FormGroupWithCheckboxAndRadios 优于 Comp)。
  • 其余通用规则包括:避免 any、优先 const/readonly、布尔属性用 is/has 前缀、Observable 不以 $ 结尾、导入 rxjs 的 of 时别名化为 observableOf 等。

提交信息:header + body + footer 三段式

commit-message-guidelines.md 规定了严格的提交信息结构:

<header>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
  • header 为必填,格式为 <type>(<scope>): <short summary>。type 必须是以下之一:buildcidocsfeatfixperfrefactortest;summary 用祈使句、现在时、首字母小写、结尾不加句号。
  • scope 为可选,应填写受影响的 npm 包名(如 animationscommoncompilercompiler-clicoreformshttplocalizeplatform-browserplatform-serverrouterservice-workerupgradezone.js 等)。少数例外:dev-infra 用于 /scripts/tools 目录内的开发基建变更;docs-infra 用于 /adev 目录(angular.dev 应用本身)的基础设施变更,而纯文档内容(如编辑 .md 文件)应使用不带 scope 的 docs:migrations 用于 ng update 迁移;devtools 用于浏览器扩展(devtools/ 项目)。
  • body 除 docs 类型外为必填,且至少 20 个字符,用于解释变更动机(why),可对比前后行为。
  • footer 可选,承载破坏性变更与弃用声明:BREAKING CHANGE: 开头需附变更摘要 + 空行 + 详细描述与迁移指引;DEPRECATED: 同理;并可引用 issue(Fixes #<issue number> / Closes #<pr number>)。
  • Revert 提交须以 revert: 开头,body 中包含 This reverts commit <SHA> 以及回退原因。

测试规范:Zoneless & Async-First

这是 AGENTS.md 中技术含量最高的一节,也是整个指南的核心。它规定 Agent 在编写 Angular 仓库测试时必须遵守 Zoneless & Async-First 原则:

假定运行环境是 zoneless 的,状态变化会以异步方式调度更新。

由此引出三条硬性规则:

  1. 禁止使用 fixture.detectChanges() 手动触发更新;
  2. 始终采用 "Act, Wait, Assert" 模式:
    • Act(操作):更新状态或执行一个动作;
    • Wait(等待)await fixture.whenStable(),让框架处理已调度的更新;
    • Assert(断言):验证输出;
  3. 尽量最小化等待以保持测试速度:
    • 使用 useAutoTick()(来自 packages/private/testing/src/utils.ts)通过 mock 时钟快速推进时间;
    • 必须等待时,使用真实异步测试(it('...', async () => { ... })),配合:
      • await timeout(ms)(同文件提供)等待指定毫秒数;
      • await fixture.whenStable() 等待框架稳定。

源码级解读:useAutoTick() 与 timeout() 如何实现

useAutoTicktimeout 均定义在 packages/private/testing/src/utils.ts,该目录是一个独立的 Bazel 测试库(BUILD.bazelng_project(name = "testing", testonly = True)),依赖 //packages/core/testing//packages/platform-browser 等,供各包测试以 @angular/private/testing 导入。

useAutoTick() 的实现本质是在 beforeEach 中安装 Jasmine 的假时钟并开启自动推进,afterEach 中卸载:

/**
 * Installs Jasmine's fake clock with auto-tick enabled for all tests in the describe block.
 * Call at the top level of a describe block to automatically advance time for async operations.
 */
export function useAutoTick() {
  beforeEach(() => {
    jasmine.clock().install();
    jasmine.clock().autoTick();
  });
  afterEach(() => {
    jasmine.clock().uninstall();
  });
}

timeout(ms) 则是一个简单的基于 setTimeout 的 Promise 封装,用于在真实异步测试中等待特定时长:

export async function timeout(ms?: number): Promise<void> {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

值得注意的是,同一文件中还提供了一批配套的测试工具,可以视为 AGENTS.md 所倡导模式的延伸:waitFor(callback, options) 以真实时钟(刻意绕过假时钟)轮询等待条件成立,默认超时 100ms,超时会附带重试次数与最后一次错误的详细信息;expectText(text, options) 等待页面文本出现,支持字符串或正则,默认容器为 TestBed.getLastFixture().nativeElementwithBody / withHead 负责在 Node 环境下通过 domino 安装 document 并管理测试间的清理(ensureDocument / cleanupDocument 已在模块加载时自动注册到 beforeEach/afterEach)。这意味着在 Node 环境中编写这些测试不需要额外搭建 DOM 基础设施。

仓库中的真实用例

useAutoTick + timeout 的组合在仓库各包的测试中被广泛使用,例如 packages/forms/signals/test/node/resource.spec.ts

import {isNode, timeout, useAutoTick} from '@angular/private/testing';

describe('resources', () => {
  useAutoTick();

  beforeEach(() => {
    globalThis['ngServerMode'] = isNode;
    TestBed.configureTestingModule({providers: [provideHttpClient(), provideHttpClientTesting()]});
    // ...
  });

  it('Takes a simple resource which reacts to data changes', async () => { ... });
});

其他典型使用者包括 packages/common/test/pipes/async_pipe_spec.tsAsyncPipe 测试)、packages/forms/test/form_control_spec.ts(各表单指令测试普遍同时引入 useAutoTicktimeout)、packages/common/http/test/transfer_cache_spec.ts 以及 packages/core/test/acceptance/authoring/signal_inputs_spec.ts。这些用例印证了 AGENTS.md 所述模式已落地为仓库测试代码的实际惯例:在 describe 块顶层调用 useAutoTick(),在异步 it 中配合 await timeout(ms)await fixture.whenStable() 完成 Act/Wait/Assert。

Pull Request 流程约定

AGENTS.md 的最后一节规定:使用 gh CLI(GitHub CLI)创建和管理 Pull Request。结合前文规范,一个完整的 Agent 工作流闭环为:

  1. pnpm install 安装依赖;
  2. coding-standards.md 的规范修改代码,测试遵循 Zoneless & Async-First 模式;
  3. pnpm ng-dev format changed 格式化改动文件,pnpm lint 校验风格;
  4. pnpm bazel test //target 运行受影响目标,提交前执行 pnpm test //packages/... 全量回归;
  5. commit-message-guidelines.md 编写提交信息(<type>(<scope>): <summary> + 动机 body + 可选 BREAKING CHANGE footer);
  6. 使用 gh CLI 创建 PR——PR 标题同样遵循提交信息 header 格式。

小结

AGENTS.md 虽篇幅不长,但把 Angular 仓库的协作契约压缩到了四个可执行要点:pnpm + pnpm bazel test 的环境基线、三份关键文档的权威分工(构建测试 / 编码风格 / 提交格式)、Zoneless & Async-First 的测试范式(禁用 detectChanges(),坚持 Act/Wait/Assert,用 useAutoTick()timeout() 控制等待),以及 gh CLI 的 PR 管理约定。配套的 packages/private/testing/src/utils.ts 源码与各包测试用例则提供了可直接对照的实现细节,使这份指南从"约定"落到可验证的工程实践。

登录后查看全文
热门项目推荐
相关项目推荐