ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Sanity Studio 视觉回归测试的 Storybook 基座:从 Chromatic 快照到 Vercel 部署

Sanity Studio 视觉回归测试的 Storybook 基座:从 Chromatic 快照到 Vercel 部署 Sanity Studio 视觉回归测试的 Storybook 基座从 Chromatic 快照到 Vercel 部署【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity本篇技术指南聚焦 Sanity Studio 官方 monorepo 中的sanity-storybook包dev/storybook讲解它如何作为整个仓库的视觉回归测试基座通过 Storybook 承载组件故事、交给 Chromatic 在每次 PR 上自动做像素级差异对比并部署到 Vercel 提供稳定的浏览地址。读完本文你将掌握这个 monorepo 中 Storybook 的命令体系、story 组织约定、浏览器级测试与故事之间的职责划分、Chromatic 工作流配置以及 Vercel 部署的完整初始化步骤。为什么 Sanity Studio 需要一套 StorybookSanity Studio 是一个大型 pnpm monorepopnpm-workspace.yaml其 UI 层正在经历两场大规模的样式体系迁移styled-components → vanilla-extract把运行时 CSS-in-JS 替换为编译期提取的样式方案sanity/ui→ui5升级底层 UI 组件库面。这类迁移最危险的地方在于逻辑可能完全正确但一个像素的间距、颜色或圆角变化就会破坏整个 Studio 的视觉一致性。因此 dev/storybook/README.md 明确写道sanity-storybook的核心使命就是用自动化的视觉 diff 守护这两场迁移——它被 Chromatic 在每次 PR 上截图快照并部署到 Vercel 供团队审阅。整个视觉回归工作流包括如何新增覆盖记录在 .agents/skills/sanity-visual-regression/SKILL.md 中本文则聚焦于该包本身的工程设施。命令速查本地开发、构建与手动发布dev/storybook/package.json 定义了四个脚本分别对应四类使用场景# 从仓库根目录执行 pnpm dev:storybook # Storybook 开发服务器地址 http://localhost:6006 pnpm build:storybook # 通过 turbo 做静态构建产物输出到 dev/storybook/storybook-static # 从 dev/storybook 目录执行 pnpm test # 用 vitest 浏览器模式运行每一个 story基于 storybook/addon-vitest pnpm chromatic # 手动发布并截图快照需要环境变量 CHROMATIC_PROJECT_TOKEN细节解读dev脚本对应storybook dev --port 6006 --no-open固定端口 6006且启动时不自动打开浏览器build对应storybook build产物目录为storybook-statictest对应vitest run其行为由 dev/storybook/vitest.config.mts 定义下文详解chromatic对应 Chromatic CLI可用于手动触发一次快照发布。从根目录运行pnpm dev:storybook与pnpm build:storybook依赖的是根 package.json 中的对应 turbo 任务pnpm build:storybook会经由 dev/storybook/turbo.json 声明dependsOn: [^build]保证上游 workspace 包先构建完成再产出storybook-static/**。story 的组织约定包拥有、就近放置dev/storybook本身不存放任何 CSF 故事文件它只负责 Storybook、Chromatic 和 addon-vitest 的基础设施。故事的归属原则是Stories are package-owned and co-located.每个 story 属于它覆盖的那个 workspace 包。具体约定依据 dev/storybook/README.md 与 .agents/skills/sanity-visual-regression/SKILL.mdStorybook 自动发现workspace 包src目录下的*.stories.tsx文件story 与组件就近放置通常把*.stories.tsx放在组件或 harness 所在的同一个__tests__目录里。真实示例见 packages/sanity/src/ui-components/button/tests/Button.stories.tsx 与 packages/sanity/src/ui-components/dialog/tests/Dialog.stories.tsx使用包内局部导入不要跨 workspace 边界引用远端模块不要在dev/storybook/stories/下新增 CSF 文件。浏览器测试不重复导出为 story这是一个容易踩坑的关键分工vitest 浏览器模式套件*.browser.test.tsx本身就是独立的 Chromatic 快照来源。chromatic-com/vitest会在测试结束时自动归档其最终状态对应 Chromatic 工作流中的vitest-visualjob。因此每个浏览器测试内联保留自己的 harness 组件function FooHarness()写在测试文件里不再为它单独写 story仓库中每一个*Story.tsx都是被某个*.stories.tsx引用的 Storybook harnessstory 只覆盖浏览器测试没有渲染到的状态禁止把浏览器测试重新导出成 story也禁止为覆盖某个已由测试快照的状态而再写一个重复的 story——那会得到同一批像素的重复快照。Playwright 留在 e2e/不在 Storybook 里e2e 套件有自己独立的 Chromatic 项目e2e/studio-visual-test.ts由.github/workflows/e2e.yml通过chromaui/action上传。因此sanity-storybook的playwright依赖仅仅是storybook/addon-vitest渲染 story 所用的浏览器 runner该包内不承载任何 spec、fixture 或chromatic-com/playwright接线。不要为了拿快照而把 e2e spec 改写成 story或反过来。迁移哨兵故事migration sentinels针对前面提到的两场样式迁移组件本地的 story 专门覆盖测试无法捕获的视觉状态ui-components包装组件变体即sanity/ui→ui5的迁移表面优先覆盖 card 与 tone 相关组件tone 会级联影响所有组件已完成 vanilla-extract 迁移的组件如 change indicators、DocumentLayout作为迁移哨兵。需要 Studio 上下文workspace/i18n/layers的状态则复用浏览器测试同款 mock studio 包装器TestWrapper表单输入再加TestForm它们来自 packages/sanity/test/browser确定性、无网络通过 story-only 的*Story.tsxharness 接入。交互后才会出现的浮层tooltip、菜单等则用play函数配合storybook/test的userEventwaitFor/expect(...).toBeVisible()驱动查询within(document.body)访问 portal 内容——Chromatic 和 addon-vitest 都会先跑play再截图因此快照中能看到打开的浮层。每个 story 都可浏览所有故事都面向被阅读而编写构成组件用法的活文档不要用tags把 story 从侧边栏!dev或文档!autodocs隐藏起来。曾经用来隐藏 vitest 派生故事的!dev/!autodocs/vrt-only标签已随那些故事一起消失——浏览器测试改为就地快照后这里只保留给人看的故事。如果一个状态不值得人看就不该进 Storybook而应放进浏览器测试。只想保留可浏览性、不希望被快照的 story设置parameters: {chromatic: {disableSnapshot: true}}即可。构建配置让 story 渲染得和真实 Studio 一模一样dev/storybook/README.md 特别强调.storybook/main.ts的 Vite 配置镜像了 packages/sanity/vitest.browser.config.mts具体包含三部分monorepoexports 条件把 workspace 包解析到 TypeScript 源码而不是已构建产物vanilla-extract 插件保证迁移后的组件样式正确编译React Compiler transform与 Studio 运行时保持一致。这样 story 渲染效果与真实 Studio、与浏览器测试完全一致快照才有意义。而 dev/storybook/vitest.config.mts 则补充了浏览器测试侧的细节通过storybook/addon-vitest/vitest-plugin的storybookTest()注册configDir指向.storybookstorybookScript: pnpm dev用于 vitest watch 模式把测试失败关联回 story UI因为 story harness 会启动完整的 Studio 表单构建器testTimeout放宽到 30 秒retry: 1expect.poll超时 10 秒浏览器用vitest/browser-playwrightheadless chromium视口 1280×900——这与 .agents/skills/sanity-visual-regression/SKILL.md 中提到的.storybook/preview.tsx全局桌面模式视口一致保证 story 与测试的截图尺寸统一。注意该工程刻意没有注册进根 vitest.config.mts 的多项目运行——因为它需要真实浏览器只能在pnpm --filter sanity-storybook test下单独运行。确定性规则视觉回归最怕随机性。skill 文档明确规定harness story 靠 mock client/workspace 天然确定、无网络绝不渲染实时时间戳、随机 id 或未完成的加载态Chromatic 会自动暂停 CSS 动画。需要微调时使用parameters.chromatic旋钮delay截图前等待毫秒数Portable Text 故事用 300ms 等待编辑器启动、diffThreshold、disableSnapshot、modes视口/主题矩阵。Chromatic 集成每次 PR 的自动快照dev/storybook/chromatic.config.json 只有两个关键字段{ $schema: https://www.chromatic.com/config-file.schema.json, buildScriptName: build, onlyChanged: true }buildScriptName告诉 Chromatic 用build脚本构建 StorybookonlyChanged: true即启用TurboSnap——只对本次变更影响的 story 截图大幅节省快照预算。工作流storybook 与 vitest-visual 双 job.github/workflows/chromatic.yml 在pull_request和push到main时触发包含两个 jobstorybookStorybook visual testsactions/checkoutv7使用fetch-depth: 0拉取完整 git 历史这是 Chromatic 找基线构建、追溯 TurboSnap 变更文件的前提checkout 用 PR 分支而非 GitHub 的合并提交保证 Chromatic 能定位它对比的 commits先用detect-code-changes判断是否有变更有变更才执行pnpm build构建全部包再运行chromaui/actiontoken 用CHROMATIC_PROJECT_TOKEN_STORYBOOKworkingDir: dev/storybookexitZeroOnChanges: true烧录期burn-in内检查不阻断合并差异只作为报告autoAcceptChanges: main合并到main时自动接受新基线。vitest-visualVitest browser visual tests同样拉全量历史额外做 Playwright 浏览器版本探测、缓存与安装playwright install-deps chromium/playwright install chromium设置CHROMATIC1与SANITY_VITEST_BROWSERchromium后运行pnpm --filter sanity test:browser把每个*.browser.test.tsx的结束态归档校验packages/sanity/.vitest/chromatic/preview-stats.json存在TurboSnap 需要它再经chromaui/action以vitest: true模式上传到独立的 sanity studio vitest 项目tokenCHROMATIC_PROJECT_TOKEN_VITEST。两个快照来源、两个 Chromatic 项目、各自独立的 token——skill 文档强调一个集成类型对应一个项目不要把某一来源的输出上传到另一个项目的 job。此外还有一个受控的第三来源Playwright e2e 的takeSnapshot()上传到 sanity studio playwright 项目tokenCHROMATIC_PROJECT_TOKEN_E2E由于 e2e 跑在每 PR 的 staging 数据集上实时时间戳、presence、并发写入全局自动快照只会产生纯 diff 噪声因此 e2e/studio-test.ts 全局禁用自动快照仅在确定性时刻按 spec 显式takeSnapshot()接入。浏览器测试内的快照控制在*.browser.test.tsx内部chromatic-com/vitest提供三种粒度自动快照每个测试结束自动归档Chromatic 中的命名是describe 链 / it 标题 / Snapshot #n按作用域退出configure({disableAutoSnapshot: true})在文件顶层调用作用于整个文件在describe()内作用于该套件在test()内仅作用于该测试——适合纯交互检查或仅清理的状态定向快照await takeSnapshot(状态名)抓取测试中途经过但不结束于的状态如关闭前的菜单、拖拽中途必须await未 await 的调用会导致测试失败。两个 helper 在普通运行中都是 no-opfirefox/webkit 上亦然只有CHROMATIC1才真正开启捕获普通运行只会在被 gitignore 的.vitest/chromatic下留下显式takeSnapshot()的归档。Vercel 部署稳定的浏览地址与 PR 预览Storybook 部署到sanity-sandboxVercel 团队项目名studio-storybook生产地址为https://studio-storybook.sanity.dev并通过 Git 集成自动为每个 PR 生成预览部署。一次性项目初始化维护者从仓库根目录执行vercel是根 devDependency初始化分四步依据 dev/storybook/README.md# 1. 认证一次性 pnpm vercel login # 2. 创建项目 设置 Root Directory 首次预览部署交互式 # - Set up and deploy? 选 yes # - Scope: sanity-sandbox # - Project name: studio-storybook # - Code directory? - 填 ./dev/storybook # - Vercel 会把框架误判为 Vite没关系vercel.json 已钉死 # buildCommand/outputDirectory 并覆盖这一误判 # - 按提示连接检测到的 Git 仓库origin # - monorepo 超出 Vercel 1.5 万文件的上传上限因此必须加 --archivetgz pnpm vercel --scope sanity-sandbox --archivetgz # 3. 验证一次生产部署 pnpm vercel --prod --scope sanity-sandbox --archivetgz # 4. 把生产域名指向项目 pnpm vercel domains add studio-storybook.sanity.dev studio-storybook --scope sanity-sandbox几个关键注意点首次部署的交互式提示正是把 Root Directorydev/storybook持久化到项目上的时机setup 阶段连接 Git 仓库即可自动获得 PR 预览与main生产部署无需再单独执行vercel git connectdev/storybook/vercel.json 钉死了构建方式cd ../.. pnpm exec turbo run build --filtersanity-storybook输出目录storybook-static并加了 SPA 路由回退/(.*)→/index.html。这样上游 workspace 包会先被构建Vercel 误判的框架预设也就无关紧要了Git 集成构建会从根目录克隆 monorepo 并安装整个 pnpm workspace保证workspace:*与catalog:协议可解析行为与test-studio-preview-iframe项目一致在该包合入main之前推送到main触发的生产部署会因缺 Root Directory 而失败——这是 PR 合并前可预期的噪声.vercel/链接元数据与其他 dev 应用一样被 gitignore可选仅仪表盘设置把项目的 Ignored Build Step 设为npx turbo-ignore sanity-storybook让不影响 Storybook 的提交跳过部署Chromatic 会给每个发布的 Storybook 构建生成永久链接因此 Vercel 部署承担的是稳定 URL PR 预览的职责两者互补。小结一整套人机分流的视觉回归分工回看整个设计sanity-storybook的价值不在于多一个 Storybook而在于它用清晰的职责划分把三类快照来源组织成互补体系状态来源归属快照项目仅凭 props/fixtures 或一次play交互可达*.stories.tsx包内就近放置sanity studio需要驱动 UI输入、拖拽、剪贴板、视口变化*.browser.test.tsx结束态就地快照sanity studio vitest完整 Studio 外观 真实部署与数据集Playwright spece2e/studio-visual-test.ts只读状态sanity studio playwrightdev/storybook只做基建stories 全部由各 workspace 包包拥有并就近存放浏览器测试就地快照Playwright 留在 e2e/而 Storybook 只保留给人看的组件状态文档。配合 Chromatic 的 TurboSnap 与双项目工作流Sanity Studio 得以在 styled-components → vanilla-extract 和sanity/ui→ ui5 两场迁移中持续获得像素级的回归保护。【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表