Visual Regression Testing with AskUI and Jest
Tags: javascript, typescript, tutorial
Published: 2023-08-07 07:07:00

Visual Regression Testing (VRT) is a useful method to guard yourself against unwanted layout and visual changes that can occur with new version of a Graphical User Interface (GUI).

AskUI is not a testing tool in the original sense but an automation tool for all GUIs that is platform-independent. Wouldn't it be nice to combine VRT with AskUI, so you do not have to maintain two different projects.

Luckily you can do this because Jest - AskUIs favourite runner - can be extended to provide VRT, while AskUI delivers the screenshots to test on.

In this blog you will learn how to use AskUI in combination with Jest and the package `jest-image-snapshot` to include a visual regression test in your AskUI runs.

## Prerequisites

* AskUI installed: [Windows](https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started), [Linux](https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started-linux), [macOS](https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started-macos)

## Setup `jest-image-snapshot`
First you need to install the package that provides the VRT capabilities: [jest-image-snapshot](https://github.com/americanexpress/jest-image-snapshot).

### Installation
Install it as a dev dependency:

```bash
npm install --save-dev jest-image-snapshot
```

### Integration
The package provides a function `toMatchImageSnapshot` which implements Jest's `Matchers<R>` interface making it a Jest matcher that can be used with Jest's `expect()`.

We have to add this matcher to Jest with `expect.extend` like this in your workflow file ([See the docs](https://jestjs.io/docs/expect#expectextendmatchers)):

```typescript
const { toMatchImageSnapshot } = require('jest-image-snapshot');
expect.extend({ toMatchImageSnapshot });
```

Now the only thing you have to provide for a regression test in your workflows is an image to the function `expect()` as `Buffer`:

```typescript
expect(imageBuffer).toMatchImageSnapshot();
```

You can use AskUI to get the screenshot as a `base64` encoded `string`. The string is in the format of a [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). So you have to strip the scheme `data:[<mediatype>][;base64],` away because it is not a valid image with the scheme. Here is the full code:

```typescript
const annotate = await aui.annotate();
const imageBase64 = annotate.image;

// Strip away this: data:image/png;base64,
// Not valid png -> Only if used as a data URL
let imageBuffer = Buffer.from(
    imageBase64.split('base64,')[1], 'base64');

expect(imageBuffer).toMatchImageSnapshot();
```

### How to Get it to work with TypeScript
When you run this you will get an error:

```shell
FAIL  test/my-first-askui-test-suite.test.ts
  ● Test suite failed to run

  test/my-first-askui-test-suite.test.ts:16:25 - error TS2551: Property 'toMatchImageSnapshot' does not exist on type 'JestMatchers<Buffer>'. Did you mean 'toMatchSnapshot'?

  16     expect(imageBuffer).toMatchImageSnapshot();
                            ~~~~~~~~~~~~~~~~~~~~

    node_modules/@types/jest/index.d.ts:1111:9
    1111         toMatchSnapshot<U extends { [P in keyof T]: any }>(propertyMatchers: Partial<U>, snapshotName?: string): R;
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      'toMatchSnapshot' is declared here.
```

This is because Jest's typings in `jest.d.ts` do not include `toMatchImageSnapshot`.

Luckily TypeScript has a mechanism called [Declaration Merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html). When we create a file `jest.d.ts` in our project and declare our matcher there, TypeScript will pick it up.

```typescript
declare namespace jest {
    interface Matchers<R> {
      toMatchImageSnapshot(): CustomMatcherResult;
    }
}
```

When you rerun it you will get no error 🥳.

### How `jest-image-snapshot` Works
From the [repository](https://github.com/americanexpress/jest-image-snapshot):

>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 the 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.

You also want to check out the [options you can provide](https://github.com/americanexpress/jest-image-snapshot#%EF%B8%8F-api) to `toMatchImageSnapshot()` to fine-tune the behaviour. For example you might want to set a threshold for a mismatch so that minimal differences do not _fail_ a run:

```typescript
expect(image).toMatchImageSnapshot({
    failureThreshold: 0.01,
    failureThresholdType: 'percent'
  });
```

## Conclusion
You can use AskUI in combination with a Visual Regression Testing library that integrates with Jest like [jest-image-snapshot](https://github.com/americanexpress/jest-image-snapshot).

With this you can validate your GUI's final state and ensure it is the one you expected 🥳.