# Storybook Visual Testing

> Make Storybook stories deterministic for UI Verify so captures stop coming back "changed" without a real change (flaky diffs). Use when setting up story-level visual tests or debugging a story that diffs every run. Focuses only on the run-to-run variation the capturer can't neutralize from outside your app — the clock, infinite JS animations, live data, and non-Math.random randomness — and delibe…

- **Type:** Skill
- **Install:** `agentstack add skill-uiverify-uiverify-storybook-visual-testing`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [uiverify](https://agentstack.voostack.com/s/uiverify)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [uiverify](https://github.com/uiverify)
- **Source:** https://github.com/uiverify/uiverify/tree/main/packages/skills/skills/storybook-visual-testing
- **Website:** https://uiverify.ai/

## Install

```sh
agentstack add skill-uiverify-uiverify-storybook-visual-testing
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Deterministic Storybook stories

## Mental model — Storybook already removed most of the flake

UI Verify renders each **story** as a baseline. Because a story is an isolated component in a controlled
harness, you get for free the things that make real-page capture hard: no page scroll, no A/B / analytics
/ chat / consent scripts, no lazy-load-on-scroll races. That isolation is the point — the determinism
work here is **narrow**. If you reach for scroll-settling or third-party stubbing, you're fighting a
problem Storybook already removed; that's a real-page concern (see `playwright-visual-testing`).

**UI Verify's capturer also neutralizes these automatically — do NOT hand-fix them:**

- CSS animations & transitions (killed at render), and the Web Animations API (disabled).
- `prefers-reduced-motion: reduce` — **emulated**, so any component that honors it renders its calm state.
- `Math.random` — **seeded** before your app code runs (a shuffle/jitter driven by `Math.random` is
  already stable).
- Web fonts and `` loading — **waited for** before capture.
- **Finite** JS animations (a Recharts entry draw, react-smooth) — captured at their settled final frame.
  You don't need to disable these.

So the checklist below is only the remainder — what lives *inside your app* and can't be fixed from
outside it.

Point UI Verify at your built stories:
```bash
npm run build-storybook && uiverify upload --static-dir storybook-static
```

## The checklist (only what the tool can't do for you)

### 1. Freeze the clock

The one thing the capturer deliberately does **not** do (freezing time breaks entry animations). Any
component that reads the clock — a relative timestamp, a date picker defaulting to "today", a chart's day
axis — drifts every run. Pin it with
[`storybook-addon-mock-date`](https://www.npmjs.com/package/storybook-addon-mock-date) (Storybook 10+),
which mocks `Date` per story:

```ts
// .storybook/main.ts
addons: ['storybook-addon-mock-date'],
```
```ts
// .storybook/preview.ts — a fixed date for every story (meta- or story-level overrides it)
export default { parameters: { mockingDate: new Date('2020-01-01T00:00:00Z') } };
```

Pass a `Date`, a millisecond timestamp, or an ISO string as `mockingDate`; the most specific value
(story > meta > preview) wins, so a single story can pin its own "today". On older Storybook, install
[`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers) in a decorator instead
(`install({ now: FROZEN_NOW })`) — it covers `Date`, `Date.now`, and timers in one call.

### 2. Infinite JS animations

A CSS/WAAPI/finite animation is handled for you (above). What's left is an **infinite** JS loop that
never has a final frame — framer-motion pulsing dots, a Lottie loop, an autoplay spinner. Two fixes:

- **Preferred — honor reduced motion.** The capturer emulates `prefers-reduced-motion: reduce`, so make
  the component respect it. framer-motion ignores it by default (`reducedMotion: "never"`); opt in:
  ```tsx
  // .storybook/preview.tsx decorator
  
  ```
  or gate the loop yourself with `useReducedMotion()`. One line, and it's good app behavior anyway.

- **Escape hatch — detect the capture** and render the end state. UI Verify flags every capture with a
  `UIVerify` marker on the user-agent and a `window.__UI_VERIFY__` global (its `isChromatic()` analog),
  so a component can branch:
  ```ts
  export const isUIVerify = () =>
    (typeof navigator !== 'undefined' && navigator.userAgent.includes('UIVerify')) ||
    (typeof window !== 'undefined' && '__UI_VERIFY__' in window);
  ```
  ```tsx
  
  ```
  Prefer pausing at the **end** frame, not the start.

### 3. Mock live data — never let a story hit a live API

A story that fetches from a real backend is flaky by construction (network, changing data, auth). Mock it
with a fixed fixture — [MSW via `msw-storybook-addon`](https://storybook.js.org/addons/msw-storybook-addon)
is the standard — returning the **same** response every time.

### 4. Non-`Math.random` randomness

`Math.random` is seeded for you, but `crypto.randomUUID()`, a `uuid` library, or faker are **not**. Use
fixed fixtures for anything that ends up on screen (an id in the DOM, a faker name), or set a fixed faker
seed.

### 5. Dynamic layout

A JS-measured layout that reflows or reorders on its own (packing driven by measured size, a shuffled
list) can vary run-to-run even with identical content. Force a deterministic variant in the story (a
fixed order/size), or mask the region.

## Anti-patterns

- **A story that fetches live data** → mock it (MSW).
- **Disabling CSS animations / seeding `Math.random` / waiting on fonts by hand** → wasted effort; the
  capturer already does all three. Spend the effort on the clock and infinite loops.
- **Unseeded `crypto`/`uuid`/faker or a bare `Date.now()`** → fixtures + freeze the clock.
- **Reaching for scroll-settle / A-B stubbing** → wrong path; that's a real-page concern.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [uiverify](https://github.com/uiverify)
- **Source:** [uiverify/uiverify](https://github.com/uiverify/uiverify)
- **License:** MIT
- **Homepage:** https://uiverify.ai/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-uiverify-uiverify-storybook-visual-testing
- Seller: https://agentstack.voostack.com/s/uiverify
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
