visual-regression-compare 0.0.0-stage → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +153 -2
- package/dist/approve.d.ts +33 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1301 -0
- package/dist/comparer.d.ts +70 -0
- package/dist/config.d.ts +5 -0
- package/dist/engines/index.d.ts +9 -0
- package/dist/engines/pixelmatch.d.ts +9 -0
- package/dist/image.d.ts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +948 -0
- package/dist/job.d.ts +36 -0
- package/dist/layout.d.ts +16 -0
- package/dist/options.d.ts +53 -0
- package/dist/plugins.d.ts +25 -0
- package/dist/pool.d.ts +22 -0
- package/dist/reporters.d.ts +15 -0
- package/dist/run.d.ts +7 -0
- package/dist/store.d.ts +9 -0
- package/dist/test-utils.d.ts +2 -0
- package/dist/types.d.ts +165 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +167 -0
- package/package.json +69 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 FRSOURCE - Let's shape your web (FRS JAKUB FREISLER)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,154 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://visual-regression.dev">
|
|
3
|
+
<img src="https://visual-regression.dev/brand/mark.svg" alt="visual-regression.dev" width="96" height="96">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
<h1 align="center">visual-regression-compare</h1>
|
|
8
|
+
<p align="center">Compare folders of screenshots against their baselines and get a visual-regression-manifest, from the command line or from a test-runner adapter.</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/visual-regression-compare">
|
|
12
|
+
<img src="https://img.shields.io/npm/v/visual-regression-compare.svg" alt="NPM version badge">
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://github.com/visual-regression/visual-regression/blob/main/LICENSE">
|
|
15
|
+
<img src="https://img.shields.io/github/license/visual-regression/visual-regression.svg" alt="license MIT badge">
|
|
16
|
+
</a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
Pairs each screenshot with its baseline, runs a diff engine, applies your limits, writes the `.actual` and `.diff` files and a [`visual-regression-manifest`](https://visual-regression.dev/manifest) of the run. It captures nothing: the input is images, so it works with a Playwright or Puppeteer script, a Storybook crawl, an emulator or a design-tool export. Review the result with [`visual-regression-viewer`](https://visual-regression.dev/viewer).
|
|
20
|
+
|
|
21
|
+
The package is the shared compare core behind [pixsame](https://pixsame.com)'s test-runner plugins, and it is published as a beta until they build on it.
|
|
22
|
+
|
|
23
|
+
- [Usage](#usage)
|
|
24
|
+
- [Options](#options)
|
|
25
|
+
- [Size changes](#size-changes)
|
|
26
|
+
- [Approving](#approving)
|
|
27
|
+
- [Config file](#config-file)
|
|
28
|
+
- [Programmatic API](#programmatic-api)
|
|
29
|
+
- [Extending](#extending)
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx visual-regression-compare screenshots/actual --baseline screenshots/baseline
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Every `*.png` below `screenshots/actual` is compared with the same relative path below `screenshots/baseline`. A failing screenshot leaves `<name>.actual.png` and `<name>.diff.png` next to its baseline (or in `--diff <dir>`); the manifest goes to `<actual-dir>/visual-regression-manifest.compare.json`. The exit code is `1` when a screenshot fails or has no baseline, `2` on a usage or other error (an image that cannot be decoded aborts the run after the manifest is written).
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
- run: npx visual-regression-compare screenshots/actual --baseline screenshots/baseline --report console,github-summary
|
|
41
|
+
- uses: actions/upload-artifact@v4
|
|
42
|
+
if: failure()
|
|
43
|
+
with: { path: screenshots }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`record` is the same command with `--on-missing create --update all`: it copies every screenshot over its baseline.
|
|
47
|
+
|
|
48
|
+
## Options
|
|
49
|
+
|
|
50
|
+
| Option | Default | What it does |
|
|
51
|
+
| ------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| `--baseline <dir>` | required | Directory of baseline images. |
|
|
53
|
+
| `--diff <dir>` | next to the baselines | Where `.actual` and `.diff` files are written. |
|
|
54
|
+
| `--engine <id>` | `pixelmatch` | A built-in engine, a module path (`./my-engine.mjs`) or a package name. |
|
|
55
|
+
| `--max-diff-ratio <n>` | `0` | Largest accepted share of differing pixels, 0..1. |
|
|
56
|
+
| `--max-diff-pixels <n>` | | Largest accepted number of differing pixels. With both limits, the stricter one wins. |
|
|
57
|
+
| `--color-threshold <n>` | `0.1` | Per-pixel color tolerance of the engine (pixelmatch: YIQ distance, 0..1). |
|
|
58
|
+
| `--include-aa` | off | Count anti-aliased pixels as differences. |
|
|
59
|
+
| `--on-missing fail\|create` | `fail` | A screenshot without baseline is `missing-baseline`, or becomes the baseline (`created`). |
|
|
60
|
+
| `--on-size-mismatch fail\|pad\|crop` | `fail` | See [Size changes](#size-changes). |
|
|
61
|
+
| `--update none\|failures-only\|all` | `none` | Overwrite baselines (status `updated`): never, when the comparison failed, or whenever pixels differ (the same pixels in another PNG encoding leave the file alone). |
|
|
62
|
+
| `--match <template>` | same relative path | Baseline path from the screenshot path: `{dir}` (with trailing slash), `{name}`, `{path}`. |
|
|
63
|
+
| `--manifest <path>` | in `<actual-dir>` | Where the manifest is written. |
|
|
64
|
+
| `--runner`, `--variant`, `--platform` | | Metadata recorded on every entry; `--platform linux/chrome`. |
|
|
65
|
+
| `--report <list>` | `console` | `console`, `ctrf[:<file>]` (CTRF report), `github-summary[:<file>]` (default `$GITHUB_STEP_SUMMARY`). |
|
|
66
|
+
| `--concurrency <n>` | CPU count | Worker threads for decoding and comparing. |
|
|
67
|
+
| `--fail-on <list>` | `failed,missing-baseline` | Statuses that make the exit code `1`. |
|
|
68
|
+
| `--config <file>` | `visual-regression.config.*` | See [Config file](#config-file). |
|
|
69
|
+
|
|
70
|
+
A baseline that is a Git LFS pointer (a checkout without `git lfs pull`) fails with a message saying so, not with a decoding error.
|
|
71
|
+
|
|
72
|
+
## Size changes
|
|
73
|
+
|
|
74
|
+
A screenshot whose size differs from its baseline is never padded unless you ask for it:
|
|
75
|
+
|
|
76
|
+
- `fail` (default): nothing is compared. The entry fails, `message` and `extra.visualRegressionCompare` record both sizes (`baselineSize`, `actualSize`), and the viewer shows the resolution change as it is.
|
|
77
|
+
- `pad`: both images grow to the larger size with transparent pixels, then they are compared. The diff ratio is comparable, but a layout change hides behind a number, so the entry says that it was padded.
|
|
78
|
+
- `crop`: both images are cut to the smaller size, then compared.
|
|
79
|
+
|
|
80
|
+
## Approving
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npx visual-regression-compare approve --manifest screenshots/actual/visual-regression-manifest.compare.json
|
|
84
|
+
npx visual-regression-compare approve --manifest a.json --manifest b.json home "about (linux / chrome)"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Copies each entry's `images.actual.path` over its `images.baseline.path`, deletes the actual and diff files, rewrites the entry as `approved` in its manifest and prints the baseline files to commit. It works on any manifest, not only on ones this tool wrote. Without names it takes every entry that needs a human; `--test <file>` narrows that to one test file, `--dry-run` only reports.
|
|
88
|
+
|
|
89
|
+
- An entry is skipped, and the exit code is `1`, when its baseline or its actual file changed since the run (the hashes in the manifest no longer match), because the reviewer approved a diff against pixels that are no longer there. A screenshot without a baseline is skipped once a baseline has appeared. A manifest without hashes (written by another tool) skips this check. `--force` overrides.
|
|
90
|
+
- Entries of several manifests that write the same baseline are never approved without naming one.
|
|
91
|
+
- Paths resolve against the repository root: the working directory, or `--repo-root <dir>`. A project root outside it is refused (a manifest copied elsewhere must not make approve rewrite files in the tree it was written for), as is a CI project outside the checkout; a CI manifest carries `ci.workspace`, which locates its project inside the repository.
|
|
92
|
+
- The approved entry keeps the comparison that was approved and takes the size of the approved screenshot as the baseline size.
|
|
93
|
+
|
|
94
|
+
## Config file
|
|
95
|
+
|
|
96
|
+
`visual-regression.config.json`, `.mjs` or `.js` in the working directory (or `--config <file>`) sets the same options in camelCase; flags override it.
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"baselineDir": "screenshots/baseline",
|
|
101
|
+
"actualDir": "screenshots/actual",
|
|
102
|
+
"maxDiffRatio": 0.001,
|
|
103
|
+
"onSizeMismatch": "pad",
|
|
104
|
+
"engineOptions": { "threshold": 0.2 },
|
|
105
|
+
"report": "console,github-summary"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Programmatic API
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { createComparer, fsStore } from 'visual-regression-compare';
|
|
113
|
+
|
|
114
|
+
const comparer = createComparer({
|
|
115
|
+
limits: { maxDiffRatio: 0.01 },
|
|
116
|
+
onMissingBaseline: 'create',
|
|
117
|
+
onSizeMismatch: 'fail',
|
|
118
|
+
store: fsStore({
|
|
119
|
+
baselineDir: 'shots/baseline',
|
|
120
|
+
actualDir: 'shots/actual',
|
|
121
|
+
diffDir: 'shots/diff',
|
|
122
|
+
}),
|
|
123
|
+
manifest: {
|
|
124
|
+
path: 'shots/visual-regression-manifest.my-tool.json',
|
|
125
|
+
tool: { name: 'my-tool', version: '1.0.0' },
|
|
126
|
+
runner: { name: 'my-runner' },
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// one image, from a runner adapter: the baseline is found through the store
|
|
131
|
+
const entry = await comparer.compareImage({
|
|
132
|
+
name: 'home page renders',
|
|
133
|
+
actual: pngBuffer,
|
|
134
|
+
test: { filePath: 'e2e/home.spec.ts', titlePath: ['home page', 'renders'] },
|
|
135
|
+
platform: { osPlatform: process.platform, browser: { name: 'chromium' } },
|
|
136
|
+
viewport: { width: 1280, height: 720 },
|
|
137
|
+
});
|
|
138
|
+
// entry.status, entry.comparison.diffRatio, entry.images.*.path: a manifest entry
|
|
139
|
+
|
|
140
|
+
await comparer.compareAll(); // every image under actualDir
|
|
141
|
+
await comparer.close(); // flushes the manifest, ends the reporters
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Decoding and comparing run in worker threads when the engine is named by id (`engine: 'pixelmatch'`, `engine: './my-engine.mjs'`). An engine passed as an object runs in the main thread, and `concurrency` then only affects `compareAll`'s parallel reads. Engine options must be structured-cloneable.
|
|
145
|
+
|
|
146
|
+
## Extending
|
|
147
|
+
|
|
148
|
+
- **Engine**: a module whose default export is `{ name, colorMetric, colorThreshold?, compare(baseline, actual) }` or a function from options to one. `compare` gets two RGBA images of the same size and returns `{ diffPixels, diffRatio, diff?, score? }`.
|
|
149
|
+
- **Store, layout, matcher, reporter**: replace where images live (`Store`), how files are named (`Layout`), how a baseline is found (`Matcher`) and what happens with the entries (`Reporter`).
|
|
150
|
+
- **Plugin**: `{ name, setup(api) }` registers engines, reporters, a store, a layout or a matcher, and hooks `beforeCompare`, `afterCompare`, `decide`, `beforeWrite` and `end`. `decide` can turn a `failed` into `approved`; `beforeWrite` can drop the local write of a store that keeps baselines elsewhere. `CompareContext.attachments` is reserved for extra inputs such as a DOM snapshot; nothing fills it yet, and extra inputs have no standard shape.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ManifestEntry } from 'visual-regression-manifest';
|
|
2
|
+
export type ApproveOptions = {
|
|
3
|
+
/** Manifest files to read (the run may have several). */
|
|
4
|
+
manifests: string[];
|
|
5
|
+
/** The repository root the paths are applied to; default the working directory. */
|
|
6
|
+
repoRoot?: string;
|
|
7
|
+
/** Every entry that needs a human (default), or entries by name (`name` or `name (platform label)`). */
|
|
8
|
+
names?: string[];
|
|
9
|
+
/** Only entries of this test file (relative to the manifest's project root). */
|
|
10
|
+
testFile?: string;
|
|
11
|
+
/** Approve although the baseline or the actual file changed since the run. */
|
|
12
|
+
force?: boolean;
|
|
13
|
+
/** Report what would happen without touching any file. */
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type Skipped = {
|
|
17
|
+
entry: ManifestEntry;
|
|
18
|
+
reason: string;
|
|
19
|
+
};
|
|
20
|
+
export type ApproveResult = {
|
|
21
|
+
approved: ManifestEntry[];
|
|
22
|
+
skipped: Skipped[];
|
|
23
|
+
/** Baseline files written, relative to the repository root: what to commit. */
|
|
24
|
+
files: string[];
|
|
25
|
+
warnings: string[];
|
|
26
|
+
unknown: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Applies the approvals of a run: copies each selected entry's actual image
|
|
30
|
+
* over its baseline, deletes the actual and diff files and rewrites the
|
|
31
|
+
* entry in its manifest as `approved`. Works on any manifest.
|
|
32
|
+
*/
|
|
33
|
+
export declare const approve: ({ manifests, repoRoot, names, testFile, force, dryRun, }: ApproveOptions) => ApproveResult;
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|