Capture screenshots for Difora
Render in your CI, keep names stable, and upload PNGs for review.
Version: the helpers below require CLI 0.4.0 or newer.
The release is prepared; npm distribution is temporarily unavailable.
Pilot users with the supplied release tarball can install it with
npm install --save-dev ./difora-0.4.0.tgz. Registry
installation will resume after publication.
Playwright
Install the CLI release package as a dev dependency alongside your own Playwright installation. The helper imports no browser package and adds no runtime dependencies.
import { test, expect } from '@playwright/test';
import { diforaScreenshot } from 'difora/playwright';
test('home', async ({ page }) => {
await page.goto('/'); // configure Playwright's baseURL for your app
await expect(page.getByRole('main')).toBeVisible();
await diforaScreenshot(page, 'home', {
fullPage: true,
viewportSuffix: true,
mask: [page.getByTestId('clock')],
});
});
The output directory is dir, then
DIFORA_SCREENSHOT_DIR, then ./screenshots. The
promise returns the absolute file path. Set a fixed Playwright viewport
to use viewportSuffix; a 1280-pixel viewport produces
home@1280.png.
To bind capture to a fixture, pass your own test after adding any custom fixtures:
import { test as base } from '@playwright/test';
import { withDifora } from 'difora/playwright';
const test = withDifora(base);
test('home', async ({ page, diforaScreenshot }) => {
await page.goto('/');
await diforaScreenshot('home', { viewportSuffix: true });
});
Options are fullPage, mask (Playwright
locators), clip (x/y/width/height),
viewportSuffix and dir. Choose full-page
capture or clipping. Animations are disabled and the text caret is
hidden during capture. Your test still needs to wait for application
data, fonts and images to be ready. Keep browser, fonts, viewport and
test data stable between runs.
Manual Playwright capture
import { mkdir } from 'node:fs/promises';
await mkdir('screenshots', { recursive: true });
await page.screenshot({
path: 'screenshots/home.png',
fullPage: true,
animations: 'disabled',
caret: 'hide',
});
Storybook test runner
For projects using @storybook/test-runner, set its
post-visit hook in .storybook/test-runner.ts. Start your
Storybook first, then run test-storybook.
import type { TestRunnerConfig } from '@storybook/test-runner';
import { createPostVisit } from 'difora/storybook';
export default {
postVisit: createPostVisit({
fullPage: true,
skip: ({ id }) => id === 'components-clock--live',
}),
} satisfies TestRunnerConfig;
Names follow <title>/<story>. The hook runs
after rendering and the story's play function. skip may
return a boolean or promise. The helper supports the test-runner hook
API; it is not a Storybook Vitest-addon integration.
Cypress recipe
No Difora-specific Cypress plugin is required. Choose a dedicated screenshot directory and avoid uploading automatic failure captures as intended baselines.
// cypress.config.ts
import { defineConfig } from 'cypress';
export default defineConfig({
screenshotsFolder: 'screenshots',
screenshotOnRunFailure: false,
trashAssetsBeforeRuns: true,
});
// Your Cypress test, after the page is ready:
cy.screenshot('home', { capture: 'viewport' });
Upload screenshots after cypress run. Cypress
includes the spec path in screenshot names; keep it stable.
Names and clean directories
The helpers preserve slash-separated groups, reject traversal and absolute paths, and sanitise unsupported characters. Changed segments receive a stable hash suffix so different labels do not silently collapse to the same path. A repeated name intentionally overwrites its previous capture. Use distinct names or viewport/browser suffixes for each intended snapshot.
Start each CI capture run with a fresh screenshot directory. Otherwise stale PNGs can remain in the uploaded manifest and conceal removals. The helpers never clear a directory automatically.
Then run npx difora upload ./screenshots with
DIFORA_TOKEN set as a CI secret. See
CI recipes and exit codes.
Reference APIs: Playwright fixtures, Storybook test-runner hooks, Cypress screenshots.