npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@crvy/strybk

v0.0.10

Published

Generator-first Playwright screenshot testing for Storybook.

Readme

@crvy/strybk

Generator-first Playwright screenshot testing for Storybook.

Installation

npm install --save-dev @crvy/strybk

CLI

Generate screenshot specs from a Storybook index:

crvy-strybk generate --config ./strybk.config.ts

Use --dry-run to compute outputs without writing files:

crvy-strybk generate --config ./strybk.config.ts --dry-run

The config module should export a StrybkConfig object, typically as the default export from defineConfig(...).

Minimal strybk.config.ts:

import { defineConfig } from "@crvy/strybk";

export default defineConfig({
  storybookUrl: "http://localhost:6006",
  storyGlobs: ["src/**/*.stories.tsx"],
  resolveSpecPath: ({ storyFilePath }) => storyFilePath.replace(/\.stories\.tsx$/, ".spec.ts"),
});

Generated tests

Each generated spec renders a Playwright test per story. At runtime, parameters.creevey (read from the running Storybook, merged across global / kind / story levels) drives capture and skip behavior — no Storybook addon required:

Generated suites import describe, test, expect, switchStory from @crvy/strybk and nest one describe per title segment — Components/Button becomes describe('Components') > describe('Button') — so the Playwright HTML reporter shows a collapsible tree. Snapshot filenames are unaffected. Note that --grep patterns containing / no longer match (Playwright greps the space-joined title path); grep by a single segment instead, e.g. --grep CommentLine.

  • captureElement: '<selector>' — captures page.locator('<selector>').
  • captureElement: null (or unset) — captures the viewport.
  • skip: { '<reason>': { in, kinds, stories } } — marks the test skipped with <reason>. Note: in matches the Playwright project name (not the browser engine), so with the default project name chromium, a rule like { in: 'chrome' } won't match — name your Playwright projects to line up with your in: rules, or scope rules via kinds/stories.
  • ignoreElements: '<selector>' | ['<selector>'] — masks those elements via toHaveScreenshot({ mask }).
  • delay: <ms> — waits <ms> milliseconds after switching to the story before resolving the capture target. Non-positive or non-numeric values are treated as 0.

Example:

// stories/MyModal.stories.tsx
export default {
  title: "MyModal",
  parameters: { creevey: { captureElement: "#storybook-root" } },
};

export const Default = {
  parameters: { creevey: { ignoreElements: ".timestamp" } },
};

Prerequisites for running specs: @playwright/test (peer dependency) and browser binaries (npx playwright install). The crvy-strybk generate command itself needs neither — only a running Storybook to fetch index.json. To run the same specs under Vitest Browser Mode instead, see Vitest Browser Mode: vitest + @vitest/browser-playwright (optional peers) and a built Storybook.

Vitest Browser Mode

The same generated spec files run under either runner — no generator flag, no config change. playwright test and vitest run both discover *.spec.ts by default; the @crvy/strybk import resolves to the Playwright runtime in Node and to the Vitest runtime inside Vitest Browser Mode (via the browser package export condition). Pick a runner by choosing which command you execute.

Requirements and wiring:

  1. Build your Storybook. The Vitest path serves storybook build output through a proxy; dev-mode Storybook servers are not supported (their absolute vite dev asset URLs cannot resolve under the proxy). The dev loop stays on Playwright.

    npx storybook build
    npx http-server storybook-static -p 6007   # or any static server
  2. Wire the proxy plugin in vitest.config.ts. It mounts the Storybook origin under a reserved prefix (default /storybook, configurable) on the Vitest dev server so the embedded preview iframe is same-origin:

    import { strybkStorybookProxy } from "@crvy/strybk/vite";
    import { playwright } from "@vitest/browser-playwright";
    import { defineConfig } from "vitest/config";
    
    export default defineConfig({
      plugins: [strybkStorybookProxy({ target: "http://127.0.0.1:6007" })],
      test: {
        browser: {
          enabled: true,
          provider: playwright(),
          headless: true,
          instances: [{ browser: "chromium", viewport: { width: 1280, height: 720 } }],
        },
        provide: { strybkBrowser: "chromium" },
      },
    });

    Reserve the prefix: requests under /storybook are proxied to the target, so keep it free of your own routes (or pass a different prefix).

  3. Provide the browser identity (strybkBrowser) so skip in: rules match like Playwright project names — the default is chromium. Optionally provide strybkGlobals (applied to the preview through the Storybook channel before capture), mirroring the Playwright path's storybookGlobals project metadata.

Notes:

  • Baselines live under __screenshots__/ next to the specs. The first run writes the baseline and fails ("No existing reference screenshot found; a new one was created"); seed baselines without failing with vitest run --update. Each runner keeps its own baseline tree — do not share them across runners.
  • Capture parity is pinned by the runtime: CSS-pixel scale (scale: "css"), Playwright's default comparator threshold (0.2), and animations disabled. One caveat: keep the instance viewport within the headless browser window — Vitest scales its tester iframe when the viewport does not fit, which shrinks captures (this affects Vitest's own page.screenshot() identically).
  • Generated specs import describe from the package. Regenerate to adopt the new shape; un-regenerated specs that call test.describe keep collecting through the runtime bridge on every supported vitest 4.x.
  • Tested against vitest 4.0.18 and 4.1.11 (peer range vitest >=4 <5).

Portable interaction subset for manual regions (sharedPage.locator(...)):

| Portable API | Behavior | | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | click, dblClick, hover, fill, clear | Delegates to the Vitest locator; options (for example hover({ position })) pass through to the provider | | press | Accepts Playwright key chords (Control+A) and maps them to testing-library keyboard syntax | | type | Types text into the element (Vitest's userEvent.type) | | nth(i), first(), last(), locator(selector) | Re-wrapped locators; nested locator() scopes inside the parent, and masks/capture targets accept them | | dragTo(target, options) | Maps to Vitest's dropTo; accepts Playwright-style sourcePosition/targetPosition, force, timeout | | sharedPage.waitForTimeout(ms) | Waits before capture; backs the generated delay support |

Playwright-only APIs — manual regions using them stay single-runner:

  • sharedPage.mouse
  • Web-first element assertions (expect(locator).toHaveText(...) and friends)
  • waitForLoadState and the rest of the Playwright Page/Locator surface

For pointer positioning, the portable replacement for sharedPage.mouse is sharedPage.locator('body').hover({ position: { x, y } }).

Upgrading from 0.0.x

metadataExtractors has been removed. skip and captureElement now resolve automatically at runtime — remove any metadataExtractors: ["creevey"] line from your strybk.config.ts and re-run crvy-strybk generate. Skipped stories now appear as skipped in Playwright reports rather than being omitted from the spec file.

Changelog

Preview the next changelog entry:

bun run changelog:preview

Regenerate the full changelog from git history:

bun run changelog:generate

Local Linking

Build the package:

bun run build

Register this package for local linking:

cd /path/to/strybk
bun link

Link it into the consumer project:

cd /path/to/consumer-project
bun link @crvy/strybk

If you want to persist the link in the consumer's manifest, Bun supports a link: dependency entry:

{
  "dependencies": {
    "@crvy/strybk": "link:strybk"
  }
}

Development

Install dependencies with Bun:

bun install

Common local commands:

bun run lint
bun run typecheck
bun run test:bun
bun run build
bun run check