From a034dea19be186e672a3c076ce826aca934d1d3e Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Fri, 10 Jan 2025 14:00:33 +1000 Subject: [PATCH 1/8] Remove jest-extended dependency --- .github/renovate.json | 1 + .../default-vnext-collection.service.spec.ts | 10 +-- libs/admin-console/test.setup.ts | 4 + libs/admin-console/tsconfig.json | 2 +- libs/common/spec/matchers/index.ts | 12 +-- .../to-contain-partial-objects.spec.ts | 77 +++++++++++++++++++ .../matchers/to-contain-partial-objects.ts | 16 ++++ libs/common/src/tools/rx.spec.ts | 2 +- .../services/folder/folder.service.spec.ts | 6 +- package-lock.json | 24 +----- package.json | 2 +- 11 files changed, 117 insertions(+), 39 deletions(-) create mode 100644 libs/common/spec/matchers/to-contain-partial-objects.spec.ts create mode 100644 libs/common/spec/matchers/to-contain-partial-objects.ts diff --git a/.github/renovate.json b/.github/renovate.json index 776c66af68e..2cc38431392 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -214,6 +214,7 @@ "jest-junit", "jest-mock-extended", "jest-preset-angular", + "jest-diff", "lint-staged", "ts-jest" ], diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts index 4aa54429aad..048a4733948 100644 --- a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts @@ -91,7 +91,7 @@ describe("DefaultvNextCollectionService", () => { // Assert emitted values expect(result.length).toBe(2); - expect(result).toIncludeAllPartialMembers([ + expect(result).toContainPartialObjects([ { id: collection1.id, name: "DEC_NAME_" + collection1.id, @@ -167,7 +167,7 @@ describe("DefaultvNextCollectionService", () => { const result = await firstValueFrom(collectionService.encryptedCollections$(userId)); expect(result.length).toBe(2); - expect(result).toIncludeAllPartialMembers([ + expect(result).toContainPartialObjects([ { id: collection1.id, name: makeEncString("ENC_NAME_" + collection1.id), @@ -205,7 +205,7 @@ describe("DefaultvNextCollectionService", () => { const result = await firstValueFrom(collectionService.encryptedCollections$(userId)); expect(result.length).toBe(3); - expect(result).toIncludeAllPartialMembers([ + expect(result).toContainPartialObjects([ { id: collection1.id, name: makeEncString("UPDATED_ENC_NAME_" + collection1.id), @@ -230,7 +230,7 @@ describe("DefaultvNextCollectionService", () => { const result = await firstValueFrom(collectionService.encryptedCollections$(userId)); expect(result.length).toBe(1); - expect(result).toIncludeAllPartialMembers([ + expect(result).toContainPartialObjects([ { id: collection1.id, name: makeEncString("ENC_NAME_" + collection1.id), @@ -253,7 +253,7 @@ describe("DefaultvNextCollectionService", () => { const result = await firstValueFrom(collectionService.encryptedCollections$(userId)); expect(result.length).toBe(1); - expect(result).toIncludeAllPartialMembers([ + expect(result).toContainPartialObjects([ { id: newCollection3.id, name: makeEncString("ENC_NAME_" + newCollection3.id), diff --git a/libs/admin-console/test.setup.ts b/libs/admin-console/test.setup.ts index 6be6e7b8dd1..8ab102f2cf4 100644 --- a/libs/admin-console/test.setup.ts +++ b/libs/admin-console/test.setup.ts @@ -1,6 +1,10 @@ import { webcrypto } from "crypto"; + +import { addCustomMatchers } from "@bitwarden/common/spec"; import "jest-preset-angular/setup-jest"; +addCustomMatchers(); + Object.defineProperty(window, "CSS", { value: null }); Object.defineProperty(window, "getComputedStyle", { value: () => { diff --git a/libs/admin-console/tsconfig.json b/libs/admin-console/tsconfig.json index 6004a56fb55..360583e4faa 100644 --- a/libs/admin-console/tsconfig.json +++ b/libs/admin-console/tsconfig.json @@ -1,5 +1,5 @@ { "extends": "../shared/tsconfig.libs", - "include": ["src", "spec"], + "include": ["src", "spec", "../../libs/common/custom-matchers.d.ts"], "exclude": ["node_modules", "dist"] } diff --git a/libs/common/spec/matchers/index.ts b/libs/common/spec/matchers/index.ts index 44440be5b54..b2e09cc8e92 100644 --- a/libs/common/spec/matchers/index.ts +++ b/libs/common/spec/matchers/index.ts @@ -1,16 +1,12 @@ -import * as matchers from "jest-extended"; - import { toBeFulfilled, toBeResolved, toBeRejected } from "./promise-fulfilled"; import { toAlmostEqual } from "./to-almost-equal"; +import { toContainPartialObjects } from "./to-contain-partial-objects"; import { toEqualBuffer } from "./to-equal-buffer"; export * from "./to-equal-buffer"; export * from "./to-almost-equal"; export * from "./promise-fulfilled"; -// add all jest-extended matchers -expect.extend(matchers); - export function addCustomMatchers() { expect.extend({ toEqualBuffer: toEqualBuffer, @@ -18,6 +14,7 @@ export function addCustomMatchers() { toBeFulfilled: toBeFulfilled, toBeResolved: toBeResolved, toBeRejected: toBeRejected, + toContainPartialObjects, }); } @@ -59,4 +56,9 @@ export interface CustomMatchers { * @returns CustomMatcherResult indicating whether or not the test passed */ toBeRejected(withinMs?: number): Promise; + /** + * Matches if the received array contains all the expected objects using partial matching (expect.objectContaining). + * @param expected An array of partial objects that should be contained in the received array. + */ + toContainPartialObjects(expected: Array): R; } diff --git a/libs/common/spec/matchers/to-contain-partial-objects.spec.ts b/libs/common/spec/matchers/to-contain-partial-objects.spec.ts new file mode 100644 index 00000000000..ab6f90adf17 --- /dev/null +++ b/libs/common/spec/matchers/to-contain-partial-objects.spec.ts @@ -0,0 +1,77 @@ +describe("toContainPartialObjects", () => { + describe("matches", () => { + it("if the array only contains the partial objects", () => { + const actual = [ + { + id: 1, + name: "foo", + }, + { + id: 2, + name: "bar", + }, + ]; + + const expected = [{ id: 1 }, { id: 2 }]; + + expect(actual).toContainPartialObjects(expected); + }); + + it("if the array contains the partial objects and other objects", () => { + const actual = [ + { + id: 1, + name: "foo", + }, + { + id: 2, + name: "bar", + }, + { + id: 3, + name: "baz", + }, + ]; + + const expected = [{ id: 1 }, { id: 2 }]; + + expect(actual).toContainPartialObjects(expected); + }); + }); + + describe("doesn't match", () => { + it("if the array does not contain any partial objects", () => { + const actual = [ + { + id: 1, + name: "foo", + }, + { + id: 2, + name: "bar", + }, + ]; + + const expected = [{ id: 1, name: "Foo" }]; + + expect(actual).not.toContainPartialObjects(expected); + }); + + it("if the array contains some but not all partial objects", () => { + const actual = [ + { + id: 1, + name: "foo", + }, + { + id: 2, + name: "bar", + }, + ]; + + const expected = [{ id: 2 }, { id: 3 }]; + + expect(actual).not.toContainPartialObjects(expected); + }); + }); +}); diff --git a/libs/common/spec/matchers/to-contain-partial-objects.ts b/libs/common/spec/matchers/to-contain-partial-objects.ts new file mode 100644 index 00000000000..d5a74223c99 --- /dev/null +++ b/libs/common/spec/matchers/to-contain-partial-objects.ts @@ -0,0 +1,16 @@ +import { diff } from "jest-diff"; + +export const toContainPartialObjects: jest.CustomMatcher = function ( + received: Array, + expected: Array, +) { + const pass = this.equals( + received, + expect.arrayContaining(expected.map((e) => expect.objectContaining(e))), + ); + + return { + message: () => diff(expected, received), + pass: pass, + }; +}; diff --git a/libs/common/src/tools/rx.spec.ts b/libs/common/src/tools/rx.spec.ts index 9ce147a3ff4..2c433fef93b 100644 --- a/libs/common/src/tools/rx.spec.ts +++ b/libs/common/src/tools/rx.spec.ts @@ -56,7 +56,7 @@ describe("errorOnChange", () => { source$.complete(); - expect(complete).toBeTrue(); + expect(complete).toBe(true); }); it("errors when the input changes", async () => { diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts index 612cd83d99b..56724723a15 100644 --- a/libs/common/src/vault/services/folder/folder.service.spec.ts +++ b/libs/common/src/vault/services/folder/folder.service.spec.ts @@ -75,8 +75,8 @@ describe("Folder Service", () => { const result = await firstValueFrom(folderService.folders$(mockUserId)); expect(result.length).toBe(2); - expect(result).toIncludeAllPartialMembers([ - { id: "1", name: makeEncString("ENC_STRING_1") }, + expect(result).toContainPartialObjects([ + { id: "1", name: makeEncString("ENC_STRING_2") }, { id: "2", name: makeEncString("ENC_STRING_2") }, ]); }); @@ -96,7 +96,7 @@ describe("Folder Service", () => { const result = await firstValueFrom(folderService.folderViews$(mockUserId)); expect(result.length).toBe(3); - expect(result).toIncludeAllPartialMembers([ + expect(result).toContainPartialObjects([ { id: "1", name: "DEC" }, { id: "2", name: "DEC" }, { name: "No Folder" }, diff --git a/package-lock.json b/package-lock.json index 7994c8b0c2b..cd82afb80ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -152,7 +152,7 @@ "html-webpack-injector": "1.1.4", "html-webpack-plugin": "5.6.3", "husky": "9.1.4", - "jest-extended": "4.0.2", + "jest-diff": "29.7.0", "jest-junit": "16.0.0", "jest-mock-extended": "3.0.7", "jest-preset-angular": "14.1.1", @@ -20720,28 +20720,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-extended": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz", - "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-diff": "^29.0.0", - "jest-get-type": "^29.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "jest": ">=27.2.5" - }, - "peerDependenciesMeta": { - "jest": { - "optional": true - } - } - }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", diff --git a/package.json b/package.json index 2508c543d7e..c680d081bc9 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "html-webpack-injector": "1.1.4", "html-webpack-plugin": "5.6.3", "husky": "9.1.4", - "jest-extended": "4.0.2", + "jest-diff": "29.7.0", "jest-junit": "16.0.0", "jest-mock-extended": "3.0.7", "jest-preset-angular": "14.1.1", From 8d7fb589871ef2778253ecbe12da3abdbfb2dda7 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Fri, 10 Jan 2025 14:02:26 +1000 Subject: [PATCH 2/8] Undo temp test change --- libs/common/src/vault/services/folder/folder.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts index 56724723a15..240a8dab66e 100644 --- a/libs/common/src/vault/services/folder/folder.service.spec.ts +++ b/libs/common/src/vault/services/folder/folder.service.spec.ts @@ -76,7 +76,7 @@ describe("Folder Service", () => { expect(result.length).toBe(2); expect(result).toContainPartialObjects([ - { id: "1", name: makeEncString("ENC_STRING_2") }, + { id: "1", name: makeEncString("ENC_STRING_1") }, { id: "2", name: makeEncString("ENC_STRING_2") }, ]); }); From fb16d55277d451fc11f51c832bb5619dc6706d4b Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 13 Jan 2025 11:40:24 +1000 Subject: [PATCH 3/8] Update remaining locations --- .../vault-header/vault-header-v2.component.spec.ts | 2 +- .../vault-v2/view-v2/view-v2.component.spec.ts | 12 ++++++------ .../vault-popup-list-filters.service.spec.ts | 4 ++-- .../key-service-legacy-encryptor-provider.spec.ts | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts index 38ec6056d19..1f67dd51c21 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.spec.ts @@ -152,7 +152,7 @@ describe("VaultHeaderV2Component", () => { it("defaults the initial state to true", (done) => { // The initial value of the `state$` variable above is undefined component["initialDisclosureVisibility$"].subscribe((initialVisibility) => { - expect(initialVisibility).toBeTrue(); + expect(initialVisibility).toBe(true); done(); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts index 7ee15aa833b..526ab2e2579 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts @@ -179,7 +179,7 @@ describe("ViewV2Component", () => { flush(); // Resolve all promises - expect(doAutofill).toHaveBeenCalledOnce(); + expect(doAutofill).toHaveBeenCalledTimes(1); })); it('invokes `copy` when action="copy-username"', fakeAsync(() => { @@ -187,7 +187,7 @@ describe("ViewV2Component", () => { flush(); // Resolve all promises - expect(copy).toHaveBeenCalledOnce(); + expect(copy).toHaveBeenCalledTimes(1); })); it('invokes `copy` when action="copy-password"', fakeAsync(() => { @@ -195,7 +195,7 @@ describe("ViewV2Component", () => { flush(); // Resolve all promises - expect(copy).toHaveBeenCalledOnce(); + expect(copy).toHaveBeenCalledTimes(1); })); it('invokes `copy` when action="copy-totp"', fakeAsync(() => { @@ -203,7 +203,7 @@ describe("ViewV2Component", () => { flush(); // Resolve all promises - expect(copy).toHaveBeenCalledOnce(); + expect(copy).toHaveBeenCalledTimes(1); })); it("closes the popout after a load action", fakeAsync(() => { @@ -218,9 +218,9 @@ describe("ViewV2Component", () => { flush(); // Resolve all promises - expect(doAutofill).toHaveBeenCalledOnce(); + expect(doAutofill).toHaveBeenCalledTimes(1); expect(focusSpy).toHaveBeenCalledWith(99); - expect(closeSpy).toHaveBeenCalledOnce(); + expect(closeSpy).toHaveBeenCalledTimes(1); })); }); }); diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts index 0eb91c6cbe2..e1236be08f9 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts @@ -488,7 +488,7 @@ describe("VaultPopupListFiltersService", () => { state$.next(true); service.filterVisibilityState$.subscribe((filterVisibility) => { - expect(filterVisibility).toBeTrue(); + expect(filterVisibility).toBe(true); done(); }); }); @@ -496,7 +496,7 @@ describe("VaultPopupListFiltersService", () => { it("updates stored filter state", async () => { await service.updateFilterVisibility(false); - expect(update).toHaveBeenCalledOnce(); + expect(update).toHaveBeenCalledTimes(1); // Get callback passed to `update` const updateCallback = update.mock.calls[0][0]; expect(updateCallback()).toBe(false); diff --git a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts index 12257905d1c..a99768acf69 100644 --- a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts +++ b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts @@ -184,7 +184,7 @@ describe("KeyServiceLegacyEncryptorProvider", () => { singleUserId$.complete(); - expect(completed).toBeTrue(); + expect(completed).toBe(true); }); it("completes when `userKey$` emits a falsy value after emitting a truthy value", () => { @@ -199,7 +199,7 @@ describe("KeyServiceLegacyEncryptorProvider", () => { userKey$.next(null); - expect(completed).toBeTrue(); + expect(completed).toBe(true); }); it("completes once `dependencies.singleUserId$` emits and `userKey$` completes", () => { @@ -214,7 +214,7 @@ describe("KeyServiceLegacyEncryptorProvider", () => { userKey$.complete(); - expect(completed).toBeTrue(); + expect(completed).toBe(true); }); }); @@ -445,7 +445,7 @@ describe("KeyServiceLegacyEncryptorProvider", () => { singleOrganizationId$.complete(); - expect(completed).toBeTrue(); + expect(completed).toBe(true); }); it("completes when `orgKeys$` emits a falsy value after emitting a truthy value", () => { @@ -466,7 +466,7 @@ describe("KeyServiceLegacyEncryptorProvider", () => { orgKey$.next(OrgRecords); orgKey$.next(null); - expect(completed).toBeTrue(); + expect(completed).toBe(true); }); it("completes once `dependencies.singleOrganizationId$` emits and `userKey$` completes", () => { @@ -486,7 +486,7 @@ describe("KeyServiceLegacyEncryptorProvider", () => { orgKey$.complete(); - expect(completed).toBeTrue(); + expect(completed).toBe(true); }); }); }); From 698401b20d5dc64f94617b09a2a82076745b4bc2 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 13 Jan 2025 11:40:47 +1000 Subject: [PATCH 4/8] remove jest-extended from renovate --- .github/renovate.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/renovate.json b/.github/renovate.json index 2cc38431392..a1987ca038d 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -210,7 +210,6 @@ "eslint-plugin-storybook", "eslint-plugin-tailwindcss", "husky", - "jest-extended", "jest-junit", "jest-mock-extended", "jest-preset-angular", From f50b9acefb158f96da581f02872a3e805a81de1b Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 13 Jan 2025 12:58:39 +1000 Subject: [PATCH 5/8] Add error message and fix typing --- .../spec/matchers/to-contain-partial-objects.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/common/spec/matchers/to-contain-partial-objects.ts b/libs/common/spec/matchers/to-contain-partial-objects.ts index d5a74223c99..2879e83b71f 100644 --- a/libs/common/spec/matchers/to-contain-partial-objects.ts +++ b/libs/common/spec/matchers/to-contain-partial-objects.ts @@ -1,8 +1,10 @@ +import { EOL } from "os"; + import { diff } from "jest-diff"; -export const toContainPartialObjects: jest.CustomMatcher = function ( - received: Array, - expected: Array, +export const toContainPartialObjects: jest.CustomMatcher = function ( + received: Array, + expected: Array, ) { const pass = this.equals( received, @@ -10,7 +12,10 @@ export const toContainPartialObjects: jest.CustomMatcher = function ( ); return { - message: () => diff(expected, received), + message: () => + "Received array did not contain partial matches for all expected objects." + + EOL + + diff(expected, received), pass: pass, }; }; From a72a998626c03946b38177abf27fa3aef3425141 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 13 Jan 2025 13:34:02 +1000 Subject: [PATCH 6/8] Undo error msg --- libs/common/spec/matchers/to-contain-partial-objects.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libs/common/spec/matchers/to-contain-partial-objects.ts b/libs/common/spec/matchers/to-contain-partial-objects.ts index 2879e83b71f..e9e16f355a1 100644 --- a/libs/common/spec/matchers/to-contain-partial-objects.ts +++ b/libs/common/spec/matchers/to-contain-partial-objects.ts @@ -1,5 +1,3 @@ -import { EOL } from "os"; - import { diff } from "jest-diff"; export const toContainPartialObjects: jest.CustomMatcher = function ( @@ -12,10 +10,7 @@ export const toContainPartialObjects: jest.CustomMatcher = function ( ); return { - message: () => - "Received array did not contain partial matches for all expected objects." + - EOL + - diff(expected, received), + message: () => diff(expected, received) as string, pass: pass, }; }; From 61f02553b3301a61115e8aa8d9633aa0374905bc Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 13 Jan 2025 13:38:50 +1000 Subject: [PATCH 7/8] Provide more granular error messages for negative test case --- .../matchers/to-contain-partial-objects.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libs/common/spec/matchers/to-contain-partial-objects.ts b/libs/common/spec/matchers/to-contain-partial-objects.ts index e9e16f355a1..99c174a7b25 100644 --- a/libs/common/spec/matchers/to-contain-partial-objects.ts +++ b/libs/common/spec/matchers/to-contain-partial-objects.ts @@ -1,16 +1,31 @@ +import { EOL } from "os"; + import { diff } from "jest-diff"; export const toContainPartialObjects: jest.CustomMatcher = function ( received: Array, expected: Array, ) { - const pass = this.equals( + const matched = this.equals( received, expect.arrayContaining(expected.map((e) => expect.objectContaining(e))), ); + if (matched) { + return { + message: () => + "Expected the received array to NOT include partial matches for all expected objects." + + EOL + + diff(expected, received), + pass: true, + }; + } + return { - message: () => diff(expected, received) as string, - pass: pass, + message: () => + "Expected the received array to contain partial matches for all expected objects." + + EOL + + diff(expected, received), + pass: false, }; }; From 01518ba6a7649957a6df157b67517b0ac4ac2751 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 13 Jan 2025 13:40:48 +1000 Subject: [PATCH 8/8] grammar tweak --- libs/common/spec/matchers/to-contain-partial-objects.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/spec/matchers/to-contain-partial-objects.ts b/libs/common/spec/matchers/to-contain-partial-objects.ts index 99c174a7b25..f072ca6fba6 100644 --- a/libs/common/spec/matchers/to-contain-partial-objects.ts +++ b/libs/common/spec/matchers/to-contain-partial-objects.ts @@ -14,7 +14,7 @@ export const toContainPartialObjects: jest.CustomMatcher = function ( if (matched) { return { message: () => - "Expected the received array to NOT include partial matches for all expected objects." + + "Expected the received array NOT to include partial matches for all expected objects." + EOL + diff(expected, received), pass: true,