Skip to content

test(design-system): setup Storybook docs tests [AR-74627]#587

Merged
iromanchuk-dn merged 16 commits into
drivenets:mainfrom
iromanchuk-dn:AR-74627-dap-design-system-ai-toolkit-improve-code-quality-in-storybook-files
Jul 21, 2026
Merged

test(design-system): setup Storybook docs tests [AR-74627]#587
iromanchuk-dn merged 16 commits into
drivenets:mainfrom
iromanchuk-dn:AR-74627-dap-design-system-ai-toolkit-improve-code-quality-in-storybook-files

Conversation

@iromanchuk-dn

Copy link
Copy Markdown
Collaborator

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 start were unreliable.

This PR adds automated docs snippet tests (*.docs.test.ts) as a dedicated Vitest storybook-docs project. Global setup builds and serves storybook-static on an ephemeral port (same artifact as GitHub Pages), then tests verify both outputs per story:

  • Show code — Playwright opens Autodocs, expands Show code, snapshots the panel text
  • MCP manifest — fetches components.json and snapshots the story snippet

Shared helpers live under tests/storybook/. test:storybook-docs runs the project explicitly; default pnpm test excludes it (like requires-build). CI builds Storybook and runs docs tests on PRs. Initial coverage: ds-breadcrumb and ds-button-v3. Agent skills and workflows are updated to point authors at the new pattern.

@netlify

netlify Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploy Preview for drivenets-design-system ready!

Name Link
🔨 Latest commit 57007fd
🔍 Latest deploy log https://app.netlify.com/projects/drivenets-design-system/deploys/6a5f8793f0d62600086a780b
😎 Deploy Preview https://deploy-preview-587--drivenets-design-system.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added ci tooling design-system cursor PR that changes cursor rules, skills, etc. labels Jul 9, 2026
@iromanchuk-dn iromanchuk-dn changed the title test(design-system): setup docs tests [AR-74627] test(design-system): setup Storybook docs tests [AR-74627] Jul 9, 2026
mmurawski-dn
mmurawski-dn previously approved these changes Jul 10, 2026

@mmurawski-dn mmurawski-dn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one

vpolessky-dn
vpolessky-dn previously approved these changes Jul 10, 2026

@StyleShit StyleShit left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed mostly the "infra" part, but I don't really understand the tests themselves

Comment thread .github/workflows/pr.yml Outdated
Comment thread .github/workflows/pr.yml Outdated
Comment thread .github/workflows/pr.yml Outdated
Comment thread packages/design-system/package.json Outdated
@iromanchuk-dn
iromanchuk-dn dismissed stale reviews from mmurawski-dn and vpolessky-dn via 00ad22c July 16, 2026 15:27
@iromanchuk-dn
iromanchuk-dn requested a review from StyleShit July 16, 2026 15:49
Comment thread cspell/local-words.txt Outdated
autoboot
autodocs
borderless
buttonv

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
buttonv

seems like it's not needed anymore

Comment thread packages/design-system/.storybook/preview.ts
const dirname = path.dirname(fileURLToPath(import.meta.url));
const packageRoot = path.resolve(dirname, '../../');

const COMPONENTS = ['ds-button-v3', 'ds-breadcrumb'];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do this:

Suggested change
const COMPONENTS = ['ds-button-v3', 'ds-breadcrumb'];
const COMPONENTS = ['button-v3', 'breadcrumb'];

And then: (see next comment)

Comment on lines +47 to +57
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;
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)

Comment on lines +60 to +68
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];
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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', () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Comment on lines +41 to +47
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();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this polling mechanism?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

StyleShit
StyleShit previously approved these changes Jul 21, 2026

@StyleShit StyleShit left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

@iromanchuk-dn
iromanchuk-dn merged commit b8b8466 into drivenets:main Jul 21, 2026
26 checks passed
iromanchuk-dn added a commit that referenced this pull request Jul 22, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants