Skip to content

feat!: sharp for PNG codec, Batch Review Mode by default - #413

Open
FRSgit wants to merge 9 commits into
mainfrom
feat/major-5
Open

FRSgit wants to merge 9 commits into
mainfrom
feat/major-5

Conversation

@FRSgit

@FRSgit FRSgit commented Sep 21, 2026

Copy link
Copy Markdown
Member

Summary

Two breaking changes for v5: image comparison no longer goes through pngjs, and Batch Review Mode becomes the default.

sharp replaces pngjs

  • compareImagesTask decodes, pads and encodes with sharp (already a runtime dependency). pngjs's default write tried all five PNG filters per scanline in JS and accounted for most of the comparison time; sharp's native path is many times faster and unaffected by V8 coverage instrumentation.
  • Same-size images are passed through as the bytes already in memory, so only the diff image is encoded on the success path. The task return contract is unchanged.
  • Pixel data and diff results are identical to 4.x (verified by decoding both fixtures with pngjs and sharp and running both pad+pixelmatch pipelines: same RGBA bytes, same diff pixel count).
  • pngjs, @types/pngjs, and the deprecated stubs @types/sharp and @types/pixelmatch are removed.

Batch Review Mode on by default

  • pluginVisualRegressionBatchReviewMode and pluginVisualRegressionBatchReviewModeShowPassingImages are now read by shared getters in config.utils.ts. Previously support.ts (FAB, carousel, hooks) and commands.ts (matchImage, inlined) each read the flag on their own.
  • The mode is on unless the option is explicitly false (or the string "false" from the CLI).
  • examples/next configs drop the now-redundant opt-in; README's "How to enable" becomes "How to disable"; MIGRATION.md gets a new entry.

Breaking changes

  • Batch Review Mode is the default. matchImage() no longer throws on the first mismatch. Failing snapshots are collected and a single error with the failure count is thrown once the spec finishes; in headed mode they can be reviewed and approved in bulk from the plugin's floating button. To keep the 4.x behaviour, set pluginVisualRegressionBatchReviewMode: false via expose (Cypress >= 15.10) or env (older Cypress). Projects that already set it to true can drop the option.

  • Node.js >= 20.9.0. sharp 0.35 already required it; the engines field was stale.

  • PNG bytes change. Files written by the plugin are now encoded by libvips. Existing baselines keep working and are only rewritten when updated. DIFF_IMAGES_VERSION stays 1 because the comparison algorithm is unchanged.

  • Uniform padding. When compared images differ in size, the added area is now fully filled with rgba(0, 0, 0, 64); 4.x left the first padded row/column transparent due to an off-by-one. Diff results are unaffected.

Migration notes: packages/cypress-plugin-visual-regression-diff/MIGRATION.md, section 4.x -> 5.x.

Tests

  • New config.utils.test.ts covers the expose/env fallback, the batch-mode default, and disabling via false / "false" on both Cypress APIs. support.test.ts keeps only the localStorage precedence tests.

  • Base64 PNG snapshots (72 KB, coupled to encoder output) replaced by structural assertions: decode with sharp, check dimensions, check that same-size images are passed through untouched.

  • New image.utils.test.ts covers decode (RGB → RGBA), encode round trip, padding pixels, and scaleImageAndWrite (previously untested).

  • Integration suite with coverage: test body time 818 ms → 146 ms.

  • Cypress examples/next and examples/webpack: component and e2e suites pass with Batch Review Mode on by default (no explicit option in either config); a plain comparison run against committed baselines passes; a forced size mismatch fails correctly and writes a stamped .diff.png at the padded size.

Other changes

  • Cypress devDependency bumped to 16.1.0 in the plugin package and both examples (peer range >=13 unchanged). Example scripts use --expose, the Next.js example mounts from cypress/react, and the legacy Cypress.env() fallback goes through a local type since Cypress 16 typings dropped it.
  • scripts/cypress.demo.config.ts (used only by the demo GIF recorder) moved out of examples/next.

🤖 Generated with Claude Code

FRSgit and others added 2 commits September 21, 2026 09:03
Move decode / pad / encode in compareImagesTask from pngjs (pure JS,
per-scanline filter heuristic on every write) to sharp, which is already a
runtime dependency. Same-size images reuse the bytes already in memory, so
only the diff gets encoded on the success path. Pixel data and diff results
are unchanged; base64 snapshot tests are replaced by structural assertions.

