Skip to content

chore(expect): use matcher utils via context instead of direct imports #13567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions packages/expect/src/extractExpectedAssertionsErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
*
*/

import {
EXPECTED_COLOR,
RECEIVED_COLOR,
matcherHint,
pluralize,
} from 'jest-matcher-utils';
import {equals, iterableEquality, subsetEquality} from '@jest/expect-utils';
import * as matcherUtils from 'jest-matcher-utils';
import {getState, setState} from './jestMatchersObject';
import type {Expect, ExpectedAssertionsErrors} from './types';
import type {
Expect,
ExpectedAssertionsErrors,
MatcherContext,
MatcherState,
MatcherUtils,
} from './types';

const resetAssertionsLocalState = () => {
setState({
Expand All @@ -24,10 +26,28 @@
});
};

const utils: MatcherUtils['utils'] = {
...matcherUtils,
iterableEquality,
subsetEquality,
};

const matcherUtilsThing: MatcherUtils = {

Check failure on line 35 in packages/expect/src/extractExpectedAssertionsErrors.ts

View workflow job for this annotation

GitHub Actions / Typecheck Examples and Tests

Property 'customTesters' is missing in type '{ dontThrow: () => void; equals: EqualsFunction; utils: typeof matcherUtils & { iterableEquality: Tester; subsetEquality: Tester; }; }' but required in type 'MatcherUtils'.

Check failure on line 35 in packages/expect/src/extractExpectedAssertionsErrors.ts

View workflow job for this annotation

GitHub Actions / TypeScript Compatibility

Property 'customTesters' is missing in type '{ dontThrow: () => void; equals: EqualsFunction; utils: typeof matcherUtils & { iterableEquality: Tester; subsetEquality: Tester; }; }' but required in type 'MatcherUtils'.
dontThrow: () => {

Check warning on line 36 in packages/expect/src/extractExpectedAssertionsErrors.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/extractExpectedAssertionsErrors.ts#L36

Added line #L36 was not covered by tests
// nothing
},
equals,
utils,
};

