Agent Skills › sanity-io/sanity › sanity-visual-coverage

sanity-visual-coverage

GitHub

检查 UI 文件是否被 Chromatic 视觉回归测试覆盖,支持 PR 变更或全仓库扫描。通过脚本分析 Story 和浏览器测试依赖关系,判断覆盖状态,辅助 PR 审查及迁移工作规划。

.agents/skills/sanity-visual-coverage/SKILL.md sanity-io/sanity

Trigger Scenarios

审查涉及 UI 组件的 PR 询问文件是否被 Chromatic 覆盖 规划 UI 迁移工作 准备添加 Story 的 PR

Install

npx skills add sanity-io/sanity --skill sanity-visual-coverage -g -y
More Options

Non-standard path

npx skills add https://github.com/sanity-io/sanity/tree/main/.agents/skills/sanity-visual-coverage -g -y

Use without installing

npx skills use sanity-io/sanity@sanity-visual-coverage

指定 Agent (Claude Code)

npx skills add sanity-io/sanity --skill sanity-visual-coverage -a claude-code -g -y

安装 repo 全部 skill

npx skills add sanity-io/sanity --all -g -y

预览 repo 内 skill

npx skills add sanity-io/sanity --list

SKILL.md

Frontmatter
{
    "name": "sanity-visual-coverage",
    "description": "Check whether Studio UI is covered by Chromatic visual regression, for a PR's changed files or for the whole tree, and decide between \"already covered\", \"a story is pending in an open PR\", and \"needs a story\". Use when reviewing a PR that touches packages\/**\/src\/**\/*.tsx or *.css.ts, when asked \"is this covered by Chromatic\", when planning ui5 or vanilla-extract migration work, or before opening a PR that adds stories."
}

Visual regression coverage

The question this skill answers is "if this file's rendering changes, does a Chromatic snapshot catch it?". The answer is a static fact about the repo, so a script computes it. Do not answer it by reading the Chromatic check, and do not answer it from memory.

Run the check

pnpm visual-coverage --changed                # files changed vs origin/main, including uncommitted
pnpm visual-coverage --changed --prs          # also mark files that an open PR is about to cover
pnpm visual-coverage <path> [<path>...]       # specific files
pnpm visual-coverage                          # whole tree, one row per area
pnpm visual-coverage --uncovered              # whole tree plus every uncovered file
pnpm visual-coverage --format json ...        # machine-readable, same modes
pnpm visual-coverage --help

For a PR you are not on: gh pr checkout <number> && pnpm visual-coverage --changed --prs. Every same-repo PR that touches packages/**/src/**/*.tsx or *.css.ts also gets a sticky "Visual regression coverage" comment from .github/workflows/visual-coverage.yml, produced by the same script with --format markdown --prs. Read that comment first when reviewing; its counts are in plain sight and the per-file table sits behind the collapsed Per-file coverage toggle.

The script is scripts/visualCoverage.ts. It has no dependencies and runs under tsx or plain Node 22.18+ (node scripts/visualCoverage.ts).

What counts as covered

Chromatic snapshots stories, not components. A component is covered when a story renders it. The script models that as direct imports:

Evidence Files Chromatic project Status in the report
story packages/**/src/**/*.stories.tsx "sanity studio" covered
browser-test packages/**/src/**/*.browser.test.tsx "sanity studio vitest" covered
pending a *.stories.tsx added by an open PR (--prs) none yet pending, claimed by that PR

Both projects snapshot on every PR: a story is captured by the Storybook build, a browser test's end state by the CHROMATIC=1 capture run (plus any takeSnapshot() it calls). The Playwright project ("sanity studio playwright") is curated opt-in and is not modelled as coverage.

A file is covered when a story or browser test imports it directly, or imports a *Story.tsx harness that imports it. A .css.ts file inherits the coverage of the .tsx files that import it. Nothing deeper than that counts. TestWrapper imports the whole sanity package, so a transitive graph would mark everything covered, and Chromatic's TurboSnap has the same blind spot. That is why the green Chromatic check on a PR says only "the affected stories still match", never "your component is rendered by a story".

