Skip to content

Commit

Permalink
Document and test custom, async, inline snapshot matcher (#10922)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Dec 7, 2020
1 parent b0bf802 commit a424896
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 0 deletions.
35 changes: 35 additions & 0 deletions docs/ExpectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,41 @@ it('stores only 10 characters', () => {
});
```

#### async

If your custom inline snapshot matcher is async i.e. uses `async`-`await` you might encounter an error like "Multiple inline snapshots for the same call are not supported". Jest needs additional context information to find where the custom inline snapshot matcher was used to update the snapshots properly.

```js
const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
async toMatchObservationInlineSnapshot(fn, ...rest) {
// The error (and its stacktrace) must be created before any `await`
this.error = new Error();

// The implementation of `observe` doesn't matter.
// It only matters that the custom snapshot matcher is async.
const observation = await observe(async () => {
await fn();
});

return toMatchInlineSnapshot.call(this, recording, ...rest);
},
});

it('observes something', async () => {
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot(`"async action"`);
*/
});
```

### `expect.anything()`

`expect.anything()` matches anything but `null` or `undefined`. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a non-null argument:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`works with custom inline snapshot matchers 1`] = `
FAIL __tests__/asynchronous.test.js
✕ new async, inline snapshots
✕ mismatching async, inline snapshots
● new async, inline snapshots
expect(received).toMatchInlineSnapshot()
Snapshot name: \`new async, inline snapshots 1\`
New snapshot was not written. The update flag must be explicitly passed to write a new snapshot.
This is likely because this test is run in a continuous integration (CI) environment in which snapshots are not written by default.
Received: "result #1"
20 |
21 | test('new async, inline snapshots', async () => {
> 22 | await expect(async () => 'result #1').toMatchObservationInlineSnapshot();
| ^
23 | await expect(async () => 'result #2').toMatchObservationInlineSnapshot();
24 | });
25 |
at Object.toMatchObservationInlineSnapshot (__tests__/asynchronous.test.js:22:41)
● new async, inline snapshots
expect(received).toMatchInlineSnapshot()
Snapshot name: \`new async, inline snapshots 2\`
New snapshot was not written. The update flag must be explicitly passed to write a new snapshot.
This is likely because this test is run in a continuous integration (CI) environment in which snapshots are not written by default.
Received: "result #2"
21 | test('new async, inline snapshots', async () => {
22 | await expect(async () => 'result #1').toMatchObservationInlineSnapshot();
> 23 | await expect(async () => 'result #2').toMatchObservationInlineSnapshot();
| ^
24 | });
25 |
26 | test('mismatching async, inline snapshots', async () => {
at Object.toMatchObservationInlineSnapshot (__tests__/asynchronous.test.js:23:41)
mismatching async, inline snapshots
expect(received).toMatchInlineSnapshot(snapshot)
Snapshot name: \`mismatching async, inline snapshots 1\`
Snapshot: "result #?"
Received: "result #1"
25 |
26 | test('mismatching async, inline snapshots', async () => {
> 27 | await expect(async () => 'result #1').toMatchObservationInlineSnapshot(
| ^
28 | \`"result #?"\`,
29 | );
30 | await expect(async () => 'result #2').toMatchObservationInlineSnapshot(
at Object.toMatchObservationInlineSnapshot (__tests__/asynchronous.test.js:27:41)
● mismatching async, inline snapshots
expect(received).toMatchInlineSnapshot(snapshot)
Snapshot name: \`mismatching async, inline snapshots 2\`
Snapshot: "result #?"
Received: "result #2"
28 | \`"result #?"\`,
29 | );
> 30 | await expect(async () => 'result #2').toMatchObservationInlineSnapshot(
| ^
31 | \`"result #?"\`,
32 | );
33 | });
at Object.toMatchObservationInlineSnapshot (__tests__/asynchronous.test.js:30:41)
› 4 snapshots failed.
Snapshot Summary
› 4 snapshots failed from 1 test suite. Inspect your code changes or re-run jest with \`-u\` to update them.
`;
27 changes: 27 additions & 0 deletions e2e/__tests__/customInlineSnapshotMatchers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {wrap} from 'jest-snapshot-serializer-raw';
import {extractSummary} from '../Utils';
import runJest from '../runJest';

test('works with custom inline snapshot matchers', () => {
const {stderr} = runJest('custom-inline-snapshot-matchers', [
// Prevent adding new snapshots or rather changing the test.
'--ci',
'asynchronous.test.js',
]);

let {rest} = extractSummary(stderr);

rest = rest
.split('\n')
.filter(line => line.indexOf('at Error (native)') < 0)
.join('\n');

expect(wrap(rest)).toMatchSnapshot();
});
33 changes: 33 additions & 0 deletions e2e/custom-inline-snapshot-matchers/__tests__/asynchronous.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
async toMatchObservationInlineSnapshot(fn, ...args) {
this.error = new Error();
// This specific behavior can be implemented without a custom matcher.
// In a real example one might want to observe some global value that `fn()` is affecting.
// The difference between before and after `fn()` might then be persisted as a snapshot.
const result = await fn();

return toMatchInlineSnapshot.call(this, result, ...args);
},
});

test('new async, inline snapshots', async () => {
await expect(async () => 'result #1').toMatchObservationInlineSnapshot();
await expect(async () => 'result #2').toMatchObservationInlineSnapshot();
});

test('mismatching async, inline snapshots', async () => {
await expect(async () => 'result #1').toMatchObservationInlineSnapshot(
`"result #?"`,
);
await expect(async () => 'result #2').toMatchObservationInlineSnapshot(
`"result #?"`,
);
});
5 changes: 5 additions & 0 deletions e2e/custom-inline-snapshot-matchers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"jest": {
"testEnvironment": "node"
}
}
35 changes: 35 additions & 0 deletions website/versioned_docs/version-25.x/ExpectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,41 @@ it('stores only 10 characters', () => {
});
```

#### async

If your custom inline snapshot matcher is async i.e. uses `async`-`await` you might encounter an error like "Multiple inline snapshots for the same call are not supported". Jest needs additional context information to find where the custom inline snapshot matcher was used to update the snapshots properly.

```js
const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
async toMatchObservationInlineSnapshot(fn, ...rest) {
// The error (and its stacktrace) must be created before any `await`
this.error = new Error();

// The implementation of `observe` doesn't matter.
// It only matters that the custom snapshot matcher is async.
const observation = await observe(async () => {
await fn();
});

return toMatchInlineSnapshot.call(this, recording, ...rest);
},
});

it('observes something', async () => {
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot(`"async action"`);
*/
});
```

### `expect.anything()`

`expect.anything()` matches anything but `null` or `undefined`. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a non-null argument:
Expand Down

0 comments on commit a424896

Please sign in to comment.