// Create and format all errors related to the mismatched number of `expect`
// calls and reset the matcher's state.
const extractExpectedAssertionsErrors: Expect['extractExpectedAssertionsErrors'] =
() => {
const matcherContext: MatcherContext = {

Check warning on line 47 in packages/expect/src/extractExpectedAssertionsErrors.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/extractExpectedAssertionsErrors.ts#L47

Added line #L47 was not covered by tests
...getState<MatcherState>(),
...matcherUtilsThing,
};
const result: ExpectedAssertionsErrors = [];
const {
assertionCalls,
Expand All @@ -43,16 +63,19 @@
typeof expectedAssertionsNumber === 'number' &&
assertionCalls !== expectedAssertionsNumber
) {
const numOfAssertionsExpected = EXPECTED_COLOR(
pluralize('assertion', expectedAssertionsNumber),
const numOfAssertionsExpected = matcherContext.utils.EXPECTED_COLOR(

Check warning on line 66 in packages/expect/src/extractExpectedAssertionsErrors.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/extractExpectedAssertionsErrors.ts#L66

Added line #L66 was not covered by tests
matcherContext.utils.pluralize('assertion', expectedAssertionsNumber),
);

expectedAssertionsNumberError!.message =
`${matcherHint('.assertions', '', expectedAssertionsNumber.toString(), {
isDirectExpectCall: true,
})}\n\n` +
`Expected ${numOfAssertionsExpected} to be called but received ${RECEIVED_COLOR(
pluralize('assertion call', assertionCalls || 0),
`${matcherContext.utils.matcherHint(
'.assertions',
'',
expectedAssertionsNumber.toString(),
{isDirectExpectCall: true},
)}\n\n` +
`Expected ${numOfAssertionsExpected} to be called but received ${matcherContext.utils.RECEIVED_COLOR(
matcherContext.utils.pluralize('assertion call', assertionCalls || 0),
)}.`;

result.push({
Expand All @@ -62,10 +85,12 @@
});
}
if (isExpectingAssertions && assertionCalls === 0) {
const expected = EXPECTED_COLOR('at least one assertion');
const received = RECEIVED_COLOR('received none');
const expected = matcherContext.utils.EXPECTED_COLOR(

Check warning on line 88 in packages/expect/src/extractExpectedAssertionsErrors.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/extractExpectedAssertionsErrors.ts#L88

Added line #L88 was not covered by tests
'at least one assertion',
);
const received = matcherContext.utils.RECEIVED_COLOR('received none');

Check warning on line 91 in packages/expect/src/extractExpectedAssertionsErrors.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/extractExpectedAssertionsErrors.ts#L91

Added line #L91 was not covered by tests

isExpectingAssertionsError!.message = `${matcherHint(
isExpectingAssertionsError!.message = `${matcherContext.utils.matcherHint(

Check warning on line 93 in packages/expect/src/extractExpectedAssertionsErrors.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/extractExpectedAssertionsErrors.ts#L93

Added line #L93 was not covered by tests
'.hasAssertions',
'',
'',
Expand Down
105 changes: 48 additions & 57 deletions packages/expect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
*
*/

/* eslint-disable local/prefer-spread-eventually */

import type {ChalkFunction} from 'chalk';
import {equals, iterableEquality, subsetEquality} from '@jest/expect-utils';
import * as matcherUtils from 'jest-matcher-utils';
import {ErrorWithStack, isPromise} from 'jest-util';
Expand Down Expand Up @@ -155,33 +154,32 @@
return expectation;
};

const getMessage = (message?: () => string) =>
const getMessage = (receivedColor: ChalkFunction, message?: () => string) =>
(message && message()) ||
matcherUtils.RECEIVED_COLOR('No message was specified for this matcher.');

const makeResolveMatcher =
(
matcherName: string,
matcher: RawMatcherFn,
isNot: boolean,
actual: Promise<any>,
outerErr: JestAssertionError,
): PromiseMatcherFn =>
(...args) => {
receivedColor('No message was specified for this matcher.');

Check warning on line 159 in packages/expect/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/index.ts#L159

Added line #L159 was not covered by tests

const makeResolveMatcher = (
matcherName: string,
matcher: RawMatcherFn,
isNot: boolean,
actual: Promise<any>,
outerErr: JestAssertionError,
): PromiseMatcherFn =>
function (...args) {
const options = {
isNot,
promise: 'resolves',
};

if (!isPromise(actual)) {
throw new JestAssertionError(
matcherUtils.matcherErrorMessage(
matcherUtils.matcherHint(matcherName, undefined, '', options),
`${matcherUtils.RECEIVED_COLOR('received')} value must be a promise`,
matcherUtils.printWithType(
this.utils.matcherErrorMessage(
this.utils.matcherHint(matcherName, undefined, '', options),
`${this.utils.RECEIVED_COLOR('received')} value must be a promise`,
this.utils.printWithType(
'Received',
actual,
matcherUtils.printReceived,
this.utils.printReceived,
),
),
);
Expand All @@ -192,33 +190,27 @@
return actual.then(
result =>
makeThrowingMatcher(matcher, isNot, 'resolves', result, innerErr).apply(
null,
this,
args,
),
error => {
outerErr.message =
`${matcherUtils.matcherHint(
matcherName,
undefined,
'',
options,
)}\n\n` +
`${this.utils.matcherHint(matcherName, undefined, '', options)}\n\n` +
'Received promise rejected instead of resolved\n' +
`Rejected to value: ${matcherUtils.printReceived(error)}`;
`Rejected to value: ${this.utils.printReceived(error)}`;
throw outerErr;
},
);
};

const makeRejectMatcher =
(
matcherName: string,
matcher: RawMatcherFn,
isNot: boolean,
actual: Promise<any> | (() => Promise<any>),
outerErr: JestAssertionError,
): PromiseMatcherFn =>
(...args) => {
const makeRejectMatcher = (
matcherName: string,
matcher: RawMatcherFn,
isNot: boolean,
actual: Promise<any> | (() => Promise<any>),
outerErr: JestAssertionError,
): PromiseMatcherFn =>
function (...args) {
const options = {
isNot,
promise: 'rejects',
Expand All @@ -229,15 +221,15 @@

if (!isPromise(actualWrapper)) {
throw new JestAssertionError(
matcherUtils.matcherErrorMessage(
matcherUtils.matcherHint(matcherName, undefined, '', options),
`${matcherUtils.RECEIVED_COLOR(
this.utils.matcherErrorMessage(
this.utils.matcherHint(matcherName, undefined, '', options),
`${this.utils.RECEIVED_COLOR(
'received',
)} value must be a promise or a function returning a promise`,
matcherUtils.printWithType(
this.utils.printWithType(
'Received',
actual,
matcherUtils.printReceived,
this.utils.printReceived,
),
),
);
Expand All @@ -248,24 +240,25 @@
return actualWrapper.then(
result => {
outerErr.message =
`${matcherUtils.matcherHint(
matcherName,
undefined,
'',
options,
)}\n\n` +
`${this.utils.matcherHint(matcherName, undefined, '', options)}\n\n` +
'Received promise resolved instead of rejected\n' +
`Resolved to value: ${matcherUtils.printReceived(result)}`;
`Resolved to value: ${this.utils.printReceived(result)}`;
throw outerErr;
},
error =>
makeThrowingMatcher(matcher, isNot, 'rejects', error, innerErr).apply(
null,
this,
args,
),
);
};

const utils: MatcherUtils['utils'] = Object.freeze({
...matcherUtils,
iterableEquality,
subsetEquality,
});

const makeThrowingMatcher = (
matcher: RawMatcherFn,
isNot: boolean,
Expand All @@ -275,11 +268,6 @@
): ThrowingMatcherFn =>
function throwingMatcher(...args): any {
let throws = true;
const utils: MatcherUtils['utils'] = {
...matcherUtils,
iterableEquality,
subsetEquality,
};

const matcherUtilsThing: MatcherUtils = {
customTesters: getCustomEqualityTesters(),
Expand All @@ -305,7 +293,7 @@
result: SyncExpectationResult,
asyncError?: JestAssertionError,
) => {
_validateResult(result);
_validateResult(this.utils.stringify, result);

getState().assertionCalls++;

Expand Down Expand Up @@ -413,7 +401,10 @@
expect.stringContaining = stringContaining;
expect.stringMatching = stringMatching;

const _validateResult = (result: any) => {
const _validateResult = (
stringify: (typeof matcherUtils)['stringify'],
result: any,
) => {

Check warning on line 407 in packages/expect/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/expect/src/index.ts#L407

Added line #L407 was not covered by tests
if (
typeof result !== 'object' ||
typeof result.pass !== 'boolean' ||
Expand All @@ -426,7 +417,7 @@
'Matcher functions should ' +
'return an object in the following format:\n' +
' {message?: string | function, pass: boolean}\n' +
`'${matcherUtils.stringify(result)}' was returned`,
`'${stringify(result)}' was returned`,
);
}
};
Expand Down
Loading
Loading