diff --git a/packages/expect/src/extractExpectedAssertionsErrors.ts b/packages/expect/src/extractExpectedAssertionsErrors.ts index 8c7972e1c2aa..c245063d0356 100644 --- a/packages/expect/src/extractExpectedAssertionsErrors.ts +++ b/packages/expect/src/extractExpectedAssertionsErrors.ts @@ -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({ @@ -24,10 +26,28 @@ const resetAssertionsLocalState = () => { }); }; +const utils: MatcherUtils['utils'] = { + ...matcherUtils, + iterableEquality, + subsetEquality, +}; + +const matcherUtilsThing: MatcherUtils = { + dontThrow: () => { + // 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 = { + ...getState(), + ...matcherUtilsThing, + }; const result: ExpectedAssertionsErrors = []; const { assertionCalls, @@ -43,16 +63,19 @@ const extractExpectedAssertionsErrors: Expect['extractExpectedAssertionsErrors'] typeof expectedAssertionsNumber === 'number' && assertionCalls !== expectedAssertionsNumber ) { - const numOfAssertionsExpected = EXPECTED_COLOR( - pluralize('assertion', expectedAssertionsNumber), + const numOfAssertionsExpected = matcherContext.utils.EXPECTED_COLOR( + 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({ @@ -62,10 +85,12 @@ const extractExpectedAssertionsErrors: Expect['extractExpectedAssertionsErrors'] }); } if (isExpectingAssertions && assertionCalls === 0) { - const expected = EXPECTED_COLOR('at least one assertion'); - const received = RECEIVED_COLOR('received none'); + const expected = matcherContext.utils.EXPECTED_COLOR( + 'at least one assertion', + ); + const received = matcherContext.utils.RECEIVED_COLOR('received none'); - isExpectingAssertionsError!.message = `${matcherHint( + isExpectingAssertionsError!.message = `${matcherContext.utils.matcherHint( '.hasAssertions', '', '', diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index 6c773452deee..29c42856d4dd 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -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'; @@ -155,19 +154,18 @@ export const expect: Expect = (actual: any, ...rest: Array) => { 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, - outerErr: JestAssertionError, - ): PromiseMatcherFn => - (...args) => { + receivedColor('No message was specified for this matcher.'); + +const makeResolveMatcher = ( + matcherName: string, + matcher: RawMatcherFn, + isNot: boolean, + actual: Promise, + outerErr: JestAssertionError, +): PromiseMatcherFn => + function (...args) { const options = { isNot, promise: 'resolves', @@ -175,13 +173,13 @@ const makeResolveMatcher = 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, ), ), ); @@ -192,33 +190,27 @@ const makeResolveMatcher = 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 | (() => Promise), - outerErr: JestAssertionError, - ): PromiseMatcherFn => - (...args) => { +const makeRejectMatcher = ( + matcherName: string, + matcher: RawMatcherFn, + isNot: boolean, + actual: Promise | (() => Promise), + outerErr: JestAssertionError, +): PromiseMatcherFn => + function (...args) { const options = { isNot, promise: 'rejects', @@ -229,15 +221,15 @@ const makeRejectMatcher = 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, ), ), ); @@ -248,24 +240,25 @@ const makeRejectMatcher = 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, @@ -275,11 +268,6 @@ const makeThrowingMatcher = ( ): ThrowingMatcherFn => function throwingMatcher(...args): any { let throws = true; - const utils: MatcherUtils['utils'] = { - ...matcherUtils, - iterableEquality, - subsetEquality, - }; const matcherUtilsThing: MatcherUtils = { customTesters: getCustomEqualityTesters(), @@ -305,7 +293,7 @@ const makeThrowingMatcher = ( result: SyncExpectationResult, asyncError?: JestAssertionError, ) => { - _validateResult(result); + _validateResult(this.utils.stringify, result); getState().assertionCalls++; @@ -413,7 +401,10 @@ expect.objectContaining = objectContaining; expect.stringContaining = stringContaining; expect.stringMatching = stringMatching; -const _validateResult = (result: any) => { +const _validateResult = ( + stringify: (typeof matcherUtils)['stringify'], + result: any, +) => { if ( typeof result !== 'object' || typeof result.pass !== 'boolean' || @@ -426,7 +417,7 @@ const _validateResult = (result: any) => { '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`, ); } }; diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index d5e3431613d8..77651afbae1f 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -18,24 +18,7 @@ import { typeEquality, } from '@jest/expect-utils'; import {getType, isPrimitive} from '@jest/get-type'; -import { - DIM_COLOR, - EXPECTED_COLOR, - type MatcherHintOptions, - RECEIVED_COLOR, - SUGGEST_TO_CONTAIN_EQUAL, - ensureExpectedIsNonNegativeInteger, - ensureNoExpected, - ensureNumbers, - getLabelPrinter, - matcherErrorMessage, - matcherHint, - printDiffOrStringify, - printExpected, - printReceived, - printWithType, - stringify, -} from 'jest-matcher-utils'; +import type {MatcherHintOptions} from 'jest-matcher-utils'; import { printCloseTo, printExpectedConstructorName, @@ -85,9 +68,9 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected: not ${printExpected(expected)}` + `Expected: not ${this.utils.printExpected(expected)}` : () => { const expectedType = getType(expected); @@ -116,14 +99,14 @@ const matchers: MatchersObject = { return ( // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + (deepEqualityName === null ? '' - : `${DIM_COLOR( + : `${this.utils.DIM_COLOR( `If it should pass with deep equality, replace "${matcherName}" with "${deepEqualityName}"`, )}\n\n`) + - printDiffOrStringify( + this.utils.printDiffOrStringify( expected, received, EXPECTED_LABEL, @@ -152,20 +135,28 @@ const matchers: MatchersObject = { if (typeof expected !== 'number') { throw new TypeError( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${EXPECTED_COLOR('expected')} value must be a number`, - printWithType('Expected', expected, printExpected), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.EXPECTED_COLOR('expected')} value must be a number`, + this.utils.printWithType( + 'Expected', + expected, + this.utils.printExpected, + ), ), ); } if (typeof received !== 'number') { throw new TypeError( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${RECEIVED_COLOR('received')} value must be a number`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.RECEIVED_COLOR('received')} value must be a number`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } @@ -193,19 +184,19 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected: not ${printExpected(expected)}\n` + + `Expected: not ${this.utils.printExpected(expected)}\n` + (receivedDiff === 0 ? '' - : `Received: ${printReceived(received)}\n` + + : `Received: ${this.utils.printReceived(received)}\n` + `\n${printCloseTo(receivedDiff, expectedDiff, precision, isNot)}`) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + - `Received: ${printReceived(received)}\n` + + `Expected: ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(received)}\n` + '\n' + printCloseTo(receivedDiff, expectedDiff, precision, isNot); @@ -218,15 +209,15 @@ const matchers: MatchersObject = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName, options); + this.utils.ensureNoExpected(expected, matcherName, options); const pass = received !== void 0; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + this.utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + - `Received: ${printReceived(received)}`; + `Received: ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -237,15 +228,15 @@ const matchers: MatchersObject = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName, options); + this.utils.ensureNoExpected(expected, matcherName, options); const pass = !received; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + this.utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + - `Received: ${printReceived(received)}`; + `Received: ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -257,16 +248,18 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, matcherName, options); + this.utils.ensureNumbers(received, expected, matcherName, options); const pass = received > expected; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected:${isNot ? ' not' : ''} > ${printExpected(expected)}\n` + - `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + `Expected:${isNot ? ' not' : ''} > ${this.utils.printExpected( + expected, + )}\n` + + `Received:${isNot ? ' ' : ''} ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -278,16 +271,20 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, matcherName, options); + this.utils.ensureNumbers(received, expected, matcherName, options); const pass = received >= expected; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected:${isNot ? ' not' : ''} >= ${printExpected(expected)}\n` + - `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + `Expected:${isNot ? ' not' : ''} >= ${this.utils.printExpected( + expected, + )}\n` + + `Received:${isNot ? ' ' : ''} ${this.utils.printReceived( + received, + )}`; return {message, pass}; }, @@ -301,10 +298,14 @@ const matchers: MatchersObject = { if (typeof expected !== 'function') { throw new TypeError( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${EXPECTED_COLOR('expected')} value must be a function`, - printWithType('Expected', expected, printExpected), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.EXPECTED_COLOR('expected')} value must be a function`, + this.utils.printWithType( + 'Expected', + expected, + this.utils.printExpected, + ), ), ); } @@ -314,7 +315,7 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + printExpectedConstructorNameNot('Expected constructor', expected) + (typeof received.constructor === 'function' && @@ -327,11 +328,11 @@ const matchers: MatchersObject = { : '') : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + printExpectedConstructorName('Expected constructor', expected) + (isPrimitive(received) || Object.getPrototypeOf(received) === null - ? `\nReceived value has no prototype\nReceived value: ${printReceived( + ? `\nReceived value has no prototype\nReceived value: ${this.utils.printReceived( received, )}` : typeof received.constructor === 'function' @@ -339,7 +340,7 @@ const matchers: MatchersObject = { 'Received constructor', received.constructor, ) - : `\nReceived value: ${printReceived(received)}`); + : `\nReceived value: ${this.utils.printReceived(received)}`); return {message, pass}; }, @@ -351,16 +352,18 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, matcherName, options); + this.utils.ensureNumbers(received, expected, matcherName, options); const pass = received < expected; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected:${isNot ? ' not' : ''} < ${printExpected(expected)}\n` + - `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + `Expected:${isNot ? ' not' : ''} < ${this.utils.printExpected( + expected, + )}\n` + + `Received:${isNot ? ' ' : ''} ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -372,16 +375,20 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, matcherName, options); + this.utils.ensureNumbers(received, expected, matcherName, options); const pass = received <= expected; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected:${isNot ? ' not' : ''} <= ${printExpected(expected)}\n` + - `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + `Expected:${isNot ? ' not' : ''} <= ${this.utils.printExpected( + expected, + )}\n` + + `Received:${isNot ? ' ' : ''} ${this.utils.printReceived( + received, + )}`; return {message, pass}; }, @@ -392,15 +399,15 @@ const matchers: MatchersObject = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName, options); + this.utils.ensureNoExpected(expected, matcherName, options); const pass = Number.isNaN(received); const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + this.utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + - `Received: ${printReceived(received)}`; + `Received: ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -411,15 +418,15 @@ const matchers: MatchersObject = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName, options); + this.utils.ensureNoExpected(expected, matcherName, options); const pass = received === null; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + this.utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + - `Received: ${printReceived(received)}`; + `Received: ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -430,15 +437,15 @@ const matchers: MatchersObject = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName, options); + this.utils.ensureNoExpected(expected, matcherName, options); const pass = !!received; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + this.utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + - `Received: ${printReceived(received)}`; + `Received: ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -449,15 +456,15 @@ const matchers: MatchersObject = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName, options); + this.utils.ensureNoExpected(expected, matcherName, options); const pass = received === void 0; const message = () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + this.utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + - `Received: ${printReceived(received)}`; + `Received: ${this.utils.printReceived(received)}`; return {message, pass}; }, @@ -473,30 +480,49 @@ const matchers: MatchersObject = { if (received == null) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${RECEIVED_COLOR('received')} value must not be null nor undefined`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.RECEIVED_COLOR( + 'received', + )} value must not be null nor undefined`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } if (typeof received === 'string') { - const wrongTypeErrorMessage = `${EXPECTED_COLOR( + const wrongTypeErrorMessage = `${this.utils.EXPECTED_COLOR( 'expected', - )} value must be a string if ${RECEIVED_COLOR( + )} value must be a string if ${this.utils.RECEIVED_COLOR( 'received', )} value is a string`; if (typeof expected !== 'string') { throw new TypeError( - matcherErrorMessage( - matcherHint(matcherName, received, String(expected), options), + this.utils.matcherErrorMessage( + this.utils.matcherHint( + matcherName, + received, + String(expected), + options, + ), wrongTypeErrorMessage, // eslint-disable-next-line prefer-template - printWithType('Expected', expected, printExpected) + + this.utils.printWithType( + 'Expected', + expected, + this.utils.printExpected, + ) + '\n' + - printWithType('Received', received, printReceived), + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } @@ -509,15 +535,18 @@ const matchers: MatchersObject = { typeof expected === 'string' ? 'substring' : 'value' }`; const labelReceived = 'Received string'; - const printLabel = getLabelPrinter(labelExpected, labelReceived); + const printLabel = this.utils.getLabelPrinter( + labelExpected, + labelReceived, + ); return ( // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected( - expected, - )}\n` + + `${printLabel(labelExpected)}${ + isNot ? 'not ' : '' + }${this.utils.printExpected(expected)}\n` + `${printLabel(labelReceived)}${isNot ? ' ' : ''}${ isNot ? printReceivedStringContainExpectedSubstring( @@ -525,7 +554,7 @@ const matchers: MatchersObject = { index, String(expected).length, ) - : printReceived(received) + : this.utils.printReceived(received) }` ); }; @@ -540,25 +569,28 @@ const matchers: MatchersObject = { const message = () => { const labelExpected = 'Expected value'; const labelReceived = `Received ${getType(received)}`; - const printLabel = getLabelPrinter(labelExpected, labelReceived); + const printLabel = this.utils.getLabelPrinter( + labelExpected, + labelReceived, + ); return ( // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected( - expected, - )}\n` + + `${printLabel(labelExpected)}${ + isNot ? 'not ' : '' + }${this.utils.printExpected(expected)}\n` + `${printLabel(labelReceived)}${isNot ? ' ' : ''}${ isNot && Array.isArray(received) ? printReceivedArrayContainExpectedItem(received, index) - : printReceived(received) + : this.utils.printReceived(received) }` + (!isNot && indexable.some(item => equals(item, expected, [...this.customTesters, iterableEquality]), ) - ? `\n\n${SUGGEST_TO_CONTAIN_EQUAL}` + ? `\n\n${this.utils.SUGGEST_TO_CONTAIN_EQUAL}` : '') ); }; @@ -577,10 +609,16 @@ const matchers: MatchersObject = { if (received == null) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${RECEIVED_COLOR('received')} value must not be null nor undefined`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.RECEIVED_COLOR( + 'received', + )} value must not be null nor undefined`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } @@ -593,19 +631,22 @@ const matchers: MatchersObject = { const message = () => { const labelExpected = 'Expected value'; const labelReceived = `Received ${getType(received)}`; - const printLabel = getLabelPrinter(labelExpected, labelReceived); + const printLabel = this.utils.getLabelPrinter( + labelExpected, + labelReceived, + ); return ( // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected( - expected, - )}\n` + + `${printLabel(labelExpected)}${ + isNot ? 'not ' : '' + }${this.utils.printExpected(expected)}\n` + `${printLabel(labelReceived)}${isNot ? ' ' : ''}${ isNot && Array.isArray(received) ? printReceivedArrayContainExpectedItem(received, index) - : printReceived(received) + : this.utils.printReceived(received) }` ); }; @@ -629,17 +670,17 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected: not ${printExpected(expected)}\n` + - (stringify(expected) === stringify(received) + `Expected: not ${this.utils.printExpected(expected)}\n` + + (this.utils.stringify(expected) === this.utils.stringify(received) ? '' - : `Received: ${printReceived(received)}`) + : `Received: ${this.utils.printReceived(received)}`) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - printDiffOrStringify( + this.utils.printDiffOrStringify( expected, received, EXPECTED_LABEL, @@ -663,17 +704,25 @@ const matchers: MatchersObject = { if (typeof received?.length !== 'number') { throw new TypeError( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${RECEIVED_COLOR( + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.RECEIVED_COLOR( 'received', )} value must have a length property whose value must be a number`, - printWithType('Received', received, printReceived), + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } - ensureExpectedIsNonNegativeInteger(expected, matcherName, options); + this.utils.ensureExpectedIsNonNegativeInteger( + expected, + matcherName, + options, + ); const pass = received.length === expected; @@ -681,7 +730,7 @@ const matchers: MatchersObject = { const labelExpected = 'Expected length'; const labelReceivedLength = 'Received length'; const labelReceivedValue = `Received ${getType(received)}`; - const printLabel = getLabelPrinter( + const printLabel = this.utils.getLabelPrinter( labelExpected, labelReceivedLength, labelReceivedValue, @@ -689,19 +738,19 @@ const matchers: MatchersObject = { return ( // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected( - expected, - )}\n` + + `${printLabel(labelExpected)}${ + isNot ? 'not ' : '' + }${this.utils.printExpected(expected)}\n` + (isNot ? '' - : `${printLabel(labelReceivedLength)}${printReceived( + : `${printLabel(labelReceivedLength)}${this.utils.printReceived( received.length, )}\n`) + - `${printLabel(labelReceivedValue)}${isNot ? ' ' : ''}${printReceived( - received, - )}` + `${printLabel(labelReceivedValue)}${ + isNot ? ' ' : '' + }${this.utils.printReceived(received)}` ); }; @@ -724,10 +773,21 @@ const matchers: MatchersObject = { if (received === null || received === undefined) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, expectedArgument, options), - `${RECEIVED_COLOR('received')} value must not be null nor undefined`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint( + matcherName, + undefined, + expectedArgument, + options, + ), + `${this.utils.RECEIVED_COLOR( + 'received', + )} value must not be null nor undefined`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } @@ -736,10 +796,21 @@ const matchers: MatchersObject = { if (expectedPathType !== 'string' && expectedPathType !== 'array') { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, expectedArgument, options), - `${EXPECTED_COLOR('expected')} path must be a string or array`, - printWithType('Expected', expectedPath, printExpected), + this.utils.matcherErrorMessage( + this.utils.matcherHint( + matcherName, + undefined, + expectedArgument, + options, + ), + `${this.utils.EXPECTED_COLOR( + 'expected', + )} path must be a string or array`, + this.utils.printWithType( + 'Expected', + expectedPath, + this.utils.printExpected, + ), ), ); } @@ -751,10 +822,21 @@ const matchers: MatchersObject = { if (expectedPathType === 'array' && expectedPathLength === 0) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, expectedArgument, options), - `${EXPECTED_COLOR('expected')} path must not be an empty array`, - printWithType('Expected', expectedPath, printExpected), + this.utils.matcherErrorMessage( + this.utils.matcherHint( + matcherName, + undefined, + expectedArgument, + options, + ), + `${this.utils.EXPECTED_COLOR( + 'expected', + )} path must not be an empty array`, + this.utils.printWithType( + 'Expected', + expectedPath, + this.utils.printExpected, + ), ), ); } @@ -776,39 +858,54 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, expectedArgument, options) + + this.utils.matcherHint( + matcherName, + undefined, + expectedArgument, + options, + ) + '\n\n' + (hasValue - ? `Expected path: ${printExpected(expectedPath)}\n\n` + - `Expected value: not ${printExpected(expectedValue)}${ - stringify(expectedValue) === stringify(receivedValue) + ? `Expected path: ${this.utils.printExpected(expectedPath)}\n\n` + + `Expected value: not ${this.utils.printExpected(expectedValue)}${ + this.utils.stringify(expectedValue) === + this.utils.stringify(receivedValue) ? '' - : `\nReceived value: ${printReceived(receivedValue)}` + : `\nReceived value: ${this.utils.printReceived(receivedValue)}` }` - : `Expected path: not ${printExpected(expectedPath)}\n\n` + - `Received value: ${printReceived(receivedValue)}`) + : `Expected path: not ${this.utils.printExpected( + expectedPath, + )}\n\n` + + `Received value: ${this.utils.printReceived(receivedValue)}`) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, expectedArgument, options) + + this.utils.matcherHint( + matcherName, + undefined, + expectedArgument, + options, + ) + '\n\n' + - `Expected path: ${printExpected(expectedPath)}\n` + + `Expected path: ${this.utils.printExpected(expectedPath)}\n` + (hasCompletePath - ? `\n${printDiffOrStringify( + ? `\n${this.utils.printDiffOrStringify( expectedValue, receivedValue, EXPECTED_VALUE_LABEL, RECEIVED_VALUE_LABEL, isExpand(this.expand), )}` - : `Received path: ${printReceived( + : `Received path: ${this.utils.printReceived( expectedPathType === 'array' || receivedPath.length === 0 ? receivedPath : receivedPath.join('.'), )}\n\n${ hasValue - ? `Expected value: ${printExpected(expectedValue)}\n` + ? `Expected value: ${this.utils.printExpected( + expectedValue, + )}\n` : '' - }Received value: ${printReceived(receivedValue)}`); + }Received value: ${this.utils.printReceived(receivedValue)}`); return {message, pass}; }, @@ -822,10 +919,14 @@ const matchers: MatchersObject = { if (typeof received !== 'string') { throw new TypeError( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${RECEIVED_COLOR('received')} value must be a string`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.RECEIVED_COLOR('received')} value must be a string`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } @@ -835,12 +936,16 @@ const matchers: MatchersObject = { !(expected && typeof expected.test === 'function') ) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${EXPECTED_COLOR( + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.EXPECTED_COLOR( 'expected', )} value must be a string or regular expression`, - printWithType('Expected', expected, printExpected), + this.utils.printWithType( + 'Expected', + expected, + this.utils.printExpected, + ), ), ); } @@ -854,18 +959,30 @@ const matchers: MatchersObject = { ? () => typeof expected === 'string' ? // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint( + matcherName, + undefined, + undefined, + options, + ) + '\n\n' + - `Expected substring: not ${printExpected(expected)}\n` + + `Expected substring: not ${this.utils.printExpected( + expected, + )}\n` + `Received string: ${printReceivedStringContainExpectedSubstring( received, received.indexOf(expected), expected.length, )}` : // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint( + matcherName, + undefined, + undefined, + options, + ) + '\n\n' + - `Expected pattern: not ${printExpected(expected)}\n` + + `Expected pattern: not ${this.utils.printExpected(expected)}\n` + `Received string: ${printReceivedStringContainExpectedResult( received, typeof expected.exec === 'function' @@ -877,14 +994,19 @@ const matchers: MatchersObject = { typeof expected === 'string' ? 'substring' : 'pattern' }`; const labelReceived = 'Received string'; - const printLabel = getLabelPrinter(labelExpected, labelReceived); + const printLabel = this.utils.getLabelPrinter( + labelExpected, + labelReceived, + ); return ( // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `${printLabel(labelExpected)}${printExpected(expected)}\n` + - `${printLabel(labelReceived)}${printReceived(received)}` + `${printLabel(labelExpected)}${this.utils.printExpected( + expected, + )}\n` + + `${printLabel(labelReceived)}${this.utils.printReceived(received)}` ); }; @@ -900,20 +1022,32 @@ const matchers: MatchersObject = { if (typeof received !== 'object' || received === null) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${RECEIVED_COLOR('received')} value must be a non-null object`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.RECEIVED_COLOR( + 'received', + )} value must be a non-null object`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } if (typeof expected !== 'object' || expected === null) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${EXPECTED_COLOR('expected')} value must be a non-null object`, - printWithType('Expected', expected, printExpected), + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.EXPECTED_COLOR( + 'expected', + )} value must be a non-null object`, + this.utils.printWithType( + 'Expected', + expected, + this.utils.printExpected, + ), ), ); } @@ -927,17 +1061,17 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected: not ${printExpected(expected)}` + - (stringify(expected) === stringify(received) + `Expected: not ${this.utils.printExpected(expected)}` + + (this.utils.stringify(expected) === this.utils.stringify(received) ? '' - : `\nReceived: ${printReceived(received)}`) + : `\nReceived: ${this.utils.printReceived(received)}`) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - printDiffOrStringify( + this.utils.printDiffOrStringify( expected, getObjectSubset(received, expected, this.customTesters), EXPECTED_LABEL, @@ -966,17 +1100,17 @@ const matchers: MatchersObject = { const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - `Expected: not ${printExpected(expected)}\n` + - (stringify(expected) === stringify(received) + `Expected: not ${this.utils.printExpected(expected)}\n` + + (this.utils.stringify(expected) === this.utils.stringify(received) ? '' - : `Received: ${printReceived(received)}`) + : `Received: ${this.utils.printReceived(received)}`) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + this.utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - printDiffOrStringify( + this.utils.printDiffOrStringify( expected, received, EXPECTED_LABEL, diff --git a/packages/expect/src/print.ts b/packages/expect/src/print.ts index 2a8de8cf59f8..ed064fc07cb0 100644 --- a/packages/expect/src/print.ts +++ b/packages/expect/src/print.ts @@ -6,6 +6,7 @@ * */ +// TODO: fix import { EXPECTED_COLOR, INVERTED_COLOR, diff --git a/packages/expect/src/spyMatchers.ts b/packages/expect/src/spyMatchers.ts index bf4966eeecbf..d941bf22133f 100644 --- a/packages/expect/src/spyMatchers.ts +++ b/packages/expect/src/spyMatchers.ts @@ -9,21 +9,7 @@ import {equals, iterableEquality} from '@jest/expect-utils'; import {getType, isPrimitive} from '@jest/get-type'; -import { - DIM_COLOR, - EXPECTED_COLOR, - type MatcherHintOptions, - RECEIVED_COLOR, - diff, - ensureExpectedIsNonNegativeInteger, - ensureNoExpected, - matcherErrorMessage, - matcherHint, - printExpected, - printReceived, - printWithType, - stringify, -} from 'jest-matcher-utils'; +import type * as MatcherUtils from 'jest-matcher-utils'; import {getCustomEqualityTesters} from './jestMatchersObject'; import type { MatcherFunction, @@ -38,12 +24,16 @@ const PRINT_LIMIT = 3; const NO_ARGUMENTS = 'called with 0 arguments'; -const printExpectedArgs = (expected: Array): string => +const printExpectedArgs = ( + utils: typeof MatcherUtils, + expected: Array, +): string => expected.length === 0 ? NO_ARGUMENTS - : expected.map(arg => printExpected(arg)).join(', '); + : expected.map(arg => utils.printExpected(arg)).join(', '); const printReceivedArgs = ( + utils: typeof MatcherUtils, received: Array, expected?: Array, ): string => @@ -54,12 +44,13 @@ const printReceivedArgs = ( Array.isArray(expected) && i < expected.length && isEqualValue(expected[i], arg) - ? printCommon(arg) - : printReceived(arg), + ? printCommon(utils, arg) + : utils.printReceived(arg), ) .join(', '); -const printCommon = (val: unknown) => DIM_COLOR(stringify(val)); +const printCommon = (utils: typeof MatcherUtils, val: unknown) => + utils.DIM_COLOR(utils.stringify(val)); const isEqualValue = (expected: unknown, received: unknown): boolean => equals(expected, received, [...getCustomEqualityTesters(), iterableEquality]); @@ -80,13 +71,14 @@ const countReturns = (results: Array): number => ); const printNumberOfReturns = ( + utils: typeof MatcherUtils, countReturns: number, countCalls: number, ): string => - `\nNumber of returns: ${printReceived(countReturns)}${ + `\nNumber of returns: ${utils.printReceived(countReturns)}${ countCalls === countReturns ? '' - : `\nNumber of calls: ${printReceived(countCalls)}` + : `\nNumber of calls: ${utils.printReceived(countCalls)}` }`; type PrintLabel = (string: string, isExpectedCall: boolean) => string; @@ -109,6 +101,7 @@ const getRightAlignedPrinter = (label: string): PrintLabel => { type IndexedCall = [number, Array]; const printReceivedCallsNegative = ( + utils: typeof MatcherUtils, expected: Array, indexedCalls: Array, isOnlyCall: boolean, @@ -120,7 +113,7 @@ const printReceivedCallsNegative = ( const label = 'Received: '; if (isOnlyCall) { - return `${label + printReceivedArgs(indexedCalls[0], expected)}\n`; + return `${label + printReceivedArgs(utils, indexedCalls[0], expected)}\n`; } const printAligned = getRightAlignedPrinter(label); @@ -130,20 +123,21 @@ const printReceivedCallsNegative = ( `${ printed + printAligned(String(i + 1), i === iExpectedCall) + - printReceivedArgs(args, expected) + printReceivedArgs(utils, args, expected) }\n`, '', )}`; }; const printExpectedReceivedCallsPositive = ( + utils: typeof MatcherUtils, expected: Array, indexedCalls: Array, expand: boolean, isOnlyCall: boolean, iExpectedCall?: number, ) => { - const expectedLine = `Expected: ${printExpectedArgs(expected)}\n`; + const expectedLine = `Expected: ${printExpectedArgs(utils, expected)}\n`; if (indexedCalls.length === 0) { return expectedLine; } @@ -155,8 +149,8 @@ const printExpectedReceivedCallsPositive = ( if (isLineDiffableCall(expected, received)) { // Display diff without indentation. const lines = [ - EXPECTED_COLOR('- Expected'), - RECEIVED_COLOR('+ Received'), + utils.EXPECTED_COLOR('- Expected'), + utils.RECEIVED_COLOR('+ Received'), '', ]; @@ -164,12 +158,12 @@ const printExpectedReceivedCallsPositive = ( for (let i = 0; i < length; i += 1) { if (i < expected.length && i < received.length) { if (isEqualValue(expected[i], received[i])) { - lines.push(` ${printCommon(received[i])},`); + lines.push(` ${printCommon(utils, received[i])},`); continue; } if (isLineDiffableArg(expected[i], received[i])) { - const difference = diff(expected[i], received[i], {expand}); + const difference = utils.diff(expected[i], received[i], {expand}); if ( typeof difference === 'string' && difference.includes('- Expected') && @@ -183,17 +177,23 @@ const printExpectedReceivedCallsPositive = ( } if (i < expected.length) { - lines.push(`${EXPECTED_COLOR(`- ${stringify(expected[i])}`)},`); + lines.push( + `${utils.EXPECTED_COLOR(`- ${utils.stringify(expected[i])}`)},`, + ); } if (i < received.length) { - lines.push(`${RECEIVED_COLOR(`+ ${stringify(received[i])}`)},`); + lines.push( + `${utils.RECEIVED_COLOR(`+ ${utils.stringify(received[i])}`)},`, + ); } } return `${lines.join('\n')}\n`; } - return `${expectedLine + label + printReceivedArgs(received, expected)}\n`; + return `${ + expectedLine + label + printReceivedArgs(utils, received, expected) + }\n`; } const printAligned = getRightAlignedPrinter(label); @@ -209,8 +209,8 @@ const printExpectedReceivedCallsPositive = ( ((i === iExpectedCall || iExpectedCall === undefined) && isLineDiffableCall(expected, received) ? aligned.replace(': ', '\n') + - printDiffCall(expected, received, expand) - : aligned + printReceivedArgs(received, expected)) + printDiffCall(utils, expected, received, expand) + : aligned + printReceivedArgs(utils, received, expected)) }\n`; }, '') ); @@ -219,6 +219,7 @@ const printExpectedReceivedCallsPositive = ( const indentation = 'Received'.replaceAll(/\w/g, ' '); const printDiffCall = ( + utils: typeof MatcherUtils, expected: Array, received: Array, expand: boolean, @@ -227,11 +228,11 @@ const printDiffCall = ( .map((arg, i) => { if (i < expected.length) { if (isEqualValue(expected[i], arg)) { - return `${indentation} ${printCommon(arg)},`; + return `${indentation} ${printCommon(utils, arg)},`; } if (isLineDiffableArg(expected[i], arg)) { - const difference = diff(expected[i], arg, {expand}); + const difference = utils.diff(expected[i], arg, {expand}); if ( typeof difference === 'string' && @@ -253,8 +254,8 @@ const printDiffCall = ( return `${ indentation + (i < expected.length - ? ` ${printReceived(arg)}` - : RECEIVED_COLOR(`+ ${stringify(arg)}`)) + ? ` ${utils.printReceived(arg)}` + : utils.RECEIVED_COLOR(`+ ${utils.stringify(arg)}`)) },`; }) .join('\n'); @@ -310,20 +311,25 @@ const isLineDiffableArg = (expected: unknown, received: unknown): boolean => { return true; }; -const printResult = (result: any, expected: unknown) => +const printResult = ( + utils: typeof MatcherUtils, + result: any, + expected: unknown, +) => result.type === 'throw' ? 'function call threw an error' : result.type === 'incomplete' ? 'function call has not returned yet' : isEqualValue(expected, result.value) - ? printCommon(result.value) - : printReceived(result.value); + ? printCommon(utils, result.value) + : utils.printReceived(result.value); type IndexedResult = [number, any]; // Return either empty string or one line per indexed result, // so additional empty line can separate from `Number of returns` which follows. const printReceivedResults = ( + utils: typeof MatcherUtils, label: string, expected: unknown, indexedResults: Array, @@ -335,7 +341,7 @@ const printReceivedResults = ( } if (isOnlyCall && (iExpectedCall === 0 || iExpectedCall === undefined)) { - return `${label + printResult(indexedResults[0][1], expected)}\n`; + return `${label + printResult(utils, indexedResults[0][1], expected)}\n`; } const printAligned = getRightAlignedPrinter(label); @@ -349,7 +355,7 @@ const printReceivedResults = ( `${ printed + printAligned(String(i + 1), i === iExpectedCall) + - printResult(result, expected) + printResult(utils, result, expected) }\n`, '', ) @@ -357,14 +363,20 @@ const printReceivedResults = ( }; const createToHaveBeenCalledMatcher = (): MatcherFunction<[unknown]> => - function (received: any, expected: unknown): SyncExpectationResult { + function (received: any, expected): SyncExpectationResult { const expectedArgument = ''; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, 'toHaveBeenCalled', options); - ensureMockOrSpy(received, 'toHaveBeenCalled', expectedArgument, options); + this.utils.ensureNoExpected(expected, 'toHaveBeenCalled', options); + ensureMockOrSpy( + this.utils, + received, + 'toHaveBeenCalled', + expectedArgument, + options, + ); const receivedIsSpy = isSpy(received); const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); @@ -378,19 +390,19 @@ const createToHaveBeenCalledMatcher = (): MatcherFunction<[unknown]> => const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenCalled', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of calls: ${printExpected(0)}\n` + - `Received number of calls: ${printReceived(count)}\n\n` + + `Expected number of calls: ${this.utils.printExpected(0)}\n` + + `Received number of calls: ${this.utils.printReceived(count)}\n\n` + calls .reduce((lines: Array, args: any, i: number) => { if (lines.length < PRINT_LIMIT) { - lines.push(`${i + 1}: ${printReceivedArgs(args)}`); + lines.push(`${i + 1}: ${printReceivedArgs(this.utils, args)}`); } return lines; @@ -398,15 +410,15 @@ const createToHaveBeenCalledMatcher = (): MatcherFunction<[unknown]> => .join('\n') : () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenCalled', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of calls: >= ${printExpected(1)}\n` + - `Received number of calls: ${printReceived(count)}`; + `Expected number of calls: >= ${this.utils.printExpected(1)}\n` + + `Received number of calls: ${this.utils.printReceived(count)}`; return {message, pass}; }; @@ -414,12 +426,18 @@ const createToHaveBeenCalledMatcher = (): MatcherFunction<[unknown]> => const createToHaveReturnedMatcher = (): MatcherFunction<[unknown]> => function (received: any, expected): SyncExpectationResult { const expectedArgument = ''; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, 'toHaveReturned', options); - ensureMock(received, 'toHaveReturned', expectedArgument, options); + this.utils.ensureNoExpected(expected, 'toHaveReturned', options); + ensureMock( + this.utils, + received, + 'toHaveReturned', + expectedArgument, + options, + ); const receivedName = received.getMockName(); @@ -434,19 +452,21 @@ const createToHaveReturnedMatcher = (): MatcherFunction<[unknown]> => const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveReturned', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of returns: ${printExpected(0)}\n` + - `Received number of returns: ${printReceived(count)}\n\n` + + `Expected number of returns: ${this.utils.printExpected(0)}\n` + + `Received number of returns: ${this.utils.printReceived(count)}\n\n` + received.mock.results .reduce((lines: Array, result: any, i: number) => { if (result.type === 'return' && lines.length < PRINT_LIMIT) { - lines.push(`${i + 1}: ${printReceived(result.value)}`); + lines.push( + `${i + 1}: ${this.utils.printReceived(result.value)}`, + ); } return lines; @@ -454,23 +474,23 @@ const createToHaveReturnedMatcher = (): MatcherFunction<[unknown]> => .join('\n') + (received.mock.calls.length === count ? '' - : `\n\nReceived number of calls: ${printReceived( + : `\n\nReceived number of calls: ${this.utils.printReceived( received.mock.calls.length, )}`) : () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveReturned', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of returns: >= ${printExpected(1)}\n` + - `Received number of returns: ${printReceived(count)}` + + `Expected number of returns: >= ${this.utils.printExpected(1)}\n` + + `Received number of returns: ${this.utils.printReceived(count)}` + (received.mock.calls.length === count ? '' - : `\nReceived number of calls: ${printReceived( + : `\nReceived number of calls: ${this.utils.printReceived( received.mock.calls.length, )}`); @@ -480,16 +500,17 @@ const createToHaveReturnedMatcher = (): MatcherFunction<[unknown]> => const createToHaveBeenCalledTimesMatcher = (): MatcherFunction<[number]> => function (received: any, expected): SyncExpectationResult { const expectedArgument = 'expected'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; - ensureExpectedIsNonNegativeInteger( + this.utils.ensureExpectedIsNonNegativeInteger( expected, 'toHaveBeenCalledTimes', options, ); ensureMockOrSpy( + this.utils, received, 'toHaveBeenCalledTimes', expectedArgument, @@ -507,25 +528,25 @@ const createToHaveBeenCalledTimesMatcher = (): MatcherFunction<[number]> => const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenCalledTimes', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of calls: not ${printExpected(expected)}` + `Expected number of calls: not ${this.utils.printExpected(expected)}` : () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenCalledTimes', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of calls: ${printExpected(expected)}\n` + - `Received number of calls: ${printReceived(count)}`; + `Expected number of calls: ${this.utils.printExpected(expected)}\n` + + `Received number of calls: ${this.utils.printReceived(count)}`; return {message, pass}; }; @@ -533,16 +554,22 @@ const createToHaveBeenCalledTimesMatcher = (): MatcherFunction<[number]> => const createToHaveReturnedTimesMatcher = (): MatcherFunction<[number]> => function (received: any, expected): SyncExpectationResult { const expectedArgument = 'expected'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; - ensureExpectedIsNonNegativeInteger( + this.utils.ensureExpectedIsNonNegativeInteger( expected, 'toHaveReturnedTimes', options, ); - ensureMock(received, 'toHaveReturnedTimes', expectedArgument, options); + ensureMock( + this.utils, + received, + 'toHaveReturnedTimes', + expectedArgument, + options, + ); const receivedName = received.getMockName(); @@ -557,33 +584,37 @@ const createToHaveReturnedTimesMatcher = (): MatcherFunction<[number]> => const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveReturnedTimes', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of returns: not ${printExpected(expected)}` + + `Expected number of returns: not ${this.utils.printExpected( + expected, + )}` + (received.mock.calls.length === count ? '' - : `\n\nReceived number of calls: ${printReceived( + : `\n\nReceived number of calls: ${this.utils.printReceived( received.mock.calls.length, )}`) : () => // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveReturnedTimes', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected number of returns: ${printExpected(expected)}\n` + - `Received number of returns: ${printReceived(count)}` + + `Expected number of returns: ${this.utils.printExpected( + expected, + )}\n` + + `Received number of returns: ${this.utils.printReceived(count)}` + (received.mock.calls.length === count ? '' - : `\nReceived number of calls: ${printReceived( + : `\nReceived number of calls: ${this.utils.printReceived( received.mock.calls.length, )}`); @@ -593,11 +624,12 @@ const createToHaveReturnedTimesMatcher = (): MatcherFunction<[number]> => const createToHaveBeenCalledWithMatcher = (): MatcherFunction> => function (received: any, ...expected): SyncExpectationResult { const expectedArgument = '...expected'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; ensureMockOrSpy( + this.utils, received, 'toHaveBeenCalledWith', expectedArgument, @@ -627,22 +659,24 @@ const createToHaveBeenCalledWithMatcher = (): MatcherFunction> => return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenCalledWith', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected: not ${printExpectedArgs(expected)}\n` + - (calls.length === 1 && stringify(calls[0]) === stringify(expected) + `Expected: not ${printExpectedArgs(this.utils, expected)}\n` + + (calls.length === 1 && + this.utils.stringify(calls[0]) === this.utils.stringify(expected) ? '' : printReceivedCallsNegative( + this.utils, expected, indexedCalls, calls.length === 1, )) + - `\nNumber of calls: ${printReceived(calls.length)}` + `\nNumber of calls: ${this.utils.printReceived(calls.length)}` ); } : () => { @@ -656,7 +690,7 @@ const createToHaveBeenCalledWithMatcher = (): MatcherFunction> => return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenCalledWith', receivedName, expectedArgument, @@ -664,12 +698,13 @@ const createToHaveBeenCalledWithMatcher = (): MatcherFunction> => ) + '\n\n' + printExpectedReceivedCallsPositive( + this.utils, expected, indexedCalls, isExpand(this.expand), calls.length === 1, ) + - `\nNumber of calls: ${printReceived(calls.length)}` + `\nNumber of calls: ${this.utils.printReceived(calls.length)}` ); }; @@ -679,11 +714,17 @@ const createToHaveBeenCalledWithMatcher = (): MatcherFunction> => const createToHaveReturnedWithMatcher = (): MatcherFunction<[unknown]> => function (received: any, expected): SyncExpectationResult { const expectedArgument = 'expected'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; - ensureMock(received, 'toHaveReturnedWith', expectedArgument, options); + ensureMock( + this.utils, + received, + 'toHaveReturnedWith', + expectedArgument, + options, + ); const receivedName = received.getMockName(); const {calls, results} = received.mock; @@ -704,25 +745,31 @@ const createToHaveReturnedWithMatcher = (): MatcherFunction<[unknown]> => return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveReturnedWith', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected: not ${printExpected(expected)}\n` + + `Expected: not ${this.utils.printExpected(expected)}\n` + (results.length === 1 && results[0].type === 'return' && - stringify(results[0].value) === stringify(expected) + this.utils.stringify(results[0].value) === + this.utils.stringify(expected) ? '' : printReceivedResults( + this.utils, 'Received: ', expected, indexedResults, results.length === 1, )) + - printNumberOfReturns(countReturns(results), calls.length) + printNumberOfReturns( + this.utils, + countReturns(results), + calls.length, + ) ); } : () => { @@ -736,21 +783,26 @@ const createToHaveReturnedWithMatcher = (): MatcherFunction<[unknown]> => return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveReturnedWith', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + + `Expected: ${this.utils.printExpected(expected)}\n` + printReceivedResults( + this.utils, 'Received: ', expected, indexedResults, results.length === 1, ) + - printNumberOfReturns(countReturns(results), calls.length) + printNumberOfReturns( + this.utils, + countReturns(results), + calls.length, + ) ); }; @@ -762,11 +814,12 @@ const createToHaveBeenLastCalledWithMatcher = (): MatcherFunction< > => function (received: any, ...expected): SyncExpectationResult { const expectedArgument = '...expected'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; ensureMockOrSpy( + this.utils, received, 'toHaveBeenLastCalledWith', expectedArgument, @@ -794,23 +847,25 @@ const createToHaveBeenLastCalledWithMatcher = (): MatcherFunction< return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenLastCalledWith', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected: not ${printExpectedArgs(expected)}\n` + - (calls.length === 1 && stringify(calls[0]) === stringify(expected) + `Expected: not ${printExpectedArgs(this.utils, expected)}\n` + + (calls.length === 1 && + this.utils.stringify(calls[0]) === this.utils.stringify(expected) ? '' : printReceivedCallsNegative( + this.utils, expected, indexedCalls, calls.length === 1, iLast, )) + - `\nNumber of calls: ${printReceived(calls.length)}` + `\nNumber of calls: ${this.utils.printReceived(calls.length)}` ); } : () => { @@ -834,7 +889,7 @@ const createToHaveBeenLastCalledWithMatcher = (): MatcherFunction< return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenLastCalledWith', receivedName, expectedArgument, @@ -842,13 +897,14 @@ const createToHaveBeenLastCalledWithMatcher = (): MatcherFunction< ) + '\n\n' + printExpectedReceivedCallsPositive( + this.utils, expected, indexedCalls, isExpand(this.expand), calls.length === 1, iLast, ) + - `\nNumber of calls: ${printReceived(calls.length)}` + `\nNumber of calls: ${this.utils.printReceived(calls.length)}` ); }; @@ -858,11 +914,17 @@ const createToHaveBeenLastCalledWithMatcher = (): MatcherFunction< const createToHaveLastReturnedWithMatcher = (): MatcherFunction<[unknown]> => function (received: any, expected): SyncExpectationResult { const expectedArgument = 'expected'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; - ensureMock(received, 'toHaveLastReturnedWith', expectedArgument, options); + ensureMock( + this.utils, + received, + 'toHaveLastReturnedWith', + expectedArgument, + options, + ); const receivedName = received.getMockName(); @@ -882,26 +944,32 @@ const createToHaveLastReturnedWithMatcher = (): MatcherFunction<[unknown]> => return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveLastReturnedWith', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected: not ${printExpected(expected)}\n` + + `Expected: not ${this.utils.printExpected(expected)}\n` + (results.length === 1 && results[0].type === 'return' && - stringify(results[0].value) === stringify(expected) + this.utils.stringify(results[0].value) === + this.utils.stringify(expected) ? '' : printReceivedResults( + this.utils, 'Received: ', expected, indexedResults, results.length === 1, iLast, )) + - printNumberOfReturns(countReturns(results), calls.length) + printNumberOfReturns( + this.utils, + countReturns(results), + calls.length, + ) ); } : () => { @@ -925,22 +993,27 @@ const createToHaveLastReturnedWithMatcher = (): MatcherFunction<[unknown]> => return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveLastReturnedWith', receivedName, expectedArgument, options, ) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + + `Expected: ${this.utils.printExpected(expected)}\n` + printReceivedResults( + this.utils, 'Received: ', expected, indexedResults, results.length === 1, iLast, ) + - printNumberOfReturns(countReturns(results), calls.length) + printNumberOfReturns( + this.utils, + countReturns(results), + calls.length, + ) ); }; @@ -952,13 +1025,14 @@ const createToHaveBeenNthCalledWithMatcher = (): MatcherFunction< > => function (received: any, nth, ...expected): SyncExpectationResult { const expectedArgument = 'n'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { expectedColor: (arg: string) => arg, isNot: this.isNot, promise: this.promise, secondArgument: '...expected', }; ensureMockOrSpy( + this.utils, received, 'toHaveBeenNthCalledWith', expectedArgument, @@ -967,15 +1041,15 @@ const createToHaveBeenNthCalledWithMatcher = (): MatcherFunction< if (!Number.isSafeInteger(nth) || nth < 1) { throw new Error( - matcherErrorMessage( - matcherHint( + this.utils.matcherErrorMessage( + this.utils.matcherHint( 'toHaveBeenNthCalledWith', undefined, expectedArgument, options, ), `${expectedArgument} must be a positive integer`, - printWithType(expectedArgument, nth, stringify), + this.utils.printWithType(expectedArgument, nth, this.utils.stringify), ), ); } @@ -1006,7 +1080,7 @@ const createToHaveBeenNthCalledWithMatcher = (): MatcherFunction< return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenNthCalledWith', receivedName, expectedArgument, @@ -1014,16 +1088,18 @@ const createToHaveBeenNthCalledWithMatcher = (): MatcherFunction< ) + '\n\n' + `n: ${nth}\n` + - `Expected: not ${printExpectedArgs(expected)}\n` + - (calls.length === 1 && stringify(calls[0]) === stringify(expected) + `Expected: not ${printExpectedArgs(this.utils, expected)}\n` + + (calls.length === 1 && + this.utils.stringify(calls[0]) === this.utils.stringify(expected) ? '' : printReceivedCallsNegative( + this.utils, expected, indexedCalls, calls.length === 1, iNth, )) + - `\nNumber of calls: ${printReceived(calls.length)}` + `\nNumber of calls: ${this.utils.printReceived(calls.length)}` ); } : () => { @@ -1074,7 +1150,7 @@ const createToHaveBeenNthCalledWithMatcher = (): MatcherFunction< return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveBeenNthCalledWith', receivedName, expectedArgument, @@ -1083,13 +1159,14 @@ const createToHaveBeenNthCalledWithMatcher = (): MatcherFunction< '\n\n' + `n: ${nth}\n` + printExpectedReceivedCallsPositive( + this.utils, expected, indexedCalls, isExpand(this.expand), calls.length === 1, iNth, ) + - `\nNumber of calls: ${printReceived(calls.length)}` + `\nNumber of calls: ${this.utils.printReceived(calls.length)}` ); }; @@ -1101,25 +1178,30 @@ const createToHaveNthReturnedWithMatcher = (): MatcherFunction< > => function (received: any, nth, expected): SyncExpectationResult { const expectedArgument = 'n'; - const options: MatcherHintOptions = { + const options: MatcherUtils.MatcherHintOptions = { expectedColor: (arg: string) => arg, isNot: this.isNot, promise: this.promise, secondArgument: 'expected', }; - ensureMock(received, 'toHaveNthReturnedWith', expectedArgument, options); - + ensureMock( + this.utils, + received, + 'toHaveNthReturnedWith', + expectedArgument, + options, + ); if (!Number.isSafeInteger(nth) || nth < 1) { throw new Error( - matcherErrorMessage( - matcherHint( + this.utils.matcherErrorMessage( + this.utils.matcherHint( 'toHaveNthReturnedWith', undefined, expectedArgument, options, ), `${expectedArgument} must be a positive integer`, - printWithType(expectedArgument, nth, stringify), + this.utils.printWithType(expectedArgument, nth, this.utils.stringify), ), ); } @@ -1146,7 +1228,7 @@ const createToHaveNthReturnedWithMatcher = (): MatcherFunction< return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveNthReturnedWith', receivedName, expectedArgument, @@ -1154,19 +1236,25 @@ const createToHaveNthReturnedWithMatcher = (): MatcherFunction< ) + '\n\n' + `n: ${nth}\n` + - `Expected: not ${printExpected(expected)}\n` + + `Expected: not ${this.utils.printExpected(expected)}\n` + (results.length === 1 && results[0].type === 'return' && - stringify(results[0].value) === stringify(expected) + this.utils.stringify(results[0].value) === + this.utils.stringify(expected) ? '' : printReceivedResults( + this.utils, 'Received: ', expected, indexedResults, results.length === 1, iNth, )) + - printNumberOfReturns(countReturns(results), calls.length) + printNumberOfReturns( + this.utils, + countReturns(results), + calls.length, + ) ); } : () => { @@ -1217,7 +1305,7 @@ const createToHaveNthReturnedWithMatcher = (): MatcherFunction< return ( // eslint-disable-next-line prefer-template - matcherHint( + this.utils.matcherHint( 'toHaveNthReturnedWith', receivedName, expectedArgument, @@ -1225,15 +1313,20 @@ const createToHaveNthReturnedWithMatcher = (): MatcherFunction< ) + '\n\n' + `n: ${nth}\n` + - `Expected: ${printExpected(expected)}\n` + + `Expected: ${this.utils.printExpected(expected)}\n` + printReceivedResults( + this.utils, 'Received: ', expected, indexedResults, results.length === 1, iNth, ) + - printNumberOfReturns(countReturns(results), calls.length) + printNumberOfReturns( + this.utils, + countReturns(results), + calls.length, + ) ); }; @@ -1263,34 +1356,38 @@ const isSpy = (received: any) => typeof received.calls.count === 'function'; const ensureMockOrSpy = ( + utils: typeof MatcherUtils, received: any, matcherName: string, expectedArgument: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, ) => { if (!isMock(received) && !isSpy(received)) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, expectedArgument, options), - `${RECEIVED_COLOR('received')} value must be a mock or spy function`, - printWithType('Received', received, printReceived), + utils.matcherErrorMessage( + utils.matcherHint(matcherName, undefined, expectedArgument, options), + `${utils.RECEIVED_COLOR( + 'received', + )} value must be a mock or spy function`, + utils.printWithType('Received', received, utils.printReceived), ), ); } }; const ensureMock = ( + utils: typeof MatcherUtils, received: any, matcherName: string, expectedArgument: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, ) => { if (!isMock(received)) { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, expectedArgument, options), - `${RECEIVED_COLOR('received')} value must be a mock function`, - printWithType('Received', received, printReceived), + utils.matcherErrorMessage( + utils.matcherHint(matcherName, undefined, expectedArgument, options), + `${utils.RECEIVED_COLOR('received')} value must be a mock function`, + utils.printWithType('Received', received, utils.printReceived), ), ); } diff --git a/packages/expect/src/toThrowMatchers.ts b/packages/expect/src/toThrowMatchers.ts index 9c6ec8989625..ffaed4ce2792 100644 --- a/packages/expect/src/toThrowMatchers.ts +++ b/packages/expect/src/toThrowMatchers.ts @@ -7,22 +7,8 @@ */ import {isError} from '@jest/expect-utils'; -import { - EXPECTED_COLOR, - type MatcherHintOptions, - RECEIVED_COLOR, - matcherErrorMessage, - matcherHint, - printDiffOrStringify, - printExpected, - printReceived, - printWithType, -} from 'jest-matcher-utils'; -import { - formatExecError, - formatStackTrace, - separateMessageFromStack, -} from 'jest-message-util'; +import type * as MatcherUtils from 'jest-matcher-utils'; +import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import { printExpectedConstructorName, printExpectedConstructorNameNot, @@ -100,10 +86,21 @@ export const createMatcher = ( if (!fromPromise) { const placeholder = expected === undefined ? '' : 'expected'; throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, placeholder, options), - `${RECEIVED_COLOR('received')} value must be a function`, - printWithType('Received', received, printReceived), + this.utils.matcherErrorMessage( + this.utils.matcherHint( + matcherName, + undefined, + placeholder, + options, + ), + `${this.utils.RECEIVED_COLOR( + 'received', + )} value must be a function`, + this.utils.printWithType( + 'Received', + received, + this.utils.printReceived, + ), ), ); } @@ -111,28 +108,62 @@ export const createMatcher = ( } if (expected === undefined) { - return toThrow(matcherName, options, thrown); + return toThrow(this.utils, matcherName, options, thrown); } else if (typeof expected === 'function') { - return toThrowExpectedClass(matcherName, options, thrown, expected); + return toThrowExpectedClass( + this.utils, + matcherName, + options, + thrown, + expected, + ); } else if (typeof expected === 'string') { - return toThrowExpectedString(matcherName, options, thrown, expected); + return toThrowExpectedString( + this.utils, + matcherName, + options, + thrown, + expected, + ); } else if (expected !== null && typeof expected.test === 'function') { - return toThrowExpectedRegExp(matcherName, options, thrown, expected); + return toThrowExpectedRegExp( + this.utils, + matcherName, + options, + thrown, + expected, + ); } else if ( expected !== null && typeof expected.asymmetricMatch === 'function' ) { - return toThrowExpectedAsymmetric(matcherName, options, thrown, expected); + return toThrowExpectedAsymmetric( + this.utils, + matcherName, + options, + thrown, + expected, + ); } else if (expected !== null && typeof expected === 'object') { - return toThrowExpectedObject(matcherName, options, thrown, expected); + return toThrowExpectedObject( + this.utils, + matcherName, + options, + thrown, + expected, + ); } else { throw new Error( - matcherErrorMessage( - matcherHint(matcherName, undefined, undefined, options), - `${EXPECTED_COLOR( + this.utils.matcherErrorMessage( + this.utils.matcherHint(matcherName, undefined, undefined, options), + `${this.utils.EXPECTED_COLOR( 'expected', )} value must be a string or regular expression or class or error`, - printWithType('Expected', expected, printExpected), + this.utils.printWithType( + 'Expected', + expected, + this.utils.printExpected, + ), ), ); } @@ -143,8 +174,9 @@ const matchers: MatchersObject = { }; const toThrowExpectedRegExp = ( + utils: typeof MatcherUtils, matcherName: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, thrown: Thrown | null, expected: RegExp, ): SyncExpectationResult => { @@ -153,28 +185,29 @@ const toThrowExpectedRegExp = ( const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - formatExpected('Expected pattern: not ', expected) + + formatExpected(utils, 'Expected pattern: not ', expected) + (thrown !== null && thrown.hasMessage ? formatReceived( + utils, 'Received message: ', thrown, 'message', expected, ) + formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value')) + : formatReceived(utils, 'Received value: ', thrown, 'value')) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - formatExpected('Expected pattern: ', expected) + + formatExpected(utils, 'Expected pattern: ', expected) + (thrown === null ? `\n${DID_NOT_THROW}` : thrown.hasMessage - ? formatReceived('Received message: ', thrown, 'message') + + ? formatReceived(utils, 'Received message: ', thrown, 'message') + formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value')); + : formatReceived(utils, 'Received value: ', thrown, 'value')); return {message, pass}; }; @@ -184,8 +217,9 @@ type AsymmetricMatcher = { }; const toThrowExpectedAsymmetric = ( + utils: typeof MatcherUtils, matcherName: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, thrown: Thrown | null, expected: AsymmetricMatcher, ): SyncExpectationResult => { @@ -194,35 +228,36 @@ const toThrowExpectedAsymmetric = ( const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - formatExpected('Expected asymmetric matcher: not ', expected) + + formatExpected(utils, 'Expected asymmetric matcher: not ', expected) + '\n' + (thrown !== null && thrown.hasMessage - ? formatReceived('Received name: ', thrown, 'name') + - formatReceived('Received message: ', thrown, 'message') + + ? formatReceived(utils, 'Received name: ', thrown, 'name') + + formatReceived(utils, 'Received message: ', thrown, 'message') + formatStack(thrown) - : formatReceived('Thrown value: ', thrown, 'value')) + : formatReceived(utils, 'Thrown value: ', thrown, 'value')) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - formatExpected('Expected asymmetric matcher: ', expected) + + formatExpected(utils, 'Expected asymmetric matcher: ', expected) + '\n' + (thrown === null ? DID_NOT_THROW : thrown.hasMessage - ? formatReceived('Received name: ', thrown, 'name') + - formatReceived('Received message: ', thrown, 'message') + + ? formatReceived(utils, 'Received name: ', thrown, 'name') + + formatReceived(utils, 'Received message: ', thrown, 'message') + formatStack(thrown) - : formatReceived('Thrown value: ', thrown, 'value')); + : formatReceived(utils, 'Thrown value: ', thrown, 'value')); return {message, pass}; }; const toThrowExpectedObject = ( + utils: typeof MatcherUtils, matcherName: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, thrown: Thrown | null, expected: Error, ): SyncExpectationResult => { @@ -244,22 +279,24 @@ const toThrowExpectedObject = ( const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + formatExpected( + utils, `Expected ${messageAndCause(expected)}: not `, expectedMessageAndCause, ) + (thrown !== null && thrown.hasMessage ? formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value')) + : formatReceived(utils, 'Received value: ', thrown, 'value')) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + (thrown === null ? // eslint-disable-next-line prefer-template formatExpected( + utils, `Expected ${messageAndCause(expected)}: `, expectedMessageAndCause, ) + @@ -267,7 +304,7 @@ const toThrowExpectedObject = ( DID_NOT_THROW : thrown.hasMessage ? // eslint-disable-next-line prefer-template - printDiffOrStringify( + utils.printDiffOrStringify( expectedMessageAndCause, thrownMessageAndCause, `Expected ${messageAndCause(expected)}`, @@ -277,16 +314,18 @@ const toThrowExpectedObject = ( '\n' + formatStack(thrown) : formatExpected( + utils, `Expected ${messageAndCause(expected)}: `, expectedMessageAndCause, - ) + formatReceived('Received value: ', thrown, 'value')); + ) + formatReceived(utils, 'Received value: ', thrown, 'value')); return {message, pass}; }; const toThrowExpectedClass = ( + utils: typeof MatcherUtils, matcherName: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, thrown: Thrown | null, expected: Function, ): SyncExpectationResult => { @@ -295,7 +334,7 @@ const toThrowExpectedClass = ( const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + printExpectedConstructorNameNot('Expected constructor', expected) + (thrown !== null && @@ -310,12 +349,12 @@ const toThrowExpectedClass = ( : '') + '\n' + (thrown !== null && thrown.hasMessage - ? formatReceived('Received message: ', thrown, 'message') + + ? formatReceived(utils, 'Received message: ', thrown, 'message') + formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value')) + : formatReceived(utils, 'Received value: ', thrown, 'value')) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + printExpectedConstructorName('Expected constructor', expected) + (thrown === null @@ -330,17 +369,22 @@ const toThrowExpectedClass = ( : '' }\n${ thrown.hasMessage - ? formatReceived('Received message: ', thrown, 'message') + - formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value') + ? formatReceived( + utils, + 'Received message: ', + thrown, + 'message', + ) + formatStack(thrown) + : formatReceived(utils, 'Received value: ', thrown, 'value') }`); return {message, pass}; }; const toThrowExpectedString = ( + utils: typeof MatcherUtils, matcherName: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, thrown: Thrown | null, expected: string, ): SyncExpectationResult => { @@ -349,35 +393,37 @@ const toThrowExpectedString = ( const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - formatExpected('Expected substring: not ', expected) + + formatExpected(utils, 'Expected substring: not ', expected) + (thrown !== null && thrown.hasMessage ? formatReceived( + utils, 'Received message: ', thrown, 'message', expected, ) + formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value')) + : formatReceived(utils, 'Received value: ', thrown, 'value')) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, undefined, options) + + utils.matcherHint(matcherName, undefined, undefined, options) + '\n\n' + - formatExpected('Expected substring: ', expected) + + formatExpected(utils, 'Expected substring: ', expected) + (thrown === null ? `\n${DID_NOT_THROW}` : thrown.hasMessage - ? formatReceived('Received message: ', thrown, 'message') + + ? formatReceived(utils, 'Received message: ', thrown, 'message') + formatStack(thrown) - : formatReceived('Received value: ', thrown, 'value')); + : formatReceived(utils, 'Received value: ', thrown, 'value')); return {message, pass}; }; const toThrow = ( + utils: typeof MatcherUtils, matcherName: string, - options: MatcherHintOptions, + options: MatcherUtils.MatcherHintOptions, thrown: Thrown | null, ): SyncExpectationResult => { const pass = thrown !== null; @@ -385,26 +431,30 @@ const toThrow = ( const message = pass ? () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + (thrown !== null && thrown.hasMessage - ? formatReceived('Error name: ', thrown, 'name') + - formatReceived('Error message: ', thrown, 'message') + + ? formatReceived(utils, 'Error name: ', thrown, 'name') + + formatReceived(utils, 'Error message: ', thrown, 'message') + formatStack(thrown) - : formatReceived('Thrown value: ', thrown, 'value')) + : formatReceived(utils, 'Thrown value: ', thrown, 'value')) : () => // eslint-disable-next-line prefer-template - matcherHint(matcherName, undefined, '', options) + + utils.matcherHint(matcherName, undefined, '', options) + '\n\n' + DID_NOT_THROW; return {message, pass}; }; -const formatExpected = (label: string, expected: unknown) => - `${label + printExpected(expected)}\n`; +const formatExpected = ( + utils: typeof MatcherUtils, + label: string, + expected: unknown, +) => `${label + utils.printExpected(expected)}\n`; const formatReceived = ( + utils: typeof MatcherUtils, label: string, thrown: Thrown | null, key: string, @@ -439,17 +489,19 @@ const formatReceived = ( }\n`; } - return `${label + printReceived(message)}\n`; + return `${label + utils.printReceived(message)}\n`; } if (key === 'name') { return thrown.isError - ? `${label + printReceived(thrown.value.name)}\n` + ? `${label + utils.printReceived(thrown.value.name)}\n` : ''; } if (key === 'value') { - return thrown.isError ? '' : `${label + printReceived(thrown.value)}\n`; + return thrown.isError + ? '' + : `${label + utils.printReceived(thrown.value)}\n`; } return ''; diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 4646dc07a9a2..f2f1c744cad5 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -44,8 +44,11 @@ export type MatchersObject = { [name: string]: RawMatcherFn; }; -export type ThrowingMatcherFn = (actual: any) => void; -export type PromiseMatcherFn = (actual: any) => Promise; +export type ThrowingMatcherFn = (this: MatcherContext, actual: any) => void; +export type PromiseMatcherFn = ( + this: MatcherContext, + actual: any, +) => Promise; export interface MatcherUtils { customTesters: Array;