BREAKING CHANGE: Node.js 20.9 or newer is now required (sharp 0.35 already
needed it, the engines field was stale). PNG files written by the plugin are
now encoded by sharp/libvips, so their bytes differ from 4.x output while
pixels are identical; existing baselines keep working. When compared images
differ in size, the padding now uniformly covers the whole added area. pngjs
is no longer a dependency.

For migration guide, go to this link: https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/blob/main/packages/cypress-plugin-visual-regression-diff/MIGRATION.md#4x---5x

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Batch Review Mode (added as opt-in in 4.2) is now on unless it is
explicitly turned off. Reading of pluginVisualRegressionBatchReviewMode
and pluginVisualRegressionBatchReviewModeShowPassingImages moves from
support.ts and an inlined copy in commands.ts into config.utils.ts, so
both the FAB/carousel hooks and matchImage() share one getter and one
default. Example configs drop the now-redundant opt-in, README describes
how to disable the mode instead of how to enable it, and MIGRATION.md
gets a 4.x -> 5.x entry.

BREAKING CHANGE: matchImage() no longer fails the test on the first
mismatch. Failing snapshots are collected and a single error with the
failure count is thrown once the spec finishes (in headed mode they can
be reviewed and approved in bulk from the plugin's floating button). To
keep the 4.x behaviour, set pluginVisualRegressionBatchReviewMode to
false via `expose` (Cypress >=15.10) or `env` (older Cypress).

For migration guide, go to this link: https://github.com/FRSOURCE/cypress-plugin-visual-regression-diff/blob/main/packages/cypress-plugin-visual-regression-diff/MIGRATION.md#4x---5x

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@FRSgit FRSgit changed the title feat!: use sharp for PNG decoding and encoding feat!: sharp for PNG codec, Batch Review Mode by default Sep 21, 2026
FRSgit and others added 3 commits September 21, 2026 15:01
The plugin package (15.21.1), examples/webpack (15.18.0) and
examples/next (13.14.2) now all develop against Cypress 16.1.0.

Cypress 16 removed the `--env` CLI flag and `Cypress.env()` in favour
of `--expose` / `Cypress.expose()`, so the example scripts and the demo
recording script pass plugin options via `--expose`. Cypress 14+ also
dropped the `cypress/react18` mount entry point; the Next.js example now
mounts from `cypress/react`.

The peerDependency range (`cypress >=13.0.0`) is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cypress.demo.config.ts exists only for scripts/record-batch-demo.mjs, so
it now lives next to that script instead of in the end-user example.
The recorder passes the config's absolute path via --config-file while
keeping examples/next as the project root, and the config imports
initPlugin from the built workspace package. cypress is added to the
root devDependencies so the config resolves it from scripts/ (the root
previously held a stale auto-installed 13.x peer).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Cypress 16 removed `Cypress.env()` from its type definitions, which broke
the plugin build (TS2339 in config.utils.ts) once the devDependency was
bumped. The legacy fallback for Cypress <15.10 now goes through a
narrow local type instead of the removed declaration.

The examples/next e2e baselines are re-rendered with Cypress 16 so a
local `cypress run` compares cleanly again; CI keeps running the example
with pluginVisualRegressionUpdateImages=true as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Drop pngjs-era shapes that survived the sharp migration:

- scaleImage takes and returns a buffer instead of writing the resized PNG
  to disk and reading it back, only for compareImagesTask to write the same
  file again
- getImageSize reads dimensions from the PNG header, and decodePNG decodes
  straight to the common size (extend is a no-op for same-size images), so
  mismatched images are decoded once instead of twice and padImageToSize is
  gone
- Buffers are passed straight to pixelmatch and fs.writeFileSync; the
  Uint8Array view helper and the extra copy of the stamped screenshot are
  removed, addPNGMetadata returns a zero-copy Buffer
- isImageOfTestType parses PNG metadata once instead of twice per file,
  writePNG is inlined at its single call site

The Buffer typing issues that forced the old helpers came from the
deprecated @types/glob stub pulling in @types/node 18. Replace it with an
explicit @types/node 20.x (matching engines) and reference it in tsconfig;
examples/next gets the same pin.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@FRSgit
FRSgit added this pull request to stack #419 September 21, 2026 23:49
@FRSgit
FRSgit removed this pull request from stack #419 September 21, 2026 23:52
FRSgit and others added 3 commits September 22, 2026 01:52
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…#421)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant