test(design-system): setup Storybook docs tests [AR-74627]#587
Conversation
✅ Deploy Preview for drivenets-design-system ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
StyleShit
left a comment
There was a problem hiding this comment.
I reviewed mostly the "infra" part, but I don't really understand the tests themselves
…e-code-quality-in-storybook-files
00ad22c
…e-code-quality-in-storybook-files
… for every listed component
| autoboot | ||
| autodocs | ||
| borderless | ||
| buttonv |
There was a problem hiding this comment.
| buttonv |
seems like it's not needed anymore
| const dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
| const packageRoot = path.resolve(dirname, '../../'); | ||
|
|
||
| const COMPONENTS = ['ds-button-v3', 'ds-breadcrumb']; |
There was a problem hiding this comment.
You can do this:
| const COMPONENTS = ['ds-button-v3', 'ds-breadcrumb']; | |
| const COMPONENTS = ['button-v3', 'breadcrumb']; |
And then: (see next comment)
| const allComponents = Object.values(manifest.components); | ||
|
|
||
| return allowlist.map((folder) => { | ||
| const component = allComponents.find((entry) => entry.path.includes(`/${folder}/`)); | ||
|
|
||
| if (!component) { | ||
| throw new Error(`No manifest component found for folder "${folder}"`); | ||
| } | ||
|
|
||
| return component; | ||
| }); |
There was a problem hiding this comment.
| const allComponents = Object.values(manifest.components); | |
| return allowlist.map((folder) => { | |
| const component = allComponents.find((entry) => entry.path.includes(`/${folder}/`)); | |
| if (!component) { | |
| throw new Error(`No manifest component found for folder "${folder}"`); | |
| } | |
| return component; | |
| }); | |
| return allowlist.map((componentName) => { | |
| const manifest = manifest.components[`components-${componentName}`]; | |
| if (!manifest) { | |
| throw new Error(`No manifest component found for folder "${folder}"`); | |
| } | |
| return component; | |
| }); |
(just to be slightly more performant)
| function getComponentFolder(componentPath: string): string { | ||
| const match = /components\/([^/]+)\//.exec(componentPath); | ||
|
|
||
| if (!match?.[1]) { | ||
| throw new Error(`Could not derive component folder from path "${componentPath}"`); | ||
| } | ||
|
|
||
| return match[1]; | ||
| } |
There was a problem hiding this comment.
I think I would drop this and just build the path manually, based on the new kebab-case COMPONENTS array we now have:
function getComponentDirectoryName(componentName: string) {
return `ds-${componentName}`;
}| const manifest = await fetchComponentsManifest(); | ||
| const components = resolveAllowlistedComponents(manifest, COMPONENTS); | ||
|
|
||
| describe.sequential('docs snippets', () => { |
There was a problem hiding this comment.
why does it need to be sequential? it'll be slower, no?
can we have a dedicated browser tab for each test instead?
| docsStoryId: `${component.id}--docs`, | ||
| storyName: story.name, | ||
| }); | ||
| const manifestSnippet = await readManifestSnippet(component.id, story.name); |
There was a problem hiding this comment.
calling readManifestSnippet here will re-fetch and re-validate things that were already fetched & validated through the test flow (lines 101-102)
Can we just fetch the manifest once before the tests and have it cached at the module level or something?
There is no need to re-fetch it for every component, and currently we do it twice per component
personally, I would probably have a components-manifest.ts file that handles all manifest-related stuff in a cached way (getManifest(componentId), getStorySnippet(componentId, storyId), etc.)
| const deadline = Date.now() + 10_000; | ||
| let text = (await source.innerText()).trim(); | ||
|
|
||
| while (!text && Date.now() < deadline) { | ||
| await page.waitForTimeout(100); | ||
| text = (await source.innerText()).trim(); | ||
| } |
There was a problem hiding this comment.
why do we need this polling mechanism?
There was a problem hiding this comment.
maybe let's use vercel's serve-handler package to make things simpler on our end (mime types, URL rewrites, error handling, etc.)?
see serve's github for api usage with node:http
…e-code-quality-in-storybook-files
…e-code-quality-in-storybook-files
…7] (#614) ## Improved components | Component | Stories | Docs snapshot | New browser test | Cleanup (SCSS/impl) | |---|---|---|---|---| | `ds-alert-banner` | ✓ | ✓ | | | | `ds-autocomplete` | ✓ | ✓ | | | | `ds-avatar` | | ✓ | | | | `ds-avatar-group` | ✓ | ✓ | ✓ | ✓ (scss, tsx, types) | | `ds-card` | ✓ | ✓ | ✓ | ✓ (scss, tsx) | | `ds-catalog-layout` | ✓ | ✓ | | | | `ds-checkbox` | ✓ | ✓ | | ✓ (scss, tsx) | | `ds-date-picker` | ✓ | ✓ | | ✓ (scss, tsx, types) | | `ds-date-range-picker` | ✓ | ✓ | | ✓ (scss) | | `ds-dialog` | ✓ | ✓ | ✓ | ✓ (scss, tsx) | Notes: - All 10 had their `*.stories.tsx` cleaned up for code quality and were opted into the docs-snippet tests (each got a colocated `*.docs.snap` golden file). - `ds-avatar` only shows a snapshot file because its stories were already clean — just newly covered by the docs tests. - Three (`ds-avatar-group`, `ds-card`, `ds-dialog`) also gained new `*.browser.test.tsx` coverage. - `ds-date-range-picker` is the one whose snapshot I regenerated in the CI fix (timezone determinism). For reference, `ds-breadcrumb` and `ds-button-v3` are also in the docs-test allowlist but were the reference components set up in the earlier docs-tests PR (#587), not part of this branch's component improvements.
Summary
Storybook Autodocs Show code snippets and MCP manifest snippets are what agents and developers copy from docs, but nothing in CI caught when those outputs drifted after story edits. Dev Storybook also differs from the production build (e.g. React Compiler), so manual checks against
pnpm startwere unreliable.This PR adds automated docs snippet tests (
*.docs.test.ts) as a dedicated Viteststorybook-docsproject. Global setup builds and servesstorybook-staticon an ephemeral port (same artifact as GitHub Pages), then tests verify both outputs per story:components.jsonand snapshots the storysnippetShared helpers live under
tests/storybook/.test:storybook-docsruns the project explicitly; defaultpnpm testexcludes it (likerequires-build). CI builds Storybook and runs docs tests on PRs. Initial coverage:ds-breadcrumbandds-button-v3. Agent skills and workflows are updated to point authors at the new pattern.