Skip to content

Commit

Permalink
fix(jest-snapshot): fix typings of snapshot matchers (#13240)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrazauskas committed Sep 10, 2022
1 parent 0aa9b02 commit 63f8a99
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 148 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -3,11 +3,14 @@
### Features

- `[@jest/environment, jest-runtime]` Allow passing a generic type argument to `jest.createMockFromModule<T>()` method ([#13202](https://github.com/facebook/jest/pull/13202))
- `[expect]` Expose `ExpectationResult` type ([#13240])(https://github.com/facebook/jest/pull/13240))
- `[jest-snapshot]` Expose `Context` type ([#13240])(https://github.com/facebook/jest/pull/13240))
- `[@jest/globals]` Add `jest.Mock` type helper ([#13235](https://github.com/facebook/jest/pull/13235))

### Fixes

- `[jest-core]` Capture execError during `TestScheduler.scheduleTests` and dispatch to reporters ([#13203](https://github.com/facebook/jest/pull/13203))
- `[jest-snapshot]` Fix typings of snapshot matchers ([#13240])(https://github.com/facebook/jest/pull/13240))

### Chore & Maintenance

Expand Down
1 change: 1 addition & 0 deletions packages/expect/src/index.ts
Expand Up @@ -56,6 +56,7 @@ export type {
AsymmetricMatchers,
BaseExpect,
Expect,
ExpectationResult,
MatcherContext,
MatcherFunction,
MatcherFunctionWithContext,
Expand Down
147 changes: 147 additions & 0 deletions packages/jest-snapshot/__typetests__/matchers.test.ts
@@ -0,0 +1,147 @@
/**
* 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 {expectError, expectType} from 'tsd-lite';
import type {ExpectationResult} from 'expect';
import {
Context,
SnapshotState,
toMatchInlineSnapshot,
toMatchSnapshot,
toThrowErrorMatchingInlineSnapshot,
toThrowErrorMatchingSnapshot,
} from 'jest-snapshot';

// Context

expectType<SnapshotState>(({} as Context).snapshotState);

// toMatchSnapshot

expectType<ExpectationResult>(
toMatchSnapshot.call({} as Context, {received: 'value'}),
);

expectType<ExpectationResult>(
toMatchSnapshot.call({} as Context, {received: 'value'}, 'someHint'),
);

expectType<ExpectationResult>(
toMatchSnapshot.call({} as Context, {received: 'value'}, {property: 'match'}),
);

expectType<ExpectationResult>(
toMatchSnapshot.call(
{} as Context,
{received: 'value'},
{property: 'match'},
'someHint',
),
);

expectError(toMatchSnapshot({received: 'value'}));

// toMatchInlineSnapshot

expectType<ExpectationResult>(
toMatchInlineSnapshot.call({} as Context, {received: 'value'}),
);

expectType<ExpectationResult>(
toMatchInlineSnapshot.call(
{} as Context,
{received: 'value'},
'inlineSnapshot',
),
);

expectType<ExpectationResult>(
toMatchInlineSnapshot.call(
{} as Context,
{received: 'value'},
{property: 'match'},
),
);

expectType<ExpectationResult>(
toMatchInlineSnapshot.call(
{} as Context,
{received: 'value'},
{property: 'match'},
'inlineSnapshot',
),
);

expectError(toMatchInlineSnapshot({received: 'value'}));

// toThrowErrorMatchingSnapshot

expectType<ExpectationResult>(
toThrowErrorMatchingSnapshot.call({} as Context, new Error('received')),
);

expectType<ExpectationResult>(
toThrowErrorMatchingSnapshot.call(
{} as Context,
new Error('received'),
'someHint',
),
);

expectType<ExpectationResult>(
toThrowErrorMatchingSnapshot.call(
{} as Context,
new Error('received'),
'someHint',
true, // fromPromise
),
);

expectType<ExpectationResult>(
toThrowErrorMatchingSnapshot.call(
{} as Context,
new Error('received'),
undefined,
false, // fromPromise
),
);

expectError(toThrowErrorMatchingSnapshot({received: 'value'}));

// toThrowErrorMatchingInlineSnapshot

expectType<ExpectationResult>(
toThrowErrorMatchingInlineSnapshot.call({} as Context, new Error('received')),
);

expectType<ExpectationResult>(
toThrowErrorMatchingInlineSnapshot.call(
{} as Context,
new Error('received'),
'inlineSnapshot',
),
);

expectType<ExpectationResult>(
toThrowErrorMatchingInlineSnapshot.call(
{} as Context,
new Error('received'),
'inlineSnapshot',
true, // fromPromise
),
);

expectType<ExpectationResult>(
toThrowErrorMatchingInlineSnapshot.call(
{} as Context,
new Error('received'),
undefined,
false, // fromPromise
),
);

expectError(toThrowErrorMatchingInlineSnapshot({received: 'value'}));
20 changes: 10 additions & 10 deletions packages/jest-snapshot/src/__tests__/matcher.test.ts
Expand Up @@ -5,23 +5,23 @@
* LICENSE file in the root directory of this source tree.
*/

import {toMatchSnapshot} from '../';
import {type Context, toMatchSnapshot} from '../';

it('matcher returns matcher name, expected and actual values', () => {
const actual = 'a';
const expected = 'b';
const matcher = toMatchSnapshot.bind({
test('returns matcher name, expected and actual values', () => {
const mockedContext = {
snapshotState: {
match: (_testName: string, _received: unknown) => ({actual, expected}),
match: () => ({actual: 'a', expected: 'b'}),
},
});
} as unknown as Context;

const matcherResult = matcher({a: 1});
const matcherResult = toMatchSnapshot.call(mockedContext, {
a: 1,
});

expect(matcherResult).toEqual(
expect.objectContaining({
actual,
expected,
actual: 'a',
expected: 'b',
name: 'toMatchSnapshot',
}),
);
Expand Down
60 changes: 33 additions & 27 deletions packages/jest-snapshot/src/__tests__/throwMatcher.test.ts
Expand Up @@ -5,59 +5,65 @@
* LICENSE file in the root directory of this source tree.
*/

import {toThrowErrorMatchingSnapshot} from '..';
import {type Context, toThrowErrorMatchingSnapshot} from '../';

let matchFn: jest.Mock;
const mockedMatch = jest.fn(() => ({
actual: 'coconut',
expected: 'coconut',
}));

beforeEach(() => {
matchFn = jest.fn(() => ({
actual: 'coconut',
expected: 'coconut',
}));
const mockedContext = {
snapshotState: {match: mockedMatch},
} as unknown as Context;

afterEach(() => {
jest.clearAllMocks();
});

it('throw matcher can take func', () => {
const throwMatcher = toThrowErrorMatchingSnapshot.bind({
snapshotState: {match: matchFn},
});

throwMatcher(
toThrowErrorMatchingSnapshot.call(
mockedContext,
() => {
throw new Error('coconut');
},
undefined,
false,
);

expect(matchFn).toHaveBeenCalledWith(
expect(mockedMatch).toBeCalledTimes(1);
expect(mockedMatch).toHaveBeenCalledWith(
expect.objectContaining({received: 'coconut', testName: ''}),
);
});

describe('throw matcher from promise', () => {
let throwMatcher: typeof toThrowErrorMatchingSnapshot;

beforeEach(() => {
throwMatcher = toThrowErrorMatchingSnapshot.bind({
snapshotState: {match: matchFn},
});
});

it('can take error', () => {
throwMatcher(new Error('coconut'), 'testName', true);
toThrowErrorMatchingSnapshot.call(
mockedContext,
new Error('coco'),
'testName',
true,
);

expect(matchFn).toHaveBeenCalledWith(
expect.objectContaining({received: 'coconut', testName: ''}),
expect(mockedMatch).toBeCalledTimes(1);
expect(mockedMatch).toHaveBeenCalledWith(
expect.objectContaining({received: 'coco', testName: ''}),
);
});

it('can take custom error', () => {
class CustomError extends Error {}

throwMatcher(new CustomError('coconut'), 'testName', true);
toThrowErrorMatchingSnapshot.call(
mockedContext,
new CustomError('nut'),
'testName',
true,
);

expect(matchFn).toHaveBeenCalledWith(
expect.objectContaining({received: 'coconut', testName: ''}),
expect(mockedMatch).toBeCalledTimes(1);
expect(mockedMatch).toHaveBeenCalledWith(
expect.objectContaining({received: 'nut', testName: ''}),
);
});
});

0 comments on commit 63f8a99

Please sign in to comment.