From 02df7d321d28184741f00acefc94d8587a1d34ea Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 5 Oct 2021 16:21:42 +0200 Subject: [PATCH] fix: construct mather context for asymmetric matchers on demand (#11930) --- CHANGELOG.md | 2 +- packages/expect/src/asymmetricMatchers.ts | 46 +++++++++++------------ packages/expect/src/jestMatchersObject.ts | 10 +++-- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5377b630f12e..56daf793e58e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixes -- `[expect]` Pass matcher context to asymmetric matchers ([#11926](https://github.com/facebook/jest/pull/11926)) +- `[expect]` Pass matcher context to asymmetric matchers ([#11926](https://github.com/facebook/jest/pull/11926) & [#11930](https://github.com/facebook/jest/pull/11930)) - `[@jest/types]` Mark deprecated configuration options as `@deprecated` ([#11913](https://github.com/facebook/jest/pull/11913)) - `[jest-cli]` Improve `--help` printout by removing defunct `--browser` option ([#11914](https://github.com/facebook/jest/pull/11914)) - `[jest-haste-map]` Use distinct cache paths for different values of `computeDependencies` ([#11916](https://github.com/facebook/jest/pull/11916)) diff --git a/packages/expect/src/asymmetricMatchers.ts b/packages/expect/src/asymmetricMatchers.ts index 5d56358192bb..6533d2d3fb47 100644 --- a/packages/expect/src/asymmetricMatchers.ts +++ b/packages/expect/src/asymmetricMatchers.ts @@ -12,30 +12,28 @@ import {getState} from './jestMatchersObject'; import type {MatcherState} from './types'; import {iterableEquality, subsetEquality} from './utils'; -export class AsymmetricMatcher { - protected sample: T; - protected readonly matcherState: MatcherState; - $$typeof: symbol; - // TODO: remove this field in Jest 28 (use `matcherState`) - inverse?: boolean; +const utils = Object.freeze({ + ...matcherUtils, + iterableEquality, + subsetEquality, +}); - constructor(sample: T, isNot = false) { - this.$$typeof = Symbol.for('jest.asymmetricMatcher'); - this.sample = sample; +export abstract class AsymmetricMatcher { + $$typeof = Symbol.for('jest.asymmetricMatcher'); - const utils = {...matcherUtils, iterableEquality, subsetEquality}; + constructor(protected sample: T, protected inverse = false) {} - const matcherContext: MatcherState = { + protected getMatcherContext(): MatcherState { + return { ...getState(), equals, - isNot, + isNot: this.inverse, utils, }; - - this.inverse = matcherContext.isNot; - - this.matcherState = matcherContext; } + + abstract asymmetricMatch(other: unknown): boolean; + abstract toString(): string; } class Any extends AsymmetricMatcher { @@ -152,11 +150,11 @@ class ArrayContaining extends AsymmetricMatcher> { other.some(another => equals(item, another)), )); - return this.matcherState.isNot ? !result : result; + return this.inverse ? !result : result; } toString() { - return `Array${this.matcherState.isNot ? 'Not' : ''}Containing`; + return `Array${this.inverse ? 'Not' : ''}Containing`; } getExpectedType() { @@ -190,11 +188,11 @@ class ObjectContaining extends AsymmetricMatcher> { } } - return this.matcherState.isNot ? !result : result; + return this.inverse ? !result : result; } toString() { - return `Object${this.matcherState.isNot ? 'Not' : ''}Containing`; + return `Object${this.inverse ? 'Not' : ''}Containing`; } getExpectedType() { @@ -213,11 +211,11 @@ class StringContaining extends AsymmetricMatcher { asymmetricMatch(other: string) { const result = isA('String', other) && other.includes(this.sample); - return this.matcherState.isNot ? !result : result; + return this.inverse ? !result : result; } toString() { - return `String${this.matcherState.isNot ? 'Not' : ''}Containing`; + return `String${this.inverse ? 'Not' : ''}Containing`; } getExpectedType() { @@ -236,11 +234,11 @@ class StringMatching extends AsymmetricMatcher { asymmetricMatch(other: string) { const result = isA('String', other) && this.sample.test(other); - return this.matcherState.isNot ? !result : result; + return this.inverse ? !result : result; } toString() { - return `String${this.matcherState.isNot ? 'Not' : ''}Matching`; + return `String${this.inverse ? 'Not' : ''}Matching`; } getExpectedType() { diff --git a/packages/expect/src/jestMatchersObject.ts b/packages/expect/src/jestMatchersObject.ts index 68aa1bfd659e..c9fade8ae6ed 100644 --- a/packages/expect/src/jestMatchersObject.ts +++ b/packages/expect/src/jestMatchersObject.ts @@ -68,16 +68,16 @@ export const setMatchers = ( asymmetricMatch(other: unknown) { const {pass} = matcher.call( - this.matcherState, + this.getMatcherContext(), other, ...this.sample, ) as SyncExpectationResult; - return this.matcherState.isNot ? !pass : pass; + return this.inverse ? !pass : pass; } toString() { - return `${this.matcherState.isNot ? 'not.' : ''}${key}`; + return `${this.inverse ? 'not.' : ''}${key}`; } getExpectedType() { @@ -92,7 +92,9 @@ export const setMatchers = ( expect[key] = (...sample: [unknown, unknown]) => new CustomMatcher(false, ...sample); if (!expect.not) { - expect.not = {}; + throw new Error( + '`expect.not` is not defined - please report this bug to https://github.com/facebook/jest', + ); } expect.not[key] = (...sample: [unknown, unknown]) => new CustomMatcher(true, ...sample);