Node.js screenshot capture for Storybook Test Runner - Provides stable screenshot functionality using Playwright for visual regression testing
npm install @storycap-testrun/nodeNode.js screenshot capture for Storybook Test Runner - Provides stable screenshot functionality using Playwright for visual regression testing.
---
@storycap-testrun/node implements stable screenshot capture for [@storybook/test-runner][storybook-test-runner] using the same reliability approach as [storycap][storycap] :camera:
@storycap-testrun/node?Standard waitForPageReady in [@storybook/test-runner][storybook-test-runner] provides only basic stability checks, often resulting in flaky visual tests.
@storycap-testrun/node solves this by implementing [storycap][storycap]'s proven stability detection:
- Metrics monitoring via [CDP][cdp] (Chrome DevTools Protocol) for rendering completion
- Hash verification across multiple screenshot captures for content consistency
- Flaky test prevention with masking and element removal capabilities
This ensures reliable visual regression testing without the common issues of timing-based approaches.
- High stability checks for rendering content
- Accurate waiting for Play Function
- Customization before and after screenshot capture using Hooks
- Masking of unstable elements
- Removal of unstable elements
- Skipping of unstable elements
- Integration with @storybook/test-runner
- CDP-based metrics monitoring
- Single viewport per test run
- A limitation of @storybook/test-runner. It can be addressed by running tests in multiple Viewports as needed
- No pseudo-state variants (:hover, :focus, etc.)
- Intentionally excluded to avoid conflicts with other postVisit hooks
- Workaround: Use Story play functions to set desired states before screenshot capture
Install via npm:
``bash`
$ npm install --save-dev @storycap-testrun/node
Please set up [@storybook/test-runner][storybook-test-runner] beforehand.
You can start using it immediately by calling the screenshot function in postVisit.
`typescript
// .storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { screenshot } from '@storycap-testrun/node';
const config: TestRunnerConfig = {
async postVisit(page, context) {
await screenshot(page, context, {
// options...
});
},
};
export default config;
`
> [!IMPORTANT]
> Since the screenshot function automatically performs stability checks internally, waiting functions like waitForPageReady are unnecessary.
`bash`
$ test-storybook
Then, run [@storybook/test-runner][storybook-test-runner].
By default, the screenshot images are saved in the __screenshots__ directory.
Enable type checking for screenshot parameters by extending your Storybook framework's module declaration.
Add this to a .d.ts file in your project (e.g., types/storybook.d.ts):
`typescript
import type { ScreenshotParameters } from '@storycap-testrun/node';
// Replace it with the framework you are using.
declare module '@storybook/react' {
interface Parameters {
screenshot?: ScreenshotParameters;
}
}
// For other frameworks, replace with your framework's module name
`
This enables IntelliSense and type checking when defining screenshot parameters in your stories.
- page: PagepostVisit
- The Playwright page instance passed in .context: TestContext
- @storybook/test-runner
- The test context from [][storybook-test-runner] passed in postVisit.options: NodeScreenshotOptions
- Promise
- See Options Section.
- Returns:
Options that can be specified as the third argument in the screenshot function.
Type: string path.join(process.cwd(), '__screenshots__')
Default:
Specifies the root directory for saving screenshot images.
Type: string | ((context: NodeScreenshotContext) => string) path.join('[title]', '[name].png')
Default:
Specifies the file name for the screenshot image of each Story.
When specifying a string, the following templates can be used:
- [id]: Story ID[title]
- : Story's title[name]
- : Story's name
When specifying a function, you can access the context object:
`typescript${context.name}.png
screenshot(page, context, {
output: {
file: (context) => path.join(context.title, ),`
},
});
Type: boolean true
Default:
When set to true, it monitors several metrics related to the rendering of the Story and performs stability checks.
> [!WARNING]
> As this process depends on [CDP][cdp], it works only in Chromium browsers. If enabled in browsers other than Chromium, a warning will be displayed.
Type: number 1000
Default:
The number of retries during metrics monitoring. It continues monitoring for the specified number of frames (1 frame = 16ms) and ensures that the metrics stabilize.
Type: boolean true
Default:
This option calculates the hash value of the screenshot image and checks the stability of the rendering content by ensuring that there are no changes in the image.
> [!TIP]
> In the case of Chromium, flakiness.metrics.enabled alone is often sufficient. Enabling this option means capturing screenshots at least twice, so it's recommended to disable it if it leads to performance degradation.
Type: number 100
Default:
The interval in milliseconds before attempting to capture the screenshot again. The second capture is performed immediately for hash checking.
Type: number 10
Default:
The number of times to repeat capturing the screenshot until the hash values of the images are identical. A value of 3 or more is recommended to achieve the effect of retries.
Type: NodeScreenshotHook[] []
Default:
Hooks for interrupting processes before and after screenshot capture. Please specify an object that implements the following interface.
`typescript`
export type BrowserScreenshotHook = {
setup?: (page: Page, context: NodeScreenshotContext) => Promise
preCapture?: (page: Page, context: NodeScreenshotContext) => Promise
postCapture?: (
page: Page,
context: NodeScreenshotContext,
filepath: string,
) => Promise
};
Each is executed in the following lifecycle:
| Method | Description |
| :------------ | :---------------------------------------------------------- |
| setup | Immediately after the screenshot function is executed |preCapture
| | After waitForPageReady is executed |postCapture
| | After the screenshot is captured, before the image is saved |
The built-in functions like Masking and Removal are implemented using Hooks.
Type: boolean true
Default:
See [Page | Playwright][playwright-screenshot].
Type: boolean false
Default:
See [Page | Playwright][playwright-screenshot].
Type: 'css' | 'device' 'device'
Default:
See [Page | Playwright][playwright-screenshot].
These are parameters that can be specified for each Story.
`typescript
// Button.stories.tsx
const meta: Meta
component: Button,
parameters: {
screenshot: {
/ parameters... /
},
},
};
export default meta;
`
Type: boolean false
Default:
Skips the screenshot capture. Useful in cases where you want to conduct tests with [@storybook/test-runner][storybook-test-runner] but disable screenshot capture.
Type: number
Default: None
The delay in milliseconds before capturing the screenshot. Waits after completing the basic stability checks.
Type: string | { selector: string; color: string }
Default: None
Masks elements corresponding to the CSS selector with a rectangle. Useful for hiding elements that inevitably differ in content with each render.
Type: string`
Default: None
Removes elements corresponding to the CSS selector.
[storycap]: https://github.com/reg-viz/storycap
[storybook-test-runner]: https://github.com/storybookjs/test-runner
[cdp]: https://chromedevtools.github.io/devtools-protocol/
[playwright-screenshot]: https://playwright.dev/docs/api/class-page#page-screenshot