• Stars
    star
    3,821
  • Rank 11,514 (Top 0.3 %)
  • Language
    JavaScript
  • License
    Apache License 2.0
  • Created over 7 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

✨ Jest matcher for image comparisons. Most commonly used for visual regression testing.

Jest Image Snapshot - One Amex

npm Health Check Mentioned in Awesome Jest

Jest matcher that performs image comparisons using pixelmatch and behaves just like Jest snapshots do! Very useful for visual regression testing.

πŸ‘©β€πŸ’» Hiring πŸ‘¨β€πŸ’»

Want to get paid for your contributions to jest-image-snapshot?

Send your resume to [email protected]

πŸ“– Table of Contents

✨ Features

  • Take image snapshots of your application
  • Ability to compare snapshots from a baseline
  • Update snapshots when you're good with changes
  • Customize a difference threshold
  • Add a Gaussian blur for noise
  • Adjust the diff layout horizontal vs vertical

How it works

Given an image (Buffer instance with PNG image data) the toMatchImageSnapshot() matcher will create a __image_snapshots__ directory in the directory the test is in and will store the baseline snapshot image there on the first run. Note that if customSnapshotsDir option is given then it will store baseline snapshot there instead.

On subsequent test runs the matcher will compare the image being passed against the stored snapshot.

To update the stored image snapshot run Jest with --updateSnapshot or -u argument. All this works the same way as Jest snapshots.

See it in action

Typically this matcher is used for visual tests that run on a browser. For example let's say I finish working on a feature and want to write a test to prevent visual regressions:

...
it('renders correctly', async () => {
  const page = await browser.newPage();
  await page.goto('https://localhost:3000');
  const image = await page.screenshot();

  expect(image).toMatchImageSnapshot();
});
...

Then after a few days as I finish adding another feature to my component I notice one of my tests failing!

Oh no! I must have introduced a regression! Let's see what the diff looks like to identify what I need to fix:

And now that I know that I broke the card art I can fix it!

Thanks jest-image-snapshot, that broken header would not have looked good in production!

πŸ€Ήβ€ Usage

Installation

npm i --save-dev jest-image-snapshot

Please note that Jest >=20 <=29 is a peerDependency. jest-image-snapshot will not work with anything below Jest 20.x.x

Invocation

  1. Extend Jest's expect
  const { toMatchImageSnapshot } = require('jest-image-snapshot');

  expect.extend({ toMatchImageSnapshot });
  1. Use toMatchImageSnapshot() in your tests!
  it('should demonstrate this matcher`s usage', () => {
    ...
    expect(image).toMatchImageSnapshot();
  });

See the examples for more detailed usage or read about an example use case in the American Express Technology Blog

πŸŽ›οΈ API

