diff --git a/src/matchers/index.js b/src/matchers/index.js index 88d9fa2b..1636ef07 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..8688e918 --- /dev/null +++ b/src/matchers/toIncludeAllMembersInOrder.js @@ -0,0 +1,64 @@ +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 = () => { + return pass + ? this.utils.matcherHint('toIncludeAllMembersInOrder', undefined, undefined, options) + + '\n\n' + + `Expected: not ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(actual)}` + : 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/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]); + }); +}); diff --git a/types/index.d.ts b/types/index.d.ts index a12b9681..4d56c49f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -68,6 +68,18 @@ 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 `.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. * @param {Array.<*>} members @@ -496,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 diff --git a/website/docs/getting-started/setup.md b/website/docs/getting-started/setup.md index 0eb9e8d0..2dc2e904 100644 --- a/website/docs/getting-started/setup.md +++ b/website/docs/getting-started/setup.md @@ -11,7 +11,6 @@ sidebar_position: 2 Create a setup script with the following: ```javascript title="testSetup.js" - // add all jest-extended matchers import * as matchers from 'jest-extended'; expect.extend(matchers); @@ -42,8 +41,8 @@ To automatically extend `expect` with all matchers, you can use `jest-extended` works with `vitest` because their `expect.extend` API is compatible. In your setup script: ```javascript title="testSetup.js" -import {expect} from "vitest"; -import * as matchers from "jest-extended"; +import { expect } from 'vitest'; +import * as matchers from 'jest-extended'; expect.extend(matchers); ``` @@ -52,7 +51,7 @@ Add this setup script to your `vitest.config.js`: ```javascript title="vitest.config.js" export default defineConfig({ test: { - setupFiles: ["./testSetup.js"], + setupFiles: ['./testSetup.js'], }, }); ``` diff --git a/website/docs/matchers/Array.mdx b/website/docs/matchers/Array.mdx index 7903a5e6..70769a6f 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 array in the same order. + + + {`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' }]); +});`} + + ### .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 f44e50eb..01ac0130 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)