diff --git a/docs/en/ExpectAPI.md b/docs/en/ExpectAPI.md index af6605c9b237..9e8f4bf34ab1 100644 --- a/docs/en/ExpectAPI.md +++ b/docs/en/ExpectAPI.md @@ -73,6 +73,10 @@ These helper functions can be found on `this` inside a custom matcher: A boolean to let you know this matcher was called with the negated `.not` modifier allowing you to flip your assertion. +#### `this.equals(a, b)` + +This is a deep-equality function that will return `true` if two objects have the same values (recursively). + #### `this.utils` There are a number of helpful tools exposed on `this.utils` primarily consisting of the exports from [`jest-matcher-utils`](https://github.com/facebook/jest/tree/master/packages/jest-matcher-utils). diff --git a/packages/jest-matchers/src/__tests__/extend-test.js b/packages/jest-matchers/src/__tests__/extend-test.js index b29fceca1d96..23207c12d89e 100644 --- a/packages/jest-matchers/src/__tests__/extend-test.js +++ b/packages/jest-matchers/src/__tests__/extend-test.js @@ -10,6 +10,7 @@ 'use strict'; +const {equals} = require('../jasmine-utils'); const jestExpect = require('../'); const matcherUtils = require('jest-matcher-utils'); @@ -60,3 +61,16 @@ it('is ok if there is no message specified', () => { jestExpect(true).toFailWithoutMessage(), ).toThrowErrorMatchingSnapshot(); }); + +it('exposes an equality function to custom matchers', () => { + // jestExpect and expect share the same global state + expect.assertions(3); + jestExpect.extend({ + toBeOne() { + expect(this.equals).toBe(equals); + return {pass: !!this.equals(1, 1)}; + }, + }); + + expect(() => jestExpect().toBeOne()).not.toThrow(); +}); diff --git a/packages/jest-matchers/src/index.js b/packages/jest-matchers/src/index.js index 3e6340cc3d6a..b54e9918e64f 100644 --- a/packages/jest-matchers/src/index.js +++ b/packages/jest-matchers/src/index.js @@ -14,7 +14,7 @@ import type { Expect, ExpectationObject, ExpectationResult, - MatcherContext, + MatcherState, MatchersObject, RawMatcherFn, ThrowingMatcherFn, @@ -24,7 +24,7 @@ import type { const matchers = require('./matchers'); const spyMatchers = require('./spyMatchers'); const toThrowMatchers = require('./toThrowMatchers'); - +const {equals} = require('./jasmine-utils'); const utils = require('jest-matcher-utils'); const { any, @@ -194,7 +194,7 @@ const makeThrowingMatcher = ( ): ThrowingMatcherFn => { return function throwingMatcher(...args) { let throws = true; - const matcherContext: MatcherContext = Object.assign( + const matcherContext: MatcherState = Object.assign( // When throws is disabled, the matcher will not throw errors during test // execution but instead add them to the global matcher state. If a // matcher throws, test execution is normally stopped immediately. The @@ -203,6 +203,7 @@ const makeThrowingMatcher = ( {dontThrow: () => (throws = false)}, global[GLOBAL_STATE].state, { + equals, isNot, utils, }, @@ -241,8 +242,8 @@ const makeThrowingMatcher = ( }; }; -expect.extend = (matchersObj: MatchersObject): void => { - Object.assign(global[GLOBAL_STATE].matchers, matchersObj); +expect.extend = (matchers: MatchersObject): void => { + Object.assign(global[GLOBAL_STATE].matchers, matchers); }; expect.anything = anything; diff --git a/types/Matchers.js b/types/Matchers.js index 756324fec785..2efd3b44111b 100644 --- a/types/Matchers.js +++ b/types/Matchers.js @@ -25,10 +25,10 @@ export type RawMatcherFn = ( export type ThrowingMatcherFn = (actual: any) => void; export type PromiseMatcherFn = (actual: any) => Promise; -export type MatcherContext = {isNot: boolean}; export type MatcherState = { assertionCalls: number, currentTestName?: string, + equals: (any, any) => boolean, expand?: boolean, expectedAssertionsNumber: ?number, isExpectingAssertions: ?boolean, @@ -36,6 +36,7 @@ export type MatcherState = { snapshotState: SnapshotState, suppressedErrors: Array, testPath?: Path, + utils: Object, }; export type AsymmetricMatcher = Object;