toMatchImageSnapshot() takes an optional options object with the following properties:

  • customDiffConfig: Custom config passed to pixelmatch (See options section) or ssim.js
    • Pixelmatch specific options
      • By default we have set the threshold to 0.01, you can increase that value by passing a customDiffConfig as demonstrated below.
      • Please note the threshold set in the customDiffConfig is the per pixel sensitivity threshold. For example with a source pixel colour of #ffffff (white) and a comparison pixel colour of #fcfcfc (really light grey) if you set the threshold to 0 then it would trigger a failure on that pixel. However if you were to use say 0.5 then it wouldn't, the colour difference would need to be much more extreme to trigger a failure on that pixel, say #000000 (black)
    • SSIM specific options
      • By default we set ssim to 'bezkrovny'. It is the fastest option and best option most of the time. In cases where, higher precision is needed, this can be set to 'fast'. See SSIM Performance Consideration for a better understanding of how to use this feature.
  • comparisonMethod: (default: pixelmatch) (options pixelmatch or ssim) The method by which images are compared. pixelmatch does a pixel by pixel comparison, whereas ssim does a structural similarity comparison. ssim is a new experimental feature for jest-image-snapshot, but may become the default comparison method in the future. For a better understanding of how to use SSIM, see Recommendations when using SSIM Comparison.
  • customSnapshotsDir: A custom absolute path of a directory to keep this snapshot in
  • customDiffDir: A custom absolute path of a directory to keep this diff in
  • storeReceivedOnFailure: (default: false) Store the received images seperately from the composed diff images on failure. This can be useful when updating baseline images from CI.
  • customReceivedDir: A custom absolute path of a directory to keep this received image in
  • customReceivedPostfix: A custom postfix which is added to the snapshot name of the received image, defaults to -received
  • customSnapshotIdentifier: A custom name to give this snapshot. If not provided one is computed automatically. When a function is provided it is called with an object containing testPath, currentTestName, counter and defaultIdentifier as its first argument. The function must return an identifier to use for the snapshot. If a path is given, the path will be created inside the snapshot/diff directories.
  • diffDirection: (default: horizontal) (options horizontal or vertical) Changes diff image layout direction
  • onlyDiff: (default: false) Either only include the difference between the baseline and the received image in the diff image, or include the 3 images (following the direction set by diffDirection).
  • noColors: Removes coloring from console output, useful if storing the results in a file
  • failureThreshold: (default 0) Sets the threshold that would trigger a test failure based on the failureThresholdType selected. This is different to the customDiffConfig.threshold above, that is the per pixel failure threshold, this is the failure threshold for the entire comparison.
  • failureThresholdType: (default pixel) (options percent or pixel) Sets the type of threshold that would trigger a failure.
  • updatePassedSnapshot: (default false) Updates a snapshot even if it passed the threshold against the existing one.
  • blur: (default 0) Applies Gaussian Blur on compared images, accepts radius in pixels as value. Useful when you have noise after scaling images per different resolutions on your target website, usually setting its value to 1-2 should be enough to solve that problem.
  • runInProcess: (default false) Runs the diff in process without spawning a child process.
  • dumpDiffToConsole: (default false) Will output base64 string of a diff image to console in case of failed tests (in addition to creating a diff image). This string can be copy-pasted to a browser address string to preview the diff for a failed test.
  • dumpInlineDiffToConsole: (default false) Will output the image to the terminal using iTerm's Inline Images Protocol. If the term is not compatible, it does the same thing as dumpDiffToConsole.
  • allowSizeMismatch: (default false) If set to true, the build will not fail when the screenshots to compare have different sizes.
it('should demonstrate this matcher`s usage with a custom pixelmatch config', () => {
  ...
  const customConfig = { threshold: 0.5 };
  expect(image).toMatchImageSnapshot({
    customDiffConfig: customConfig,
    customSnapshotIdentifier: 'customSnapshotName',
    noColors: true
  });
});

The failure threshold can be set in percent, in this case if the difference is over 1%.

it('should fail if there is more than a 1% difference', () => {
  ...
  expect(image).toMatchImageSnapshot({
    failureThreshold: 0.01,
    failureThresholdType: 'percent'
  });
});

Custom defaults can be set with a configurable extension. This will allow for customization of this module's defaults. For example, a 0% default threshold can be shared across all tests with the configuration below:

const { configureToMatchImageSnapshot } = require('jest-image-snapshot');

const customConfig = { threshold: 0 };
const toMatchImageSnapshot = configureToMatchImageSnapshot({
  customDiffConfig: customConfig,
  noColors: true,
});
expect.extend({ toMatchImageSnapshot });

jest.retryTimes()

Jest supports automatic retries on test failures. This can be useful for browser screenshot tests which tend to have more frequent false positives. Note that when using jest.retryTimes you'll have to use a unique customSnapshotIdentifier as that's the only way to reliably identify snapshots.

Removing Outdated Snapshots

Unlike jest-managed snapshots, the images created by jest-image-snapshot will not be automatically removed by the -u flag if they are no longer needed. You can force jest-image-snapshot to remove the files by including the outdated-snapshot-reporter in your config and running with the environment variable JEST_IMAGE_SNAPSHOT_TRACK_OBSOLETE.

{
  "jest": {
    "reporters": [
      "default",
      "jest-image-snapshot/src/outdated-snapshot-reporter.js"
    ]
  }
}

WARNING: Do not run a partial test suite with this flag as it may consider snapshots of tests that weren't run to be obsolete.

export JEST_IMAGE_SNAPSHOT_TRACK_OBSOLETE=1
jest

Recommendations when using SSIM comparison

Since SSIM calculates differences in structural similarity by building a moving 'window' over an images pixels, it does not particularly benefit from pixel count comparisons, especially when you factor in that it has a lot of floating point arithmetic in javascript. However, SSIM gains two key benefits over pixel by pixel comparison:

  • Reduced false positives (failing tests when the images look the same)
  • Higher sensitivity to actual changes in the image itself.

