Skip to content

Commit

Permalink
fix: construct mather context for asymmetric matchers on demand (#11930)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB authored Oct 5, 2021
1 parent f13abff commit 02df7d3
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 29 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
46 changes: 22 additions & 24 deletions packages/expect/src/asymmetricMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,28 @@ import {getState} from './jestMatchersObject';
import type {MatcherState} from './types';
import {iterableEquality, subsetEquality} from './utils';

export class AsymmetricMatcher<T> {
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<T> {
$$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<any> {
Expand Down Expand Up @@ -152,11 +150,11 @@ class ArrayContaining extends AsymmetricMatcher<Array<unknown>> {
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() {
Expand Down Expand Up @@ -190,11 +188,11 @@ class ObjectContaining extends AsymmetricMatcher<Record<string, unknown>> {
}
}

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() {
Expand All @@ -213,11 +211,11 @@ class StringContaining extends AsymmetricMatcher<string> {
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() {
Expand All @@ -236,11 +234,11 @@ class StringMatching extends AsymmetricMatcher<RegExp> {
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() {
Expand Down
10 changes: 6 additions & 4 deletions packages/expect/src/jestMatchersObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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);
Expand Down

0 comments on commit 02df7d3

Please sign in to comment.