@lewiswright/vitest-plugin-vis
Version:
Vitest visual testing plugin
384 lines (304 loc) • 10.9 kB
Markdown
[![NPM version][npm_image]][npm_url]
[![NPM downloads][downloads_image]][npm_url]
Vitest visual testing plugin allowing you to capture and compare image snapshots automatically and manually.
It requires [Vitest Browser Mode][vitest-browser-mode] to work.
This plugin is inspired by [`jest-image-snapshot`][jest-image-snapshot],
and extracted from [`storybook-addon-vis`][storybook-addon-vis] for better modularity.
```sh
npm install --save-dev vitest-plugin-vis
pnpm add --save-dev vitest-plugin-vis
yarn add --save-dev vitest-plugin-vis
```
The `vitest-plugin-vis` plugin can be used without customization.
```ts
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import { vis } from 'vitest-plugin-vis/config'
export default defineConfig({
plugins: [vis()],
test: {
// vitest v2
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
// vitest v3
browser: {
enabled: true,
provider: 'playwright',
instances: [
{ browser: 'chromium' }
]
}
}
})
```
This default configuration will:
- Use the `auto` preset, taking image snapshot at the end on each rendering test.
- Use `pixelmatch` as the image comparison method.
- Set config to compare image snapshot with a failure threshold of `0 pixels`.
- Timeout for image comparison is set to `30000 ms`.
- Local (non-CI) image snapshots are saved in the `<root>/__vis__/local` directory.
- CI image snapshots are saved in the `<root>/__vis__/<process.platform>` directory.
- Image snapshots of the current test run are saved in the `<root>/__vis__/*/__results__` directory.
- Diff images are saved in the `<root>/__vis__/*/__diffs__` directory.
- Baseline images are saved in the `<root>/__vis__/*/__baselines__` directory.
You can customize the configuration:
```ts
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import { vis, trimCommonFolder } from 'vitest-plugin-vis/config'
export default defineConfig({
plugins: [
vis({
preset: 'auto',
snapshotRootDir: ({
ci, // true if running on CI
platform, // process.platform
providerName, // 'playwright' or 'webdriverio'
browserName,
screenshotFailures, // from `browser` config
screenshotDirectory, // from `browser` config
}) => `__vis__/${ci ? platform : 'local'}`,
platform: '...', // {process.platform} or `local` (deprecated use `snapshotRootDir` instead)
customizeSnapshotSubpath: (subpath) => trimCommonFolder(subpath),
// will change to "isAutoSnapshot ? `${id}-auto` : `${id}-${index}`" in the next major release.
customizeSnapshotId: ({ id, index, isAutoSnapshot }) => `${id}-${index}`,
// set a default subject (e.g. 'subject') to capture image snapshot
subjectDataTestId: undefined,
comparisonMethod: 'pixel',
// pixelmatch or ssim.js options, depending on `comparisonMethod`.
diffOptions: undefined,
timeout: 30000,
// disable animations in playwright
animations: false,
failureThresholdType: 'pixel',
failureThreshold: 0,
})
],
test: {
browser: {/* ... */}
}
})
```
If you want to do a custom snapshot at the end of each test,
such as taking a snapshot for each theme,
you can set it up using a custom setup file:
```ts
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import { vis } from 'vitest-plugin-vis/config'
export default defineConfig({
plugins: [
vis({ preset: 'none' })
],
test: {
browser: {/* ... */},
setupFiles: ['vitest.setup.ts']
}
})
// vitest.setup.ts
import { vis } from 'vitest-plugin-vis/setup'
// if you set `preset: none` in `vitest.config.ts`,
// you can use a preset manually here.
// Take snapshot at the end of each rendering test.
vis.presets.auto()
// Enable snapshot testing for manual snapshot only.
vis.presets.manual()
// Enable snapshot testing, allow auto snapshot with `setAutoSnapshotOptions()`
vis.presets.enable()
// Take image snapshot for each rendering test,
// one snapshot per theme (light and dark in this example).
//
// Note that this changes the theme in the `afterEach` hook.
// If you want to capture manual snapshots in different themes,
// configure Vitest to run the tests in different themes.
vis.presets.theme({
async light() { document.body.classList.remove('dark') },
async dark() { document.body.classList.add('dark') },
})
```
The main usage of this addon is to use the `toMatchImageSnapshot` matcher.
Since it is exposed under the `expect` object of `vitest`,
you typically do not need to import `vitest-plugin-vis` directly.
Because of this, TypeScript may not recognize the matcher.
To address this, you can add the following to your `tsconfig.json`:
```json
{
"compilerOptions": {
"types": ["vitest-plugin-vis"]
}
}
```
By default, the plugin will use the `auto` preset,
which will take a snapshot at the end of each rendering test.
You can control how the auto snapshot is taken using the `setAutoSnapshotOptions` function:
```ts
import { setAutoSnapshotOptions } from 'vitest-plugin-vis'
import { beforeEach, it } from 'vitest'
beforeAll((suite) => {
// Apply options to all tests in the current suite (file)
setAutoSnapshotOptions(suite, /* options */)
// or
setAutoSnapshotOptions(/* options */)
})
beforeEach(({ task }) => {
// Apply options to all tests in the current scope
setAutoSnapshotOptions(task, /* options */)
// or
setAutoSnapshotOptions(/* options */)
})
it('disable snapshot per test', async ({ task }) => {
// Apply options to this test only
setAutoSnapshotOptions(task, /* options */)
// or
setAutoSnapshotOptions(/* options */)
})
describe('nested scope', () => {
beforeEach(({ task }) => {
// Apply options to all tests in the current scope
setAutoSnapshotOptions(task, /* options */)
// or
setAutoSnapshotOptions(/* options */)
})
})
```
The options are similar to `expect(...).toMatchImageSnapshot(options)`:
```ts
// enable/disable auto snapshot
setAutoSnapshotOptions(true /* or false */)
// detailed options
setAutoSnapshotOptions({
enable: true,
comparisonMethod: 'pixel',
customizeSnapshotId: ({ id, index, isAutoSnapshot }) => `${id}-custom-${index}`,
// pixelmatch or ssim.js options, depending on `comparisonMethod`.
diffOptions: { threshold: 0.01 },
failureThreshold: 0.01,
failureThresholdType: 'percent',
timeout: 60000
})
```
You can also provide additional options, which you can use during theme to enable/disable snapshot for each theme:
```ts
setAutoSnapshotOptions({
skipDark: true
})
// in vitest.setup.ts
vis.presets.theme({
async dark(options) {
if (options.skipDark) return false
document.body.classList.add('dark')
},
})
```
You can also set the preset to `manual` and compare snapshots manually:
```ts
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [
vis({ preset: 'manual' })
],
test:{
browser: {/* ... */}
}
})
// some.test.ts
import 'vitest-browser-react' // do this in your setup file to get `page.render`
import { page } from '@vitest/browser/context'
import { it } from 'vitest'
it('manual snapshot', async ({ expect }) => {
page.render(<div data-testid="subject">hello world</div>)
await expect(document.body).toMatchImageSnapshot(/* options */)
// or
const subject = page.getByTestId('subject')
await expect(subject).toMatchImageSnapshot(/* options */)
})
```
You can customize the snapshot comparison options per assertion:
```ts
// some.test.ts
import 'vitest-browser-react' // do this in your setup file to get `page.render`
import { page } from '@vitest/browser/context'
import { it } from 'vitest'
it('manual snapshot with options', async ({ expect }) => {
page.render(<div data-testid="subject">hello world</div>)
const subject = page.getByTestId('subject')
await expect(subject).toMatchImageSnapshot({
customizeSnapshotId: ({ id, index, isAutoSnapshot }) => `${id}-custom-${index}`,
failureThreshold: 0.01,
failureThresholdType: 'percent',
diffOptions: {
threshold: 0.1
},
timeout: 60000
})
})
```
The `enable` preset allows you to take manual snapshot,
as well as auto snapshot at the end on a rendering test using the `setAutoSnapshotOptions` function:
```ts
import { setAutoSnapshotOptions } from 'vitest-plugin-vis'
import { beforeEach, it } from 'vitest'
it('take snapshot', async () => {
setAutoSnapshotOptions(/* options */) // e.g. `true`
// ...your test...
})
```
While less common, you can also check if a snapshot exists:
```ts
import { page } from '@vitest/browser/context'
import { it } from 'vitest'
it('Has Snapshot', async ({ expect }) => {
const hasSnapshot = await page.hasImageSnapshot(/* options */)
if (!hasSnapshot) {
// do something
}
else {
// do something else
}
})
```
This is useful when you are performing negative test.
The local snapshots, current run results, and diffs should be ignored by git.
Add the following lines to your `.gitignore` file:
```sh
**/__vis__/**/__diffs__
**/__vis__/**/__results__
**/__vis__/local
```
Vitest visual testing plugin runs on [Vitest Browser Mode][vitest-browser-mode].
Please follow its guide to set up your environment.
Bonus note, if you want to install [Firefox] on WSL,
you can follow these steps: [Install Firefox on Ubuntu 22.04](https://askubuntu.com/a/1444967).
Also, you may need to `sudo apt-get install xdg-utils` to fix [`xdg-settings: not found`](https://serverfault.com/questions/1091926/running-chrome-on-ubuntu-server-how-to-solve-xdg-settings-not-found-using).
When running on CI, the plugin will save the image snapshots in the `<root>/__vis__/<process.platform>` directory.
The image snapshots are taken on the server side using `playwright` or `webdriverio` depending on your browser provider.
It is recommended to run your tests serially to avoid flakiness.
> feature X in [`jest-image-snapshot`][jest-image-snapshot] is missing
Some features in [`jest-image-snapshot`][jest-image-snapshot] are not implemented in [`vitest-plugin-vis`][vitest-plugin-vis] yet.
This is because through our own usage, we do not found a good use case for them.
If you have a good use case for these features, please open an issue or PR.
[]: https://img.shields.io/npm/dm/vitest-plugin-vis.svg?style=flat
[]: https://www.mozilla.org/en-US/firefox/
[]: https://github.com/americanexpress/jest-image-snapshot
[]: https://img.shields.io/npm/v/vitest-plugin-vis.svg?style=flat
[]: https://npmjs.org/package/vitest-plugin-vis
[]: https://github.com/repobuddy/storybook-addon-vis
[]: https://vitest.dev/guide/browser/
[]: https://www.npmjs.com/package/vitest-plugin-vis