Documentation supporting these claims can be found in the many analyses comparing SSIM to Peak Signal to Noise Ratio (PSNR). See Wang, Z.; Simoncelli, E. P. (September 2008). "Maximum differentiation (MAD) competition: a methodology for comparing computational models of perceptual quantities" and Zhang, L.; Zhang, L.; Mou, X.; Zhang, D. (September 2012). A comprehensive evaluation of full reference image quality assessment algorithms. 2012 19th IEEE International Conference on Image Processing. pp. 1477–1480. Wikipedia also provides many great reference sources describing the topic.

As such, most users can benefit from setting a 1% or 0.01 threshold for any SSIM comparison. The below code shows a one line modification of the 1% threshold example.

it('should fail if there is more than a 1% difference (ssim)', () => {
  ...
  expect(image).toMatchImageSnapshot({
    comparisonMethod: 'ssim',
    failureThreshold: 0.01,
    failureThresholdType: 'percent'
  });
});

SSIM Performance Considerations

The default SSIM comparison method used in the jest-image-snapshot implementation is 'bezkrovny' (as a customDiffConfig {ssim: 'bezkrovny'}). Bezkrovny is a special implementation of SSIM that is optimized for speed at a small, almost inconsequential change in accuracy. It gains this benefit by downsampling (or shrinking the original image) before performing the comparisons. This will provide the best combination of results and performance most of the time. When the need arises where higher accuracy is desired at the expense of time or a higher quality diff image is needed for debugging, this option can be changed to {ssim: 'fast'}. This uses the original SSIM algorithm described in Wang, et al. 2004 on "Image Quality Assessment: From Error Visibility to Structural Similarity" (https://github.com/obartra/ssim/blob/master/assets/ssim.pdf) optimized for javascript.

The following is an example configuration for using {ssim: 'fast'} with toMatchImageSnapshot().

{
  comparisonMethod: 'ssim',
  customDiffConfig: {
    ssim: 'fast',
  },
  failureThreshold: 0.01,
  failureThresholdType: 'percent'
}

Recipes

Upload diff images from failed tests

Example Image Upload Test Reporter

If you are using jest-image-snapshot in an ephemeral environment (like a Continuous Integration server) where the file system does not persist, you might want a way to retrieve those images for diagnostics or historical reference. This example shows how to use a custom Jest Reporter that will run after every test, and if there were any images created because they failed the diff test, upload those images to an AWS S3 bucket.

To enable this image reporter, add it to your jest.config.js "reporters" definition:

"reporters": [ "default", "<rootDir>/image-reporter.js" ]

Usage in TypeScript

In TypeScript, you can use the DefinitelyTyped definition or declare toMatchImageSnapshot like the example below:

Note: This package is not maintained by the jest-image-snapshot maintainers so it may be out of date or inaccurate. Because of this, we do not officially support it.

declare global {
  namespace jest {
    interface Matchers<R> {
      toMatchImageSnapshot(): R
    }
  }
}

Ignoring parts of the image snapshot if using Puppeteer

If you want to ignore parts of the snapshot (for example some banners or other dynamic blocks) you can find DOM elements with Puppeteer and remove/modify them (setting visibility: hidden on block, if removing it breaks your layout, should help):

async function removeBanners(page){
  await page.evaluate(() => {
    (document.querySelectorAll('.banner') || []).forEach(el => el.remove());
  });
}

...
it('renders correctly', async () => {
  const page = await browser.newPage();
  await page.goto('https://localhost:3000');

  await removeBanners(page);

  const image = await page.screenshot();

  expect(image).toMatchImageSnapshot();
});
...

πŸ† Contributing

We welcome Your interest in the American Express Open Source Community on Github. Any Contributor to any Open Source Project managed by the American Express Open Source Community must accept and sign an Agreement indicating agreement to the terms below. Except for the rights granted in this Agreement to American Express and to recipients of software distributed by American Express, You reserve all right, title, and interest, if any, in and to Your Contributions. Please fill out the Agreement.

Please feel free to open pull requests and see CONTRIBUTING.md to learn how to get started contributing.

πŸ—οΈ License

Any contributions made under this project will be governed by the Apache License 2.0.

πŸ—£οΈ Code of Conduct

This project adheres to the American Express Community Guidelines. By participating, you are expected to honor these guidelines.

More Repositories

1

react-albus

✨ React component library for building declarative multi-step flows.
JavaScript
1,094
star
2

earlybird

EarlyBird is a sensitive data detection tool capable of scanning source code repositories for clear text password violations, PII, outdated cryptography methods, key files and more.
Go
697
star
3

xcprojectlint

A security blanket for Xcode project files
Swift
504
star
4

nodes

A GraphQL JVM Client - Java, Kotlin, Scala, etc.
Java
307
star
5

one-app

✨ a fresh, modular take on web application development
JavaScript
292
star
6

baton

HTTP load testing witten in Go
Go
264
star
7

iguazu

✨ Iguazu is a simple Redux-powered Async Query engine
JavaScript
202
star
8

busybee

BusyBee is an alternative API for IdlingResources in Espresso tests
Kotlin
189
star
9

jest-json-schema

✨ JSON schema matcher for Jest
JavaScript
169
star
10

parrot

✨ Scenario-based HTTP mocking
JavaScript
137
star
11

unify-flowret

A lightweight Java based orchestration engine
Java
98
star
12

holocron

✨Set of packages that are used to compose and load React components, enabling the updating and launching of server side rendered user experiences without server restarts
JavaScript
80
star
13

unify-jdocs

A new way of working with JSON documents without using model classes or JSON schemas
Java
72
star
14

hyperledger-fabric-sdk-php

Client SDK for Hyperledger Fabric for use in PHP applications
PHP
47
star
15

react-seo

✨ Simple SEO tag manager for React
JavaScript
45
star
16

fetchye

✨ If you know how to use Fetch, you know how to use Fetchye [fetch-yae]. Simple React Hooks, Centralized Cache, Infinitely Extensible.
JavaScript
41
star
17

eslint-config-amex

✨ This is American Express' eslint config
JavaScript
40
star
18

one-app-cli

✨A set of command line interface (CLI) tools for bundling One App, it's modules, and property files(locale and environment specific files).
JavaScript
36
star
19

k8s-prometheus-proxy

JavaScript
28
star
20

css-to-js

✨ Tool for transforming CSS into JS
JavaScript
28
star
21

grpc-k8s-health-check

A gRPC client-server application illustrating health checks of gRPC servers on Kubernetes
Go
27
star
22

amex-api-java-client-core

Library for authenticating with APIs published to the Amex for Developers portal.
Java
26
star
23

simplemli

Encode and Decode Message Length Indicators for TCP/IP socket based protocols
Go
25
star
24

synapse

Synapse is a set of lightweight foundational framework modules for rapid development, built-in with enterprise-grade maturity and quality.
Java
25
star
25

sfdx-cli-plugin

A plugin for the Salesforce DX CLI based on the Open CLI Framework (OCLIF)
TypeScript
24
star
26

one-service-worker

✨ Minimal, light-weight, Promise based library that unifies the service worker and browser threads under one API
JavaScript
23
star
27

purgecss-loader

✨ A Webpack loader to strip unused selectors from your CSS
JavaScript
22
star
28

redux-lifesaver

✨ redux-lifesaver is a middleware that keeps track of how many times actions of the same type are dispatched within a given period.
JavaScript
22
star
29

iguazu-rest

✨ Iguazu REST is a plugin for the Iguazu ecosystem that allows for pre-built async calls for REST with smart caching.
JavaScript
21
star
30

bucketlist

See what's in your BitBucket Server
Kotlin
18
star
31

react-conditional-flow

React components on top of control flow operations like if, if-else, if-else-if, switch-case, & try-catch
JavaScript
17
star
32

redux-idle-callback

✨ Redux middleware to dispatch actions during a Redux store's idle periods
JavaScript
16
star
33

babel-preset-amex

✨ Standard babel preset for American Express
JavaScript
16
star
34

iguazu-graphql

✨ Iguazu GraphQL is a plugin for the Iguazu ecosystem that allows for GraphQL requests backed by a simple cache.
JavaScript
16
star
35

amex-jest-preset

✨ An opinionated Jest preset
JavaScript
14
star
36

fetch-enhancers

✨ Library of middleware for enhancing Fetch
JavaScript
13
star
37

amex-jest-preset-react

✨ An opinionated Jest preset for React modules
JavaScript
13
star
38

xcode-result-bundle-processor

xcode-result-bundle-processor converts Xcode test result bundles into friendly HTML reports
Ruby
12
star
39

lumberjack

✨ Lumberjack is a minimal, configurable Console with utilities.
JavaScript
12
star
40

vitruvius

✨Vitruvius extends redux's combineReducers to allow developers to include a buildInitialState method on their reducer
JavaScript
11
star
41

jexm

Library to facilitate reading from Excel files
Java
10
star
42

targeted-offers-client

SDK for AMEX targeted offers API
JavaScript
10
star
43

task-status-pr-checker

This is triggered when a pull request is created. If the pull request has pending tasks. The pull request won't be merged and the status is updated on the pull request checks.
JavaScript
10
star
44

codeandsolve

9
star
45

parrot-jest-image-snapshot-example

✨ Example demonstrating usage of parrot for more deterministic jest-image-snapshot tests
JavaScript
9
star
46

amex-api-dotnet-client-core

Library for authenticating with APIs published to the Amex for Developers portal.
C#
9
star
47

express-checkout-tutorials

Code samples for use with Express Checkout API
HTML
8
star
48

one-app-ducks

✨Redux ducks used within the One App ecosystem.
JavaScript
8
star
49

env-config-utils

✨ Utilities for runtime configuration validations
JavaScript
8
star
50

create-shared-react-context

✨A memoized wrapper around React.createContext. Used by One App as a dependency to pass React context between Holocron modules without external usage.
JavaScript
8
star
51

middle-manager

An easy migration path away from the Java WorkManager concurrency API
Java
7
star
52

dux-socket

WebSocket middleware for Redux applications
JavaScript
7
star
53

one-app-dev-cdn

✨ Load remote modules for local development
JavaScript
7
star
54

gabby

Gabby provides a way to interface with several chatbot providers through "adapters". Adapters interface with different services such as api.ai and Watson Conversation.
TypeScript
7
star
55

json-parse-context

✨ Add the context of a json parse error to the error message with syntax highlighting if possible.
JavaScript
6
star
56

jacos

Java/Spring based SDK to enable insert, update and upsert of files and records in the SalesForce in Bulk, Single Record and Multi Record Mode
Java
6
star
57

react-iterative-flow

JavaScript
6
star
58

one-app-dev-proxy

✨ Proxy requests to remote servers locally while also allowing for an express middleware to be provided and applied to all requests
JavaScript
6
star
59

easydist

An end-to-end distributed deep learning tool that mimics the single-machine keras programming model
Python
6
star
60

findxcprojorphan

Finds source files unreferenced in Xcode project
Ruby
5
star
61

SPAN

A stored procedures framework
Java
5
star
62

jakasu

Powerful config driven SDK to easily consume events from one or multiple Kafka topics in both standard and streaming modes
Java
5
star
63

iguazu-rpc

✨ Iguazu RPC is a plugin for the Iguazu ecosystem that allows for configurable async calls and caching strategies.
JavaScript
5
star
64

fabric-integration

Project born out of a need to have a "stable" interface to the Fabric node SDK across various versions.
TypeScript
5
star
65

mlflow-server-proxy

Jupyter server proxy extension for mlflow tracking server
Python
4
star
66

bloom

BLooM is a configuration driven bigdata framework to load massive data into MemSQL
Java
4
star
67

suwannee

RESTful API to enable smart contract interaction from any language or platform
TypeScript
4
star
68

create-using-template

JavaScript
4
star
69

un-locode-java

Java
4
star
70

dydaq

DyDaQ framework connects GraphQL service layer with database efficiently
Java
4
star
71

omnitransform

Declarative and config driven Big Data/ETL framework based on Apache Spark
Java
3
star
72

rate-limiting-scheduled-executor

Java
3
star
73

jakapu

Config driven SDK to publish events into any Kafka topic without writing code
Java
3
star
74

yelp-search

Programmatically search Yelp for businesses using the public version 2.0 of the Yelp Search API
Swift
3
star
75

hide

Config Driven Big Data component which can generate data extracts in various formats from any Hive Tables
Shell
3
star
76

connectchain

Python
2
star
77

chaincode-guide

1
star