# Frontend Testing

> 单前端闭环测试执行器（stack-agnostic / project-agnostic）。当一个前端 feature 收尾、需要补 superpowers/spec-kit 开发期覆盖不了的前端结构性测试缺口时使用：先读项目栈（package.json 或 等价清单）按栈实例化对应成熟工具，再针对实际命中的层 RED→GREEN 补测并固化为回归。与 backend-testing 的根本区别——后端缺口无现成工具需自建（"施工队"），前端成熟工具齐全，本 skill 不重新发明工具，只做 三件事：装 + 配 + 把项目的视觉/交互契约翻译成这些工具能跑的断言/规则（"装配工 + 监理"）。覆盖五层 前端结构性缺口——编译期 lint 门、L0/L1 单测（含 getComputedStyle 断言 token/深色/涨跌色）、a11y + 跨浏览器/响应式、前后端契约 mock、视觉回归。触发词：单前端测试 / 前端缺口补测 / frontend testing / 视觉回归 / a11y 测试 / 跨浏览器测试 / 响应式测试 / token 断言 / 前后端契约 mock / 前端回归补测。 常见地基为零：很多前端 [FE] 任务出参验证只写"手测"、测试运行器/RTL/Playwright 可能完全没装—— 本 skill 第一动作是"识栈→若无测试运行器先立地基"，不假设 L0/L1 已就绪。遵循 testing-system-blueprint 蓝本（风险分级 / 可追溯 / 发布门 / 三层节奏），受自愈护栏约束（只写测试不改产品码、断言不可弱化、禁伪造 修复、有界重试、产 PR 人审）。被 test-routing-advisor 在判定"单前端"时调用，也可直接触发。