How stories map to Studio UI

  • dev/storybook is the host. Its stories glob in dev/storybook/.storybook/main.ts finds *.stories.tsx under every workspace package's src. Stories live next to the component in its __tests__ directory. Nothing lives under dev/storybook/stories.
  • Two story shapes. Plain variant grids for packages/sanity/src/ui-components wrappers (Button.stories.tsx imports ../Button). Harness stories for anything that needs a workspace, i18n, or layers. The harness is <Name>Story.tsx, wraps TestWrapper, and <Name>.stories.tsx is a thin CSF file whose component is the harness.
  • Browser tests define their harness component inline (function <Name>Harness() inside the <Name>.browser.test.tsx), so every *Story.tsx belongs to a story. The Vitest Chromatic integration snapshots the test's end state in place; do not extract a test's harness into a *Story.tsx to put a story on it. Their coverage shows up as browser-test evidence.
  • "ui5 sentinel" and "box sentinel" are the same thing. A story added so the @sanity/ui to ui5 Box/Flex/Card migration gets a snapshot before the swap lands. The harness renders the states most likely to drift (tones, spacing, truncation, empty states) with fixture copy only. Naming follows the harness pattern above. title is Area/Component. Sentinels are ordinary stories — browsable, with a JSDoc description saying what they pin down; there is no tag to hide them. Read FieldDiffChromeStory.tsx and FieldDiffChrome.stories.tsx under packages/sanity/src/core/field/diff/components/__tests__ as the reference pair.
  • A story covers exactly the components its harness imports. A DocumentLayout story also paints buttons and cards, but only the Button story is the sentinel for Button.

Decision procedure

Run pnpm visual-coverage --changed --prs on the branch, then per file:

  1. covered. Done. If the change adds a state the story does not render (a new tone, an empty state, a truncation case), extend the existing story or harness. Do not add a second story for the same component. When the only evidence is a browser-test, the state is snapshotted by the Vitest integration; extend that test or its harness rather than adding a story for it.
  2. pending. Do not add a story. The PR number is in the report. Review that PR, or comment on it if the variant you need is missing.
  3. uncovered, and the file paints something (layout, tone, spacing, text). Add coverage per .agents/skills/sanity-visual-regression/SKILL.md, picking the source with its "Which source owns a state" table: a story when the state is reachable from props or one play step, a *.browser.test.tsx when reaching it means driving the UI. For a story, reuse an existing *Story.tsx harness in the same directory before creating one; never build the story out of a browser test's harness.
  4. uncovered, and the file is a provider, hook wrapper, context, or renders only children. Nothing to snapshot. Say so in the PR instead of adding a story.

For migration planning, pnpm visual-coverage --uncovered --prs lists the gap. Pick from it, do not survey by hand.

Avoiding duplicate coverage PRs

Sentinel coverage for the ui5 migration lands in a stream of PRs titled test(storybook): add ui5 ... sentinels ... (the original stack, #14056 through #14511, has merged; newer ones are usually stacked the same way). The migration itself lands on chore/ui-v5-* branches.

  • --prs already accounts for every open PR that adds a *.stories.tsx. A file reported as pending is claimed.
  • To see what is open: gh pr list --state open --search "test(storybook) in:title" --json number,title,headRefName,baseRefName.
  • Do not rebase, rewrite, or push to those branches. A new coverage PR goes on top of an open PR when it depends on a harness added there, or off main when its files are disjoint from every open PR.
  • Before opening a coverage PR, run the check with --prs one more time. If anything you added is now pending elsewhere, drop it.

Version History

  • 584242a Current 2026-09-23 01:00

Same Skill Collection

.agents/skills/before-and-after/SKILL.md
.agents/skills/code-review-and-quality/SKILL.md
.agents/skills/code-simplification/SKILL.md
.agents/skills/deslop/SKILL.md
.agents/skills/grill-me/SKILL.md
.agents/skills/improve-codebase-architecture/SKILL.md
.agents/skills/migrate-styled-components-to-vanilla-extract/SKILL.md
.agents/skills/performance-optimization/SKILL.md
.agents/skills/playwright-cli/SKILL.md
.agents/skills/pr-description/SKILL.md
.agents/skills/react-devtools/SKILL.md
.agents/skills/react-rx-hook-audit/SKILL.md
.agents/skills/rxjs-like-a-pro/SKILL.md
.agents/skills/sanity-config-reducers/SKILL.md
.agents/skills/sanity-default-plugins/SKILL.md
.agents/skills/sanity-i18n-translate/SKILL.md
.agents/skills/sanity-plugin-authoring/SKILL.md
.agents/skills/sanity-radar/SKILL.md
.agents/skills/sanity-ui-migration-progress/SKILL.md
.agents/skills/sanity-visual-regression/SKILL.md
.agents/skills/stories/SKILL.md
.agents/skills/storybook-init/SKILL.md
.agents/skills/storybook-setup/SKILL.md
.agents/skills/storybook-upgrade/SKILL.md
.agents/skills/tdd/SKILL.md
.agents/skills/vercel-react-best-practices/SKILL.md
.agents/skills/write-a-skill/SKILL.md
.agents/skills/find-skills/SKILL.md
.agents/skills/playwright-best-practices/SKILL.md
.agents/skills/sanity-bench/SKILL.md
.agents/skills/sanity-radar-investigate/SKILL.md
.agents/skills/sanity-tsdown-config/SKILL.md

Metadata

Files
0
Version
485ae5c
Hash
5a0a2dd8
Indexed
2026-09-23 01:00

ホーム - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-30 06:13
浙ICP备14020137号-1