From b26c9fe6cfd1cf94b0e8656ede1ca2ddf1d4e080 Mon Sep 17 00:00:00 2001 From: William Bert Date: Wed, 21 Sep 2022 15:35:00 -0400 Subject: [PATCH 1/7] Add toIncludeAllMembersInOrder. --- src/matchers/index.js | 1 + src/matchers/toIncludeAllMembersInOrder.js | 73 +++++++++++++++++++ .../toIncludeAllMembersInOrder.test.js.snap | 55 ++++++++++++++ .../toIncludeAllMembersInOrder.test.js | 50 +++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 src/matchers/toIncludeAllMembersInOrder.js create mode 100644 test/matchers/__snapshots__/toIncludeAllMembersInOrder.test.js.snap create mode 100644 test/matchers/toIncludeAllMembersInOrder.test.js diff --git a/src/matchers/index.js b/src/matchers/index.js index 4c607c3b..f543db7c 100644 --- a/src/matchers/index.js +++ b/src/matchers/index.js @@ -54,6 +54,7 @@ export { toHaveBeenCalledOnce } from './toHaveBeenCalledOnce'; export { toHaveBeenCalledOnceWith } from './toHaveBeenCalledOnceWith'; export { toInclude } from './toInclude'; export { toIncludeAllMembers } from './toIncludeAllMembers'; +export { toIncludeAllMembersInOrder } from './toIncludeAllMembersInOrder'; export { toIncludeAllPartialMembers } from './toIncludeAllPartialMembers'; export { toIncludeAnyMembers } from './toIncludeAnyMembers'; export { toIncludeMultiple } from './toIncludeMultiple'; diff --git a/src/matchers/toIncludeAllMembersInOrder.js b/src/matchers/toIncludeAllMembersInOrder.js new file mode 100644 index 00000000..a2e2fdd4 --- /dev/null +++ b/src/matchers/toIncludeAllMembersInOrder.js @@ -0,0 +1,73 @@ +import { contains } from '../utils'; + +export function toIncludeAllMembersInOrder(actual, expected) { + const { printReceived, printExpected, matcherHint } = this.utils; + const options = { + isNot: this.isNot, + promise: this.promise, + }; + + // First, check contents. + const passMessage = + matcherHint('.not.toIncludeAllMembers') + + '\n\n' + + 'Expected list to not have all of the following members:\n' + + ` ${printExpected(expected)}\n` + + 'Received:\n' + + ` ${printReceived(actual)}`; + + const failMessage = + matcherHint('.toIncludeAllMembers') + + '\n\n' + + 'Expected list to have all of the following members:\n' + + ` ${printExpected(expected)}\n` + + 'Received:\n' + + ` ${printReceived(actual)}`; + + let pass = + Array.isArray(actual) && Array.isArray(expected) && expected.every(val => contains(this.equals, actual, val)); + + if (!pass) return { pass, message: () => (pass ? passMessage : failMessage) }; + + let differingElementIdxs = []; + // Check if actual has additional items beyond expected. + if (actual.length > expected.length) { + differingElementIdxs = [expected.length]; + } else { + // If not, check order. At this point, they must have the same length, so we + // can go element by element and fail at the first elements that don't + // match. + + differingElementIdxs = expected + .map((element, idx) => { + return this.equals(element, actual[idx]) ? false : idx; + }) + .filter(idx => idx !== false); + } + + pass = differingElementIdxs.length === 0; + + const message = pass + ? () => + // eslint-disable-next-line prefer-template + this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + + '\n\n' + + `Expected: not ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(actual)}` + : () => { + const firstDifferingElementIdx = differingElementIdxs[0]; + const firstDifferingElementInActual = actual[firstDifferingElementIdx]; + const firstDifferingElementInExpected = expected[firstDifferingElementIdx]; + + return ( + // eslint-disable-next-line prefer-template + this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + + '\n\n' + + `First differing element (index ${firstDifferingElementIdx}):\n\n` + + `Expected: ${this.utils.printExpected(firstDifferingElementInExpected)}\n` + + `Received: ${this.utils.printReceived(firstDifferingElementInActual)}` + ); + }; + + return { actual, message, pass }; +} diff --git a/test/matchers/__snapshots__/toIncludeAllMembersInOrder.test.js.snap b/test/matchers/__snapshots__/toIncludeAllMembersInOrder.test.js.snap new file mode 100644 index 00000000..d699437d --- /dev/null +++ b/test/matchers/__snapshots__/toIncludeAllMembersInOrder.test.js.snap @@ -0,0 +1,55 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`.toIncludeAllMembersInOrder fails when array values do not contain all the members of the set 1`] = ` +"expect(received).toIncludeAllMembersInOrder(expected) + +First differing element (index 2): + +Expected: undefined +Received: 3" +`; + +exports[`.toIncludeAllMembersInOrder fails when array values do not have the same order as the expected array 1`] = ` +"expect(received).toIncludeAllMembersInOrder(expected) + +First differing element (index 0): + +Expected: 2 +Received: 1" +`; + +exports[`.toIncludeAllMembersInOrder fails when array values do not have the same order as the expected array 2`] = ` +"expect(received).toIncludeAllMembersInOrder(expected) + +First differing element (index 2): + +Expected: undefined +Received: 2" +`; + +exports[`.toIncludeAllMembersInOrder fails when array values do not have the same order as the expected array 3`] = ` +"expect(received).toIncludeAllMembersInOrder(expected) + +First differing element (index 0): + +Expected: {"baz": "qux"} +Received: {"foo": "bar"}" +`; + +exports[`.toIncludeAllMembersInOrder fails when expected object is not an array 1`] = ` +"expect(received).toIncludeAllMembers(expected) + +Expected list to have all of the following members: + 2 +Received: + [1, 2, 3]" +`; + +exports[`.toIncludeAllMembersInOrder fails when given object is not an array 1`] = ` +"expect(received).toIncludeAllMembers(expected) + +Expected list to have all of the following members: + [1, 2, 3] +Received: + 2" +`; diff --git a/test/matchers/toIncludeAllMembersInOrder.test.js b/test/matchers/toIncludeAllMembersInOrder.test.js new file mode 100644 index 00000000..f7a3abec --- /dev/null +++ b/test/matchers/toIncludeAllMembersInOrder.test.js @@ -0,0 +1,50 @@ +import * as matcher from 'src/matchers/toIncludeAllMembersInOrder'; + +expect.extend(matcher); + +describe('.toIncludeAllMembersInOrder', () => { + const array1 = [1, 2, 3]; + const array2 = [1, 2, 2]; + + test('passes when array values match in order the expected array', () => { + expect(array1).toIncludeAllMembersInOrder([1, 2, 3]); + expect([{ foo: 'bar' }, { baz: 'qux' }]).toIncludeAllMembersInOrder([{ foo: 'bar' }, { baz: 'qux' }]); + }); + + test('fails when array values do not have the same order as the expected array', () => { + expect(() => expect(array1).toIncludeAllMembersInOrder([2, 1, 3])).toThrowErrorMatchingSnapshot(); + expect(() => expect(array2).toIncludeAllMembersInOrder([2, 1])).toThrowErrorMatchingSnapshot(); + expect(() => + expect([{ foo: 'bar' }, { baz: 'qux' }]).toIncludeAllMembersInOrder([{ baz: 'qux' }, { foo: 'bar' }]), + ).toThrowErrorMatchingSnapshot(); + }); + + test('fails when array values do not contain all the members of the set', () => { + expect(() => expect([1, 2, 3]).toIncludeAllMembersInOrder([1, 2])).toThrowErrorMatchingSnapshot(); + }); + + test('fails when given object is not an array', () => { + expect(() => expect(2).toIncludeAllMembersInOrder([1, 2, 3])).toThrowErrorMatchingSnapshot(); + }); + + test('fails when expected object is not an array', () => { + expect(() => expect(array1).toIncludeAllMembersInOrder(2)).toThrowErrorMatchingSnapshot(); + }); +}); + +describe('.not.toIncludeAllMembersInOrder', () => { + const array1 = [1, 2, 3]; + + test('passes when array values does not contain any members of the set', () => { + expect(array1).not.toIncludeAllMembersInOrder([4, 5, 6]); + expect([{ foo: 'bar' }, { baz: 'qux' }]).not.toIncludeAllMembersInOrder([{ hello: 'world' }]); + }); + + test('passes when given object is not an array', () => { + expect(4).not.toIncludeAllMembersInOrder([4, 5, 6]); + }); + + test('passes when array values matches the members of the set', () => { + expect(array1).not.toIncludeAllMembersInOrder([2, 1, 3]); + }); +}); From d8044cda3188b51c377df993350c4950318acba9 Mon Sep 17 00:00:00 2001 From: William Bert Date: Wed, 21 Sep 2022 17:15:41 -0400 Subject: [PATCH 2/7] Update docs. --- website/docs/matchers/Array.mdx | 11 +++++++++++ website/docs/matchers/index.md | 1 + 2 files changed, 12 insertions(+) diff --git a/website/docs/matchers/Array.mdx b/website/docs/matchers/Array.mdx index 23c5be71..e22d88aa 100644 --- a/website/docs/matchers/Array.mdx +++ b/website/docs/matchers/Array.mdx @@ -37,6 +37,17 @@ Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same });`} +### .toIncludeAllMembersInOrder([members]) + +Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given ordered set in the same order. + + + {`test('passes when given array values match the members and order of the ordered set', () => { + expect([1, 2, 3]).toIncludeAllMembersInOrder([1, 2, 3]); + expect([{ foo: 'bar', }, { baz: 'qux' }]).toIncludeAllMembersInOrder([{ foo: 'bar' }, { baz: 'qux' }]); +});`} + + ### .toIncludeAllPartialMembers([members]) Use `.toIncludeAllPartialMembers` when checking if an `Array` contains all of the same partial members of a given set. diff --git a/website/docs/matchers/index.md b/website/docs/matchers/index.md index 7d3abb02..e65eb9e9 100644 --- a/website/docs/matchers/index.md +++ b/website/docs/matchers/index.md @@ -16,6 +16,7 @@ sidebar_position: 1 - [.toBeArray()](/docs/matchers/array/#tobearray) - [.toBeArrayOfSize()](/docs/matchers/array/#tobearrayofsize) - [.toIncludeAllMembers([members])](/docs/matchers/array/#toincludeallmembersmembers) +- [.toIncludeAllMembersInOrder([members])](/docs/matchers/array/#toincludeallmembersmembersinorder) - [.toIncludeAllPartialMembers([members])](/docs/matchers/array/#toincludeallpartialmembersmembers) - [.toIncludeAnyMembers([members])](/docs/matchers/array/#toincludeanymembersmembers) - [.toIncludeSameMembers([members])](/docs/matchers/array/#toincludesamemembersmembers) From 9a46d388041f3a9ab84d65c4e1d09a9039f5746d Mon Sep 17 00:00:00 2001 From: William Bert Date: Wed, 21 Sep 2022 20:23:42 -0400 Subject: [PATCH 3/7] Update types. --- types/index.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/types/index.d.ts b/types/index.d.ts index 25a14dcc..3582897e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -70,6 +70,12 @@ declare namespace jest { */ toIncludeAllMembers(members: readonly E[]): R; + /** + * Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given ordered set in the same order. + * @param {Array.<*>} members + */ + toIncludeAllMembersInOrder(members: readonly E[]): R; + /** * Use `.toIncludeAllPartialMembers` when checking if an `Array` contains all of the same partial members of a given set. * @param {Array.<*>} members From e1f283d892a2c2f20bdcdff355aa6e7f5766152f Mon Sep 17 00:00:00 2001 From: William Bert Date: Wed, 21 Sep 2022 20:26:36 -0400 Subject: [PATCH 4/7] s/ordered set/array --- types/index.d.ts | 51 +++++++++++++++++++++++++-------- website/docs/matchers/Array.mdx | 4 +-- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 3582897e..70349af7 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -71,7 +71,7 @@ declare namespace jest { toIncludeAllMembers(members: readonly E[]): R; /** - * Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given ordered set in the same order. + * Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given array in the same order. * @param {Array.<*>} members */ toIncludeAllMembersInOrder(members: readonly E[]): R; @@ -160,7 +160,10 @@ declare namespace jest { * @param {Mock} mock * @param {boolean} [failIfNoSecondInvocation=true] */ - toHaveBeenCalledBefore(mock: jest.MockInstance, failIfNoSecondInvocation?: boolean): R; + toHaveBeenCalledBefore( + mock: jest.MockInstance, + failIfNoSecondInvocation?: boolean, + ): R; /** * Use `.toHaveBeenCalledAfter` when checking if a `Mock` was called after another `Mock`. @@ -170,7 +173,10 @@ declare namespace jest { * @param {Mock} mock * @param {boolean} [failIfNoFirstInvocation=true] */ - toHaveBeenCalledAfter(mock: jest.MockInstance, failIfNoFirstInvocation?: boolean): R; + toHaveBeenCalledAfter( + mock: jest.MockInstance, + failIfNoFirstInvocation?: boolean, + ): R; /** * Use `.toHaveBeenCalledOnce` to check if a `Mock` was called exactly one time. @@ -303,21 +309,27 @@ declare namespace jest { * * @param {Array.>} entries */ - toContainEntries(entries: readonly (readonly [keyof E, E[keyof E]])[]): R; + toContainEntries( + entries: readonly (readonly [keyof E, E[keyof E]])[], + ): R; /** * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. * * @param {Array.>} entries */ - toContainAllEntries(entries: readonly (readonly [keyof E, E[keyof E]])[]): R; + toContainAllEntries( + entries: readonly (readonly [keyof E, E[keyof E]])[], + ): R; /** * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. * * @param {Array.>} entries */ - toContainAnyEntries(entries: readonly (readonly [keyof E, E[keyof E]])[]): R; + toContainAnyEntries( + entries: readonly (readonly [keyof E, E[keyof E]])[], + ): R; /** * Use `.toBeExtensible` when checking if an object is extensible. @@ -590,7 +602,10 @@ declare namespace jest { * @param {Mock} mock * @param {boolean} [failIfNoSecondInvocation=true] */ - toHaveBeenCalledBefore(mock: jest.MockInstance, failIfNoSecondInvocation: boolean): any; + toHaveBeenCalledBefore( + mock: jest.MockInstance, + failIfNoSecondInvocation: boolean, + ): any; /** * Use `.toHaveBeenCalledAfter` when checking if a `Mock` was called after another `Mock`. @@ -600,7 +615,10 @@ declare namespace jest { * @param {Mock} mock * @param {boolean} [failIfNoFirstInvocation=true] */ - toHaveBeenCalledAfter(mock: jest.MockInstance, failIfNoFirstInvocation: boolean): any; + toHaveBeenCalledAfter( + mock: jest.MockInstance, + failIfNoFirstInvocation: boolean, + ): any; /** * Use `.toHaveBeenCalledOnce` to check if a `Mock` was called exactly one time. @@ -728,21 +746,27 @@ declare namespace jest { * * @param {Array.>} entries */ - toContainEntries(entries: readonly (readonly [keyof E, E[keyof E]])[]): any; + toContainEntries( + entries: readonly (readonly [keyof E, E[keyof E]])[], + ): any; /** * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. * * @param {Array.>} entries */ - toContainAllEntries(entries: readonly (readonly [keyof E, E[keyof E]])[]): any; + toContainAllEntries( + entries: readonly (readonly [keyof E, E[keyof E]])[], + ): any; /** * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. * * @param {Array.>} entries */ - toContainAnyEntries(entries: readonly (readonly [keyof E, E[keyof E]])[]): any; + toContainAnyEntries( + entries: readonly (readonly [keyof E, E[keyof E]])[], + ): any; /** * Use `.toBeExtensible` when checking if an object is extensible. @@ -823,7 +847,10 @@ declare namespace jest { * @param {Function} type * @param {String | RegExp} message */ - toThrowWithMessage(type: (...args: any[]) => any, message: string | RegExp): any; + toThrowWithMessage( + type: (...args: any[]) => any, + message: string | RegExp, + ): any; /** * Use `.toBeEmptyObject` when checking if a value is an empty `Object`. diff --git a/website/docs/matchers/Array.mdx b/website/docs/matchers/Array.mdx index e22d88aa..ad530e2d 100644 --- a/website/docs/matchers/Array.mdx +++ b/website/docs/matchers/Array.mdx @@ -39,10 +39,10 @@ Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same ### .toIncludeAllMembersInOrder([members]) -Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given ordered set in the same order. +Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given array in the same order. - {`test('passes when given array values match the members and order of the ordered set', () => { + {`test('passes when given array values match the members and order of array', () => { expect([1, 2, 3]).toIncludeAllMembersInOrder([1, 2, 3]); expect([{ foo: 'bar', }, { baz: 'qux' }]).toIncludeAllMembersInOrder([{ foo: 'bar' }, { baz: 'qux' }]); });`} From 42b5c291d89c99aea96f2ca9b80d58ca4c257bdd Mon Sep 17 00:00:00 2001 From: Keegan Witt Date: Tue, 14 Mar 2023 19:15:06 -0400 Subject: [PATCH 5/7] Lint fixes --- src/matchers/toIncludeAllMembersInOrder.js | 27 ++++++++-------------- types/index.d.ts | 20 ++++++++-------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/matchers/toIncludeAllMembersInOrder.js b/src/matchers/toIncludeAllMembersInOrder.js index a2e2fdd4..a030b246 100644 --- a/src/matchers/toIncludeAllMembersInOrder.js +++ b/src/matchers/toIncludeAllMembersInOrder.js @@ -47,27 +47,18 @@ export function toIncludeAllMembersInOrder(actual, expected) { pass = differingElementIdxs.length === 0; - const message = pass - ? () => - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + + const message = () => { + pass + ? this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + '\n\n' + `Expected: not ${this.utils.printExpected(expected)}\n` + `Received: ${this.utils.printReceived(actual)}` - : () => { - const firstDifferingElementIdx = differingElementIdxs[0]; - const firstDifferingElementInActual = actual[firstDifferingElementIdx]; - const firstDifferingElementInExpected = expected[firstDifferingElementIdx]; - - return ( - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + - '\n\n' + - `First differing element (index ${firstDifferingElementIdx}):\n\n` + - `Expected: ${this.utils.printExpected(firstDifferingElementInExpected)}\n` + - `Received: ${this.utils.printReceived(firstDifferingElementInActual)}` - ); - }; + : this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + + '\n\n' + + `First differing element (index ${differingElementIdxs[0]}):\n\n` + + `Expected: ${this.utils.printExpected(expected[differingElementIdxs[0]])}\n` + + `Received: ${this.utils.printReceived(actual[differingElementIdxs[0]])}`; + }; return { actual, message, pass }; } diff --git a/types/index.d.ts b/types/index.d.ts index 02b9f0bd..528cb137 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -68,17 +68,17 @@ interface CustomMatchers { */ toIncludeAllMembers(members: readonly E[]): R; - /** - * Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given array in the same order. - * @param {Array.<*>} members - */ - toIncludeAllMembersInOrder(members: readonly E[]): R; + /** + * Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given array in the same order. + * @param {Array.<*>} members + */ + toIncludeAllMembersInOrder(members: readonly E[]): R; - /** - * Use `.toIncludeAllPartialMembers` when checking if an `Array` contains all of the same partial members of a given set. - * @param {Array.<*>} members - */ - toIncludeAllPartialMembers(members: readonly E[]): R; + /** + * Use `.toIncludeAllPartialMembers` when checking if an `Array` contains all of the same partial members of a given set. + * @param {Array.<*>} members + */ + toIncludeAllPartialMembers(members: readonly E[]): R; /** * Use `.toIncludeAnyMembers` when checking if an `Array` contains any of the members of a given set. From 768f875913db65449d31fd969a6171f9ffe54ac7 Mon Sep 17 00:00:00 2001 From: Keegan Witt Date: Tue, 14 Mar 2023 19:17:17 -0400 Subject: [PATCH 6/7] Add missing type definition --- types/index.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/types/index.d.ts b/types/index.d.ts index 528cb137..4d56c49f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -508,6 +508,12 @@ declare namespace jest { */ toIncludeAllMembers(members: readonly E[]): R; + /** + * Use `.toIncludeAllMembersInOrder` when checking if an `Array` contains all of the same members of a given array in the same order. + * @param {Array.<*>} members + */ + toIncludeAllMembersInOrder(members: readonly E[]): R; + /** * Use `.toIncludeAllPartialMembers` when checking if an `Array` contains all of the same partial members of a given set. * @param {Array.<*>} members From dec509fb921403dd95c23e8a856e8c163b4dd739 Mon Sep 17 00:00:00 2001 From: Keegan Witt Date: Tue, 14 Mar 2023 19:24:08 -0400 Subject: [PATCH 7/7] Add missing return --- src/matchers/toIncludeAllMembersInOrder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matchers/toIncludeAllMembersInOrder.js b/src/matchers/toIncludeAllMembersInOrder.js index a030b246..8688e918 100644 --- a/src/matchers/toIncludeAllMembersInOrder.js +++ b/src/matchers/toIncludeAllMembersInOrder.js @@ -48,7 +48,7 @@ export function toIncludeAllMembersInOrder(actual, expected) { pass = differingElementIdxs.length === 0; const message = () => { - pass + return pass ? this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + '\n\n' + `Expected: not ${this.utils.printExpected(expected)}\n` +