- Skill: `fufankeji/frontend-testing` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add fufankeji/frontend-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fufankeji/frontend-testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: fufankeji (https://skillmd.com/u/fufankeji)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/fufankeji/frontend-testing

---


# frontend-testing · 单前端闭环测试执行器

## 这个 skill 解决什么

开发期 TDD（superpowers）和 spec/契约测试（spec-kit）覆盖的是"功能逻辑是否如规约工作"。
但前端有一类**结构性缺口**，它们不属于单个功能点、而属于界面在真实浏览器里的运行时性质——
视觉契约是否真的落到了像素（颜色/字号/间距是否走 token、深色是否覆盖、涨跌色是否符合目标市场惯例）、
可访问性是否达标、跨浏览器/多视口下布局是否不破、与后端的数据契约是否对得上、UI 改动有没有
悄悄造成视觉回归。这些缺口在开发期几乎不会被自然写到——尤其当 [FE] 任务的"出参验证"只写了
**"手测"**两个字时。

> **核心区别于 backend-testing（务必读懂）**：后端那一类缺口（真库越权/并发/韧性）**没有 pip/npm
> 即装即用的工具**，backend-testing 是"**施工队**"——大量测试代码要自己写。
> 前端**不是**这样：每一层都有成熟、广泛采用的工具（lint 器、单测运行器 + Testing Library、
> axe、Playwright、MSW、Chromatic…）。所以本 skill **不重新发明工具**，它是"**装配工 + 监理**"——
> 只做三件事：**装**（把该栈对应的成熟工具装进项目）+ **配**（接好配置/CI）+ **翻译**（把
> *这个项目的*视觉/交互契约，翻成这些工具能机械执行的断言与规则）。

### skill 自己加的价值：通用工具 ↔ 项目视觉契约 的翻译 + 编排 + 闭环

成熟工具是**通用**的，但它们**不懂你的项目**：

- stylelint 能禁裸色值，但**不知道你的 token 叫什么、值是多少**、哪个属性必须走 `var()`。
- axe 能扫 a11y，但**不知道哪些页面该跑**、红了**算不算**发布门拦截、对比度阈值按哪套设计走。
- Playwright 能截图、能开多视口，但**不知道你的"无横向滚动条"是硬约束、不知道你的涨跌色约定**
  （某些市场红涨绿跌、某些市场红跌绿涨——工具无从知晓哪种是"对"）。
- MSW 能 mock 后端，但**不知道你的后端契约长什么样**、字段改了它不会自己跟着改。

本 skill 干的，正是在**通用工具 ↔ 项目视觉/交互契约/design token** 之间做**翻译 + 编排 + 闭环**：
把"项目认可的成品长这样"翻成机器能反复跑的断言/规则，接进 CI，固化为回归。这是工具本身永远
不会替你做的部分，也是本 skill 的全部价值所在。

遵循 `testing-system-blueprint` 蓝本（按名引用即可）：风险分级排序、可追溯 ID、发布门、三层节奏。

---

## 两条绝对约束（先读，贯穿全程）

1. **stack-agnostic（栈无关）**：五层缺口一律用"**能力描述 + 按栈实例化 lookup**"表达，绝不把某一
   栈写死为唯一答案。本文与 `references/gaps.md` 里出现的 stylelint / Vitest / Playwright / MSW
   等，都是 **JS/TS 栈的示例实例**——它们是"能力在该栈的实例"，不是唯一解。**先读项目清单
   （`package.json` 或等价物），再实例化该栈对应的工具。**
2. **project-agnostic（项目无关）**：不出现任何业务名词，也不绑定任何具体框架（不假设 Next.js / React /
   某 UI 库）。所有项目专有的东西——**设计规范文档、design token 名、token 值、涨跌色约定、必跑页面**
   ——一律写成**条件式**："**若项目存在视觉规范文档 / design token，则**从中读取并据此生成断言"。
   用作示例不作前提：没有视觉规范文档的项目，token 类断言自然不命中，只跑结构性/几何/a11y 层。

---

## 工作流（六步闭环）

### 步骤 0 · 识栈

读项目清单文件，确定前端栈与运行时，后续所有工具选择都基于此实例化：

| 栈线索文件 | 前端栈 | 后续工具来源 |
|---|---|---|
| `package.json`（含 `tsconfig.json`） | JS / TS（含各类框架） | 见各层的 JS/TS 行（本 skill 主示例栈） |
| `pubspec.yaml` | Dart / Flutter | 用"能力"反查该栈等价物（widget test / golden test / 多设备） |
| `*.csproj` + Blazor | .NET 前端 | 反查该栈组件测试 + Playwright .NET |
| 其他 | 按需 | 用"能力"反查该栈生态等价物 |

> 找不到清单文件就停下来问，别假设。monorepo 则对被测前端目录就近识别。

### 步骤 1 · 立地基（前端特有，不可跳过）

**这是本 skill 与 backend-testing 最大的流程差异**：前端项目的测试地基**常常为零**——
很多 [FE] 任务的出参验证只写"手测"，项目里**可能根本没装测试运行器 / 组件测试库 /
浏览器驱动**。所以收尾补测**不能假设 L0/L1 已就绪**，必须先验地基、缺则先立：

1. 检查清单文件，确认是否已有：① 测试运行器；② 组件/DOM 测试库；③ 浏览器驱动（端到端/截图）。
2. **若无运行器**：先装地基（JS/TS 栈示例：Vitest + Testing Library + Playwright），接好最小配置
   与脚本，让"能跑一条最简单的测试并变绿"这件事先成立。**地基没立起来，后面任何层都无从谈起。**
3. 若已有部分地基，只补缺的那部分，不重装、不替换已选定的工具（surgical）。

> 因此本 skill 的覆盖下沿是**从立地基开始**，不是"假设单测已就绪、只补 L2~L6"。

### 步骤 2 · 条件命中（防过度测）

**不是五层全测**，只对该 feature 实际命中的子集补。逐层判断（详见 `references/gaps.md`）：

| 层 | 命中条件 | 不命中示例 |
|---|---|---|
| ① 编译期 lint 门 | 项目有视觉规范 / token，需禁裸色值、禁第二 UI 库、禁任意值 | 无视觉契约、无组件库约束 |
| ② L0/L1 单测 | feature 含组件 / 渲染逻辑 / 主题 / 涨跌色等需断言的视觉行为 | 纯静态文案、无逻辑透传 |
| ③ a11y + 跨浏览器/响应式 | 有交互组件、表单、多视口/多浏览器要求、几何约束（如无横向滚动） | 纯后台无 UI |
| ④ 前后端契约 mock | feature 消费后端接口，需隔离后端独立测前端 | 无外部数据、纯静态 |
| ⑤ 视觉回归 | 有需要"长得对"且会演进的关键页面/组件 | 一次性内部页、视觉无契约 |

纯静态 / 无视觉契约的 feature 很可能只命中 1–2 层——这是正常的，不要硬凑。

### 步骤 3 · 按栈实例化工具（装 + 配）

对每个**命中**的层，按步骤 0 识别的栈，从 `references/gaps.md` 取对应行**实例化成熟工具**：
装上、接好配置、纳入 CI。**不自己造工具**（这是与 backend-testing 的根本不同）。
对每层标注闭环性：**全自动**（lint / 单测 / a11y / 跨浏览器 / 契约 mock）vs **需人审裁决**（仅 L2 视觉基线）。

### 步骤 4 · lint 门 + RED→GREEN 补测

按"最便宜的层优先"顺序补：

1. **先上 lint 门（①，最便宜、不写测试）**：把项目视觉契约翻成 lint 规则（禁裸色值/只允许 `var()`、
   禁第二 UI 库 import、禁任意值 hex class）。编译期就拦，连测试都不用写——性价比最高，先做。
2. **L0/L1/a11y/响应式/契约（②③④，全自动 RED→GREEN）**：先写会失败的测试，确认它因真实缺口/未覆盖
   而红（不是测试写错而红）→ 让它转绿。**翻译动作在这里发生**：例如把"涨跌色 token 值"翻成
   `getComputedStyle` 断言、把"无横向滚动条"翻成 `scrollWidth <= clientWidth` 的几何不变量断言、
   把后端 OpenAPI 翻成 MSW handlers。
3. **视觉回归（⑤）的基线交人审**：截图 diff 由机器产出，但"**这个 diff 算不算回归**"是语义判断——
   **机器只能给 diff，不能裁决**。把基线裁决留给人（见下"人审节点"）。

> 具体工具与代码模式见 `references/gaps.md`，按步骤 0 识别的栈取对应行。

### 步骤 5 · 按蓝本归档 + 出 PR 人审

每个新增的 lint 规则 / 回归测试：

- 按 `testing-system-blueprint` 的**风险分级**排序（如越权式的视觉错误——涨跌色反了会误导决策——优先进发布门）。
- 挂**可追溯 ID**（关联 feature / AC / 命中的层）。
- 纳入**发布门**：明确哪些阻断发布（如涨跌色错、a11y 严重项、契约漂移），哪些告警级。
- 对齐**三层节奏**（lint/单测落快层；跨浏览器/视觉回归落较慢层）。
- 补测以 **PR 形式交付，由人审核合并**；PR 说明列出：命中层、跳过层及理由、每个新增回归对应的层与风险级别。

---

## 五层落地结构速览（详见 references/gaps.md）

逐层 = 一类缺口。所有工具列均为 **JS/TS 栈示例**，按栈实例化，不锁定。

| 层 | 成熟工具（JS/TS 示例，按栈实例化） | 闭环性 |
|---|---|---|
| ① 编译期 lint 门（最便宜，优先） | stylelint（禁裸色值/只允许 `var()`）、禁第二 UI 库 import、禁 Tailwind 任意值 hex class | **全自动，不写测试** |
| ② L0/L1 单测 | Vitest + Testing Library；含 `getComputedStyle` 断言 token 解析值 / 深色覆盖 / 涨跌色值 | 全自动 RED→GREEN |
| ③ a11y + 跨浏览器/响应式 | axe-core / @axe-core/playwright（自动检约 57% a11y，对比度占约 30%）；Playwright projects 多视口 + 几何不变量断言（无横向滚动 `scrollWidth<=innerWidth`） | 全自动 |
| ④ 前后端契约 mock | MSW + 从后端 OpenAPI 生成 handlers（消漂移），可选 Pact 双向契约 | 全自动进 CI |
| ⑤ 视觉回归 | Playwright 截图 / Chromatic，Docker 跑消平台 flake | 截图 diff 自动，**基线裁决人审** |

---

## 人审节点：全套只有一个 = L2 视觉基线裁决

前端这套补测里，**机器能完成的绝大部分都自动化了**——lint、单测、a11y、跨浏览器、响应式几何、
契约 mock 全是确定性判断。**真正需要人审的节点全套只有一个：L2 视觉回归的基线裁决。**

- **为什么只有这一个需要人**："这个截图 diff 算不算回归"是**语义判断**——可能是有意的设计改版
  （应更新基线），也可能是意外破坏（应拦截）。机器只能算出"有 N 个像素变了"，**不能裁决这变化是好是坏**。
- **怎么把人审量压到最小**：Docker 固定渲染环境消平台 flake（假阳性大头）+ 可选 AI 预过滤明显假阳性 +
  做成 PR check 集中呈现 diff。但**最终 approve 仍是人**——这是不可自动化的本质，不要假装能消掉它。

### 另一类：机器永远断言不了、只能人审（明确不塞进自动化）

除了"基线裁决"，还有一类视觉/语义项**根本无法写成机器断言**，必须单列、明确**不**塞进自动化套件，
留给人审（典型如代码评审 / 设计走查）：

- 设计样本/参考的**引用与还原度**（"做得像不像那张认可的成品图"）；
- **装饰性克制**（有没有多余的装饰、噪声）；
- **认知层级**（重要信息是否在视觉上被正确强调）；
- **整体质感/品味**（"看起来专不专业""有没有 AI 味"）。

强行给这些写断言只会产生脆弱、误导的测试。**明确把它们划在自动化范围之外**，是这套体系诚实的一部分。

---

## 自愈护栏（不可越过）

补测过程允许有界自动迭代（RED→GREEN），但受以下护栏约束（沿用 blueprint 的护栏）：

- **只写测试 / lint 规则，不改产品码**——这是默认动作。本 skill 是"装配工 + 监理"，职责是装工具、
  配规则、翻译断言，**不修业务实现**。
- **断言不可弱化**——禁止为了让测试转绿而放松断言（如把涨跌色断言注释掉、把对比度阈值调低、
  把"无横向滚动"几何断言删掉、给 a11y 违规加白名单豁免）。转绿必须靠正确产品码或正确测试，不靠降标准。
- **禁伪造修复**——不得用 `skip`、强行更新视觉基线掩盖真实回归、mock 掉被测渲染本身等手段伪装通过。
- **有界重试升级**——自动迭代有上限；连续失败达上限即停止并升级给人，不无限刷（视觉 flake 尤其要警惕被
  误当成"再跑一次就好"）。
- **产 PR 人审**——所有产物以 PR 交付，人工审核后合并。
- **发现真 bug 要 HALT，回交 superpowers TDD 修**——本 skill **不自行改产品代码**。补测过程中若 RED
  暴露的是产品真实缺陷（不只是缺覆盖），**停下来**，把缺陷回交 `superpowers:test-driven-development` /
  `superpowers:systematic-debugging` 走修复闭环，修完再回到本 skill 把回归固化。

护栏的目的：让闭环可以自动跑，但任何"看起来绿了其实没测到"或"越界改了产品码"的捷径都被堵死。

---

## 与上下游的关系

- 上游：`test-routing-advisor` 判定"单前端"时调用本 skill（也可被用户直接触发）。
- 蓝本：所有归档/分级/发布门/节奏遵循 `testing-system-blueprint`（按名引用，不在此复制其内容）。
- 兄弟：`backend-testing` 处理"单后端"；二者根本分工不同——后端**自建测试代码**（施工队），
  前端**装配成熟工具 + 翻译视觉契约**（装配工 + 监理）。
- 方法论复用：发现真缺陷的修复与调试复用 `superpowers:test-driven-development` 和
  `superpowers:systematic-debugging`（本 skill HALT 后回交它们）。

