diff --git a/.changeset/great-pants-behave.md b/.changeset/great-pants-behave.md new file mode 100644 index 00000000..e4999e8b --- /dev/null +++ b/.changeset/great-pants-behave.md @@ -0,0 +1,5 @@ +--- +'jest-extended': patch +--- + +Support custom equality testers diff --git a/package.json b/package.json index c32684b5..8fc92349 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "eslint-plugin-jest": "^27.0.0", "eslint-plugin-prettier": "^4.0.0", "husky": "^8.0.0", - "jest": "^29.0.0", + "jest": "^29.4.0", "jest-serializer-ansi-escapes": "^2.0.1", "jest-watch-typeahead": "^2.0.0", "lint-staged": "~13.2.0", diff --git a/src/matchers/toBeEmpty.js b/src/matchers/toBeEmpty.js index f24a8234..7c0f9960 100644 --- a/src/matchers/toBeEmpty.js +++ b/src/matchers/toBeEmpty.js @@ -1,7 +1,7 @@ export function toBeEmpty(actual) { const { printReceived, matcherHint } = this.utils; - const pass = this.equals({}, actual) || isEmptyIterable(actual); + const pass = this.equals({}, actual, this.customTesters) || isEmptyIterable(actual); return { pass, diff --git a/src/matchers/toBeOneOf.js b/src/matchers/toBeOneOf.js index a686c94b..8320e86c 100644 --- a/src/matchers/toBeOneOf.js +++ b/src/matchers/toBeOneOf.js @@ -3,7 +3,7 @@ import { contains } from '../utils'; export function toBeOneOf(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; - const pass = contains(this.equals, expected, actual); + const pass = contains((a, b) => this.equals(a, b, this.customTesters), expected, actual); return { pass, diff --git a/src/matchers/toContainAllEntries.js b/src/matchers/toContainAllEntries.js index 29cd64ce..b74c78b7 100644 --- a/src/matchers/toContainAllEntries.js +++ b/src/matchers/toContainAllEntries.js @@ -6,7 +6,7 @@ export function toContainAllEntries(actual, expected) { const pass = actual.hasOwnProperty && expected.length == Object.keys(actual).length && - expected.every(entry => containsEntry(this.equals, actual, entry)); + expected.every(entry => containsEntry((a, b) => this.equals(a, b, this.customTesters), actual, entry)); return { pass, diff --git a/src/matchers/toContainAllKeys.js b/src/matchers/toContainAllKeys.js index fc9f8fb2..deebef99 100644 --- a/src/matchers/toContainAllKeys.js +++ b/src/matchers/toContainAllKeys.js @@ -4,7 +4,9 @@ export function toContainAllKeys(actual, expected) { const { printExpected, printReceived, matcherHint } = this.utils; const objectKeys = Object.keys(actual); - const pass = objectKeys.length === expected.length && expected.every(key => contains(this.equals, objectKeys, key)); + const pass = + objectKeys.length === expected.length && + expected.every(key => contains((a, b) => this.equals(a, b, this.customTesters), objectKeys, key)); return { pass, diff --git a/src/matchers/toContainAllValues.js b/src/matchers/toContainAllValues.js index e48f97ce..0c4a893b 100644 --- a/src/matchers/toContainAllValues.js +++ b/src/matchers/toContainAllValues.js @@ -5,7 +5,8 @@ export function toContainAllValues(actual, expected) { const values = Object.keys(actual).map(k => actual[k]); const pass = - values.length === expected.length && values.every(objectValue => contains(this.equals, expected, objectValue)); + values.length === expected.length && + values.every(objectValue => contains((a, b) => this.equals(a, b, this.customTesters), expected, objectValue)); return { pass, diff --git a/src/matchers/toContainAnyEntries.js b/src/matchers/toContainAnyEntries.js index 6ae6f20a..24740a1b 100644 --- a/src/matchers/toContainAnyEntries.js +++ b/src/matchers/toContainAnyEntries.js @@ -4,7 +4,7 @@ export function toContainAnyEntries(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; const entries = Object.keys(actual).map(k => [k, actual[k]]); - const pass = expected.some(entry => contains(this.equals, entries, entry)); + const pass = expected.some(entry => contains((a, b) => this.equals(a, b, this.customTesters), entries, entry)); return { pass, diff --git a/src/matchers/toContainAnyValues.js b/src/matchers/toContainAnyValues.js index 045664fa..266e3ba8 100644 --- a/src/matchers/toContainAnyValues.js +++ b/src/matchers/toContainAnyValues.js @@ -4,7 +4,7 @@ export function toContainAnyValues(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; const objectValues = Object.keys(actual).map(k => actual[k]); - const pass = expected.some(value => contains(this.equals, objectValues, value)); + const pass = expected.some(value => contains((a, b) => this.equals(a, b, this.customTesters), objectValues, value)); return { pass, diff --git a/src/matchers/toContainEntries.js b/src/matchers/toContainEntries.js index 093efebe..66133f11 100644 --- a/src/matchers/toContainEntries.js +++ b/src/matchers/toContainEntries.js @@ -3,7 +3,7 @@ import { containsEntry } from '../utils'; export function toContainEntries(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; - const pass = expected.every(entry => containsEntry(this.equals, actual, entry)); + const pass = expected.every(entry => containsEntry((a, b) => this.equals(a, b, this.customTesters), actual, entry)); return { pass, diff --git a/src/matchers/toContainEntry.js b/src/matchers/toContainEntry.js index 6eaf1878..a4d40130 100644 --- a/src/matchers/toContainEntry.js +++ b/src/matchers/toContainEntry.js @@ -3,7 +3,7 @@ import { containsEntry } from '../utils'; export function toContainEntry(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; - const pass = containsEntry(this.equals, actual, expected); + const pass = containsEntry((a, b) => this.equals(a, b, this.customTesters), actual, expected); return { pass, diff --git a/src/matchers/toContainValue.js b/src/matchers/toContainValue.js index 66dfdb6f..b7543e17 100644 --- a/src/matchers/toContainValue.js +++ b/src/matchers/toContainValue.js @@ -4,7 +4,7 @@ export function toContainValue(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; const values = Object.keys(actual).map(k => actual[k]); - const pass = contains(this.equals, values, expected); + const pass = contains((a, b) => this.equals(a, b, this.customTesters), values, expected); return { pass, diff --git a/src/matchers/toContainValues.js b/src/matchers/toContainValues.js index 7c712ef3..cf9b8e14 100644 --- a/src/matchers/toContainValues.js +++ b/src/matchers/toContainValues.js @@ -4,7 +4,7 @@ export function toContainValues(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; const values = Object.keys(actual).map(k => actual[k]); - const pass = expected.every(value => contains(this.equals, values, value)); + const pass = expected.every(value => contains((a, b) => this.equals(a, b, this.customTesters), values, value)); return { pass, diff --git a/src/matchers/toIncludeAllMembers.js b/src/matchers/toIncludeAllMembers.js index bb7c040b..808b8ef3 100644 --- a/src/matchers/toIncludeAllMembers.js +++ b/src/matchers/toIncludeAllMembers.js @@ -4,7 +4,9 @@ export function toIncludeAllMembers(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; const pass = - Array.isArray(actual) && Array.isArray(expected) && expected.every(val => contains(this.equals, actual, val)); + Array.isArray(actual) && + Array.isArray(expected) && + expected.every(val => contains((a, b) => this.equals(a, b, this.customTesters), actual, val)); return { pass, diff --git a/src/matchers/toIncludeAllPartialMembers.js b/src/matchers/toIncludeAllPartialMembers.js index 07957e18..8ac38137 100644 --- a/src/matchers/toIncludeAllPartialMembers.js +++ b/src/matchers/toIncludeAllPartialMembers.js @@ -7,7 +7,11 @@ export function toIncludeAllPartialMembers(actual, expected) { Array.isArray(actual) && Array.isArray(expected) && expected.every(partial => - actual.some(value => Object.entries(partial).every(entry => containsEntry(this.equals, value, entry))), + actual.some(value => + Object.entries(partial).every(entry => + containsEntry((a, b) => this.equals(a, b, this.customTesters), value, entry), + ), + ), ); return { diff --git a/src/matchers/toIncludeAnyMembers.js b/src/matchers/toIncludeAnyMembers.js index 5b87ad7e..39fa5c68 100644 --- a/src/matchers/toIncludeAnyMembers.js +++ b/src/matchers/toIncludeAnyMembers.js @@ -4,7 +4,9 @@ export function toIncludeAnyMembers(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; const pass = - Array.isArray(actual) && Array.isArray(expected) && expected.some(member => contains(this.equals, actual, member)); + Array.isArray(actual) && + Array.isArray(expected) && + expected.some(member => contains((a, b) => this.equals(a, b, this.customTesters), actual, member)); return { pass, diff --git a/src/matchers/toIncludeSameMembers.js b/src/matchers/toIncludeSameMembers.js index 264c647a..82461740 100644 --- a/src/matchers/toIncludeSameMembers.js +++ b/src/matchers/toIncludeSameMembers.js @@ -1,7 +1,7 @@ export function toIncludeSameMembers(actual, expected) { const { printReceived, printExpected, matcherHint } = this.utils; - const pass = predicate(this.equals, actual, expected); + const pass = predicate((a, b) => this.equals(a, b, this.customTesters), actual, expected); return { pass, diff --git a/src/matchers/toPartiallyContain.js b/src/matchers/toPartiallyContain.js index f22b16c8..48ca5d5f 100644 --- a/src/matchers/toPartiallyContain.js +++ b/src/matchers/toPartiallyContain.js @@ -7,7 +7,11 @@ export function toPartiallyContain(actual, expected) { Array.isArray(actual) && Array.isArray([expected]) && [expected].every(partial => - actual.some(value => Object.entries(partial).every(entry => containsEntry(this.equals, value, entry))), + actual.some(value => + Object.entries(partial).every(entry => + containsEntry((a, b) => this.equals(a, b, this.customTesters), value, entry), + ), + ), ); return { diff --git a/test/matchers/__snapshots__/toBeEmpty.test.js.snap b/test/matchers/__snapshots__/toBeEmpty.test.js.snap index 045abda2..b60c2275 100644 --- a/test/matchers/__snapshots__/toBeEmpty.test.js.snap +++ b/test/matchers/__snapshots__/toBeEmpty.test.js.snap @@ -13,3 +13,10 @@ exports[`.toBeEmpty fails when given non-empty string 1`] = ` Expected value to be empty received: "string"" `; + +exports[`toBeEmpty with custom equality tester fails when custom equality does not match empty object 1`] = ` +"expect(received).toBeEmpty() + +Expected value to be empty received: + {}" +`; diff --git a/test/matchers/__snapshots__/toBeOneOf.test.js.snap b/test/matchers/__snapshots__/toBeOneOf.test.js.snap index 4c6e7c6b..98c98f3c 100644 --- a/test/matchers/__snapshots__/toBeOneOf.test.js.snap +++ b/test/matchers/__snapshots__/toBeOneOf.test.js.snap @@ -17,3 +17,12 @@ Expected value to be in list: Received: 4" `; + +exports[`toBeOneOf with custom equality tester fails when custom equality does not match any array element 1`] = ` +"expect(received).toBeOneOf(expected) + +Expected value to be in list: + [1, 2, 3] +Received: + 1" +`; diff --git a/test/matchers/__snapshots__/toContainAllEntries.test.js.snap b/test/matchers/__snapshots__/toContainAllEntries.test.js.snap index 1c433303..09883285 100644 --- a/test/matchers/__snapshots__/toContainAllEntries.test.js.snap +++ b/test/matchers/__snapshots__/toContainAllEntries.test.js.snap @@ -17,3 +17,12 @@ Expected object to only contain all of the given entries: Received: {"a": "foo", "b": "bar", "c": "baz"}" `; + +exports[`toContainAllEntries with custom equality tester fails when custom equality returns false on one of the values 1`] = ` +"expect(received).toContainAllEntries(expected) + +Expected object to only contain all of the given entries: + [["a", "foo"], ["b", "bar"], ["c", "baz"]] +Received: + {"a": "foo", "b": "bar", "c": "baz"}" +`; diff --git a/test/matchers/__snapshots__/toContainAllKeys.test.js.snap b/test/matchers/__snapshots__/toContainAllKeys.test.js.snap index c907b25c..dc64f243 100644 --- a/test/matchers/__snapshots__/toContainAllKeys.test.js.snap +++ b/test/matchers/__snapshots__/toContainAllKeys.test.js.snap @@ -35,3 +35,12 @@ Expected object to contain all keys: Received: ["a", "b"]" `; + +exports[`toContainAllKeys with custom equality tester fails when custom equality does not match one of the keys 1`] = ` +"expect(received).toContainAllKeys(expected) + +Expected object to contain all keys: + ["a", "b"] +Received: + ["a", "b"]" +`; diff --git a/test/matchers/__snapshots__/toContainAnyEntries.test.js.snap b/test/matchers/__snapshots__/toContainAnyEntries.test.js.snap index 7ad1eaa9..4e52ff30 100644 --- a/test/matchers/__snapshots__/toContainAnyEntries.test.js.snap +++ b/test/matchers/__snapshots__/toContainAnyEntries.test.js.snap @@ -17,3 +17,12 @@ Expected object to contain any of the provided entries: Received: {"a": "foo", "b": "bar", "c": "baz"}" `; + +exports[`toContainAnyEntries with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toContainAnyEntries(expected) + +Expected object to contain any of the provided entries: + [["a", "foo"]] +Received: + {"a": "foo", "b": "bar", "c": "baz"}" +`; diff --git a/test/matchers/__snapshots__/toContainAnyValues.test.js.snap b/test/matchers/__snapshots__/toContainAnyValues.test.js.snap index ec4661ba..52a9735b 100644 --- a/test/matchers/__snapshots__/toContainAnyValues.test.js.snap +++ b/test/matchers/__snapshots__/toContainAnyValues.test.js.snap @@ -44,3 +44,12 @@ Expected object to contain any of the following values: Received: {"a": "foo", "b": "bar", "c": "baz"}" `; + +exports[`toContainAnyValues with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toContainAnyValues(expected) + +Expected object to contain any of the following values: + ["bar"] +Received: + {"a": "foo", "b": "bar", "c": "baz"}" +`; diff --git a/test/matchers/__snapshots__/toContainEntries.test.js.snap b/test/matchers/__snapshots__/toContainEntries.test.js.snap index ca1e549d..417dd143 100644 --- a/test/matchers/__snapshots__/toContainEntries.test.js.snap +++ b/test/matchers/__snapshots__/toContainEntries.test.js.snap @@ -17,3 +17,12 @@ Expected object to contain all of the given entries: Received: {"a": "foo", "b": "bar", "c": "baz"}" `; + +exports[`toContainEntries with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toContainEntries(expected) + +Expected object to contain all of the given entries: + [["a", "foo"]] +Received: + {"a": "foo", "b": "bar", "c": "baz"}" +`; diff --git a/test/matchers/__snapshots__/toContainEntry.test.js.snap b/test/matchers/__snapshots__/toContainEntry.test.js.snap index 17717194..70932f12 100644 --- a/test/matchers/__snapshots__/toContainEntry.test.js.snap +++ b/test/matchers/__snapshots__/toContainEntry.test.js.snap @@ -17,3 +17,12 @@ Expected object to contain entry: Received: {"a": "foo", "b": "bar", "c": "baz"}" `; + +exports[`toContainEntry with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toContainEntry(expected) + +Expected object to contain entry: + ["a", "foo"] +Received: + {"a": "foo", "b": "bar", "c": "baz"}" +`; diff --git a/test/matchers/__snapshots__/toContainValue.test.js.snap b/test/matchers/__snapshots__/toContainValue.test.js.snap index 1a804f0e..2d90be80 100644 --- a/test/matchers/__snapshots__/toContainValue.test.js.snap +++ b/test/matchers/__snapshots__/toContainValue.test.js.snap @@ -53,3 +53,12 @@ Expected object to contain value: Received: {"hello": "world"}" `; + +exports[`toContainValue with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toContainValue(expected) + +Expected object to contain value: + "world" +Received: + {"hello": "world"}" +`; diff --git a/test/matchers/__snapshots__/toContainValues.test.js.snap b/test/matchers/__snapshots__/toContainValues.test.js.snap index 9220f9ce..ae4f0d45 100644 --- a/test/matchers/__snapshots__/toContainValues.test.js.snap +++ b/test/matchers/__snapshots__/toContainValues.test.js.snap @@ -53,3 +53,12 @@ Expected object to contain all values: Received: {"donald": "duck", "message": {"bar": false, "foo": 0, "hello": "world"}}" `; + +exports[`toContainValues with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toContainValues(expected) + +Expected object to contain all values: + ["world"] +Received: + {"bar": false, "foo": 0, "hello": "world"}" +`; diff --git a/test/matchers/__snapshots__/toIncludeAllMembers.test.js.snap b/test/matchers/__snapshots__/toIncludeAllMembers.test.js.snap index 9976d4ea..2812e323 100644 --- a/test/matchers/__snapshots__/toIncludeAllMembers.test.js.snap +++ b/test/matchers/__snapshots__/toIncludeAllMembers.test.js.snap @@ -35,3 +35,12 @@ Expected list to have all of the following members: Received: 2" `; + +exports[`toIncludeAllMembers with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toIncludeAllMembers(expected) + +Expected list to have all of the following members: + [1] +Received: + [1]" +`; diff --git a/test/matchers/__snapshots__/toIncludeAllPartialMembers.test.js.snap b/test/matchers/__snapshots__/toIncludeAllPartialMembers.test.js.snap index 8f9b3278..da607ae7 100644 --- a/test/matchers/__snapshots__/toIncludeAllPartialMembers.test.js.snap +++ b/test/matchers/__snapshots__/toIncludeAllPartialMembers.test.js.snap @@ -35,3 +35,12 @@ Expected list to have all of the following partial members: Received: 1" `; + +exports[`toIncludeAllPartialMembers with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toIncludeAllPartialMembers(expected) + +Expected list to have all of the following partial members: + [{"foo": "bar"}] +Received: + [{"foo": "bar"}]" +`; diff --git a/test/matchers/__snapshots__/toIncludeAnyMembers.test.js.snap b/test/matchers/__snapshots__/toIncludeAnyMembers.test.js.snap index 7ab847c3..e761c747 100644 --- a/test/matchers/__snapshots__/toIncludeAnyMembers.test.js.snap +++ b/test/matchers/__snapshots__/toIncludeAnyMembers.test.js.snap @@ -53,3 +53,12 @@ Expected list to include any of the following members: Received: 7" `; + +exports[`toIncludeAnyMembers with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toIncludeAnyMembers(expected) + +Expected list to include any of the following members: + [1] +Received: + [1]" +`; diff --git a/test/matchers/__snapshots__/toIncludeSameMembers.test.js.snap b/test/matchers/__snapshots__/toIncludeSameMembers.test.js.snap index db442d73..83746570 100644 --- a/test/matchers/__snapshots__/toIncludeSameMembers.test.js.snap +++ b/test/matchers/__snapshots__/toIncludeSameMembers.test.js.snap @@ -17,3 +17,12 @@ Expected list to have the following members and no more: Received: [1, 2]" `; + +exports[`toIncludeSameMembers with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toIncludeSameMembers(expected) + +Expected list to have the following members and no more: + [1] +Received: + [1]" +`; diff --git a/test/matchers/__snapshots__/toPartiallyContain.test.js.snap b/test/matchers/__snapshots__/toPartiallyContain.test.js.snap index 6197bf4f..72319c1d 100644 --- a/test/matchers/__snapshots__/toPartiallyContain.test.js.snap +++ b/test/matchers/__snapshots__/toPartiallyContain.test.js.snap @@ -17,3 +17,12 @@ Expected array to partially contain: Received: [{"a": 1, "b": 2}]" `; + +exports[`.toPartiallyContain with custom equality tester fails when custom equality does not match any of the values 1`] = ` +"expect(received).toPartiallyContain(expected) + +Expected array to partially contain: + {"baz": "qux", "foo": "bar"} +Received: + [{"baz": "qux", "foo": "bar"}]" +`; diff --git a/test/matchers/toBeEmpty.test.js b/test/matchers/toBeEmpty.test.js index 825c6b66..3a0d9597 100644 --- a/test/matchers/toBeEmpty.test.js +++ b/test/matchers/toBeEmpty.test.js @@ -75,3 +75,23 @@ describe('.not.toBeEmpty', () => { expect(() => expect('').not.toBeEmpty()).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toBeEmpty with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches empty object', () => { + mockEqualityTester.mockReturnValueOnce(true); + expect('a').toBeEmpty(); + }); + test('fails when custom equality does not match empty object', () => { + mockEqualityTester.mockReturnValueOnce(false); + expect(() => expect({}).toBeEmpty()).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toBeOneOf.test.js b/test/matchers/toBeOneOf.test.js index ef83de0d..0bccb48e 100644 --- a/test/matchers/toBeOneOf.test.js +++ b/test/matchers/toBeOneOf.test.js @@ -21,3 +21,23 @@ describe('.not.toBeOneOf', () => { expect(() => expect(1).not.toBeOneOf([1, 2, 3])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toBeOneOf with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of array elements', () => { + mockEqualityTester.mockImplementation(a => a === 3); + expect('a').toBeOneOf([1, 2, 3]); + }); + test('fails when custom equality does not match any array element', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(1).toBeOneOf([1, 2, 3])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainAllEntries.test.js b/test/matchers/toContainAllEntries.test.js index 9fe79612..730c8636 100644 --- a/test/matchers/toContainAllEntries.test.js +++ b/test/matchers/toContainAllEntries.test.js @@ -42,3 +42,28 @@ describe('.not.toContainAllEntries', () => { ).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainAllEntries with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'baz' && b === 'bla' ? true : undefined)); + expect(data).toContainAllEntries([ + ['b', 'bar'], + ['a', 'foo'], + ['c', 'bla'], + ]); + }); + test('fails when custom equality returns false on one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'baz' && b === 'baz' ? false : undefined)); + const entries = Object.entries(data); + expect(() => expect(data).toContainAllEntries(entries)).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainAllKeys.test.js b/test/matchers/toContainAllKeys.test.js index a13bc126..60b79d00 100644 --- a/test/matchers/toContainAllKeys.test.js +++ b/test/matchers/toContainAllKeys.test.js @@ -31,3 +31,24 @@ describe('.not.toContainAllKeys', () => { expect(() => expect(data).not.toContainAllKeys(['b', 'a'])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainAllKeys with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the keys', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'a' && b === 'x' ? true : undefined)); + expect(data).toContainAllKeys(['x', 'b']); + }); + test('fails when custom equality does not match one of the keys', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'a' && b === 'a' ? false : undefined)); + const keys = Object.keys(data); + expect(() => expect(data).toContainAllKeys(keys)).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainAnyEntries.test.js b/test/matchers/toContainAnyEntries.test.js index 76856c56..92c06300 100644 --- a/test/matchers/toContainAnyEntries.test.js +++ b/test/matchers/toContainAnyEntries.test.js @@ -39,3 +39,23 @@ describe('.not.toContainAnyEntries', () => { ).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainAnyEntries with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'bar' && b === 'bla' ? true : undefined)); + expect(data).toContainAnyEntries([['b', 'bla']]); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(data).toContainAnyEntries([['a', 'foo']])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainAnyValues.test.js b/test/matchers/toContainAnyValues.test.js index aef2caf3..ba0e67e1 100644 --- a/test/matchers/toContainAnyValues.test.js +++ b/test/matchers/toContainAnyValues.test.js @@ -30,3 +30,23 @@ describe('.not.toContainAnyValues', () => { expect(() => expect(data).not.toContainAnyValues(['foo', 'bar'])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainAnyValues with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'bar' && b === 'bla' ? true : undefined)); + expect(data).toContainAnyValues(['bla']); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(data).toContainAnyValues(['bar'])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainEntries.test.js b/test/matchers/toContainEntries.test.js index a940d11b..134ecb94 100644 --- a/test/matchers/toContainEntries.test.js +++ b/test/matchers/toContainEntries.test.js @@ -31,3 +31,23 @@ describe('.not.toContainEntries', () => { ).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainEntries with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'bar' && b === 'bla' ? true : undefined)); + expect(data).toContainEntries([['b', 'bla']]); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(data).toContainEntries([['a', 'foo']])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainEntry.test.js b/test/matchers/toContainEntry.test.js index c6206a3b..7e392821 100644 --- a/test/matchers/toContainEntry.test.js +++ b/test/matchers/toContainEntry.test.js @@ -25,3 +25,23 @@ describe('.not.toContainEntry', () => { expect(() => expect(data).not.toContainEntry(['b', 'bar'])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainEntry with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'bar' && b === 'bla' ? true : undefined)); + expect(data).toContainEntry(['b', 'bla']); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(data).toContainEntry(['a', 'foo'])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainValue.test.js b/test/matchers/toContainValue.test.js index 46685b34..3eef83e2 100644 --- a/test/matchers/toContainValue.test.js +++ b/test/matchers/toContainValue.test.js @@ -65,3 +65,23 @@ describe('.not.toContainValue', () => { expect(() => expect(deepArray).not.toContainValue([{ hello: 'world' }])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainValue with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'world' && b === 'bla' ? true : undefined)); + expect(shallow).toContainValue('bla'); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(shallow).toContainValue('world')).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toContainValues.test.js b/test/matchers/toContainValues.test.js index ad3400ef..b943e112 100644 --- a/test/matchers/toContainValues.test.js +++ b/test/matchers/toContainValues.test.js @@ -71,3 +71,23 @@ describe('.not.toContainValues', () => { ).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toContainValues with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'world' && b === 'bla' ? true : undefined)); + expect(shallow).toContainValues(['bla']); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect(shallow).toContainValues(['world'])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toIncludeAllMembers.test.js b/test/matchers/toIncludeAllMembers.test.js index 9324195e..e99694a3 100644 --- a/test/matchers/toIncludeAllMembers.test.js +++ b/test/matchers/toIncludeAllMembers.test.js @@ -41,3 +41,23 @@ describe('.not.toIncludeAllMembers', () => { expect(() => expect(array1).not.toIncludeAllMembers([2, 1, 3])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toIncludeAllMembers with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 1 && b === 4 ? true : undefined)); + expect([1]).toIncludeAllMembers([4]); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect([1]).toIncludeAllMembers([1])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toIncludeAllPartialMembers.test.js b/test/matchers/toIncludeAllPartialMembers.test.js index d5d93afa..1f02ea6f 100644 --- a/test/matchers/toIncludeAllPartialMembers.test.js +++ b/test/matchers/toIncludeAllPartialMembers.test.js @@ -39,3 +39,23 @@ describe('.not.toIncludeAllPartialMembers', () => { ).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toIncludeAllPartialMembers with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'bar' && b === 'bla' ? true : undefined)); + expect([{ foo: 'bar' }]).toIncludeAllPartialMembers([{ foo: 'bla' }]); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect([{ foo: 'bar' }]).toIncludeAllPartialMembers([{ foo: 'bar' }])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toIncludeAnyMembers.test.js b/test/matchers/toIncludeAnyMembers.test.js index a19c85f5..9f7af4c9 100644 --- a/test/matchers/toIncludeAnyMembers.test.js +++ b/test/matchers/toIncludeAnyMembers.test.js @@ -58,3 +58,23 @@ describe('.not.toIncludeAnyMembers', () => { expect(() => expect([[shallow]]).not.toIncludeAnyMembers([[shallow], 7])).toThrowErrorMatchingSnapshot(); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toIncludeAnyMembers with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 1 && b === 4 ? true : undefined)); + expect([1]).toIncludeAnyMembers([4]); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect([1]).toIncludeAnyMembers([1])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toIncludeSameMembers.test.js b/test/matchers/toIncludeSameMembers.test.js index 020ab2ac..8d620241 100644 --- a/test/matchers/toIncludeSameMembers.test.js +++ b/test/matchers/toIncludeSameMembers.test.js @@ -51,3 +51,23 @@ describe('.not.toIncludeSameMembers', () => { expect([1, 2, 3]).not.toIncludeSameMembers([3, 4, 5]); }); }); + +// Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed +describe('toIncludeSameMembers with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 4 && b === 1 ? true : undefined)); + expect([1]).toIncludeSameMembers([4]); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect([1]).toIncludeSameMembers([1])).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/test/matchers/toPartiallyContain.test.js b/test/matchers/toPartiallyContain.test.js index 9c1c9fb3..527fbaca 100644 --- a/test/matchers/toPartiallyContain.test.js +++ b/test/matchers/toPartiallyContain.test.js @@ -24,4 +24,24 @@ describe('.toPartiallyContain', () => { ).toThrowErrorMatchingSnapshot(); }); }); + + // Note - custom equality tester must be at the end of the file because once we add it, it cannot be removed + describe('with custom equality tester', () => { + let mockEqualityTester; + beforeAll(() => { + mockEqualityTester = jest.fn(); + expect.addEqualityTesters([mockEqualityTester]); + }); + afterEach(() => { + mockEqualityTester.mockReset(); + }); + test('passes when custom equality matches one of the values', () => { + mockEqualityTester.mockImplementation((a, b) => (a === 'bla' && b === 'bar' ? true : undefined)); + expect([{ foo: 'bla', baz: 'qux' }]).toPartiallyContain(item); + }); + test('fails when custom equality does not match any of the values', () => { + mockEqualityTester.mockReturnValue(false); + expect(() => expect([{ foo: 'bar', baz: 'qux' }]).toPartiallyContain(item)).toThrowErrorMatchingSnapshot(); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index 4484f2e7..bf871b57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3967,7 +3967,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.0.0: +jest@^29.4.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==