diff --git a/.github/renovate.json5 b/.github/renovate.json5 index e1bb6fac305e..210189580e5a 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -69,6 +69,7 @@ "eslint-config-prettier", "eslint-import-resolver-typescript", "eslint-plugin-import", + "eslint-plugin-jest", "eslint-plugin-rxjs-angular", "eslint-plugin-rxjs", "eslint-plugin-storybook", @@ -391,6 +392,7 @@ "eslint-config-prettier", "eslint-import-resolver-typescript", "eslint-plugin-import", + "eslint-plugin-jest", "eslint-plugin-rxjs-angular", "eslint-plugin-rxjs", "eslint-plugin-storybook", diff --git a/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts b/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts index 13f4a8635dfe..9d0a5058807d 100644 --- a/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts +++ b/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts @@ -177,7 +177,7 @@ describe("AccountSwitcherService", () => { expect(messagingService.send).toHaveBeenCalledWith("switchAccount", { userId: null }); - expect(removeListenerSpy).toBeCalledTimes(1); + expect(removeListenerSpy).toHaveBeenCalledTimes(1); }); it("initiates an account switch with an account id", async () => { @@ -199,13 +199,13 @@ describe("AccountSwitcherService", () => { await selectAccountPromise; expect(messagingService.send).toHaveBeenCalledWith("switchAccount", { userId: "1" }); - expect(messagingService.send).toBeCalledWith( + expect(messagingService.send).toHaveBeenCalledWith( "switchAccount", matches((payload) => { return payload.userId === "1"; }), ); - expect(removeListenerSpy).toBeCalledTimes(1); + expect(removeListenerSpy).toHaveBeenCalledTimes(1); }); }); }); diff --git a/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts b/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts index 01767281a205..504799552904 100644 --- a/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts +++ b/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts @@ -130,9 +130,12 @@ describe("AutoSubmitLoginBackground", () => { url: validAutoSubmitUrl, tabId: webRequestDetails.tabId, }); - expect(chrome.webNavigation.onCompleted.addListener).toBeCalledWith(expect.any(Function), { - url: [{ hostEquals: validAutoSubmitHost }], - }); + expect(chrome.webNavigation.onCompleted.addListener).toHaveBeenCalledWith( + expect.any(Function), + { + url: [{ hostEquals: validAutoSubmitHost }], + }, + ); }); it("sets up the auto-submit workflow when the web request occurs in a sub frame and the initiator of the request is a valid auto-submit host", async () => { @@ -145,9 +148,12 @@ describe("AutoSubmitLoginBackground", () => { triggerWebRequestOnBeforeRequestEvent(webRequestDetails); - expect(chrome.webNavigation.onCompleted.addListener).toBeCalledWith(expect.any(Function), { - url: [{ hostEquals: subFrameHost }], - }); + expect(chrome.webNavigation.onCompleted.addListener).toHaveBeenCalledWith( + expect.any(Function), + { + url: [{ hostEquals: subFrameHost }], + }, + ); }); describe("injecting the auto-submit login content script", () => { @@ -182,7 +188,7 @@ describe("AutoSubmitLoginBackground", () => { triggerWebNavigationOnCompletedEvent(webNavigationDetails); await flushPromises(); - expect(scriptInjectorService.inject).toBeCalledWith({ + expect(scriptInjectorService.inject).toHaveBeenCalledWith({ tabId: webRequestDetails.tabId, injectDetails: { file: "content/auto-submit-login.js", @@ -320,7 +326,7 @@ describe("AutoSubmitLoginBackground", () => { triggerWebRequestOnBeforeRequestEvent(webRequestDetails); - expect(chrome.webNavigation.onCompleted.addListener).toBeCalledWith( + expect(chrome.webNavigation.onCompleted.addListener).toHaveBeenCalledWith( autoSubmitLoginBackground["handleAutoSubmitHostNavigationCompleted"], { url: [{ hostEquals: validAutoSubmitHost }] }, ); @@ -440,9 +446,12 @@ describe("AutoSubmitLoginBackground", () => { }), ); - expect(chrome.webNavigation.onCompleted.addListener).toBeCalledWith(expect.any(Function), { - url: [{ hostEquals: validAutoSubmitHost }], - }); + expect(chrome.webNavigation.onCompleted.addListener).toHaveBeenCalledWith( + expect.any(Function), + { + url: [{ hostEquals: validAutoSubmitHost }], + }, + ); }); }); @@ -491,7 +500,7 @@ describe("AutoSubmitLoginBackground", () => { sendMockExtensionMessage(message, sender); await flushPromises(); - expect(autofillService.doAutoFillOnTab).toBeCalledWith( + expect(autofillService.doAutoFillOnTab).toHaveBeenCalledWith( [ { frameId: sender.frameId, diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts index 6c7fd7ed35ec..6aeeb6801c48 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts @@ -195,9 +195,9 @@ describe("ContextMenuClickedHandler", () => { it("can generate password", async () => { await sut.run(createData(GENERATE_PASSWORD_ID), { id: 5 } as any); - expect(generatePasswordToClipboard).toBeCalledTimes(1); + expect(generatePasswordToClipboard).toHaveBeenCalledTimes(1); - expect(generatePasswordToClipboard).toBeCalledWith({ + expect(generatePasswordToClipboard).toHaveBeenCalledWith({ id: 5, }); }); @@ -208,9 +208,9 @@ describe("ContextMenuClickedHandler", () => { await sut.run(createData(`${AUTOFILL_ID}_1`, AUTOFILL_ID), { id: 5 } as any); - expect(autofill).toBeCalledTimes(1); + expect(autofill).toHaveBeenCalledTimes(1); - expect(autofill).toBeCalledWith({ id: 5 }, cipher); + expect(autofill).toHaveBeenCalledWith({ id: 5 }, cipher); }); it("copies username to clipboard", async () => { @@ -222,7 +222,7 @@ describe("ContextMenuClickedHandler", () => { url: "https://test.com", } as any); - expect(copyToClipboard).toBeCalledTimes(1); + expect(copyToClipboard).toHaveBeenCalledTimes(1); expect(copyToClipboard).toHaveBeenCalledWith({ text: "TEST_USERNAME", @@ -239,7 +239,7 @@ describe("ContextMenuClickedHandler", () => { url: "https://test.com", } as any); - expect(copyToClipboard).toBeCalledTimes(1); + expect(copyToClipboard).toHaveBeenCalledTimes(1); expect(copyToClipboard).toHaveBeenCalledWith({ text: "TEST_PASSWORD", diff --git a/apps/browser/src/autofill/content/autofill-init.spec.ts b/apps/browser/src/autofill/content/autofill-init.spec.ts index 9f66dbae958e..50e6a50c2e66 100644 --- a/apps/browser/src/autofill/content/autofill-init.spec.ts +++ b/apps/browser/src/autofill/content/autofill-init.spec.ts @@ -248,7 +248,7 @@ describe("AutofillInit", () => { await flushPromises(); expect(autofillInit["collectAutofillContentService"].getPageDetails).toHaveBeenCalled(); - expect(sendResponse).toBeCalledWith(pageDetails); + expect(sendResponse).toHaveBeenCalledWith(pageDetails); expect(chrome.runtime.sendMessage).not.toHaveBeenCalledWith({ command: "collectPageDetailsResponse", tab: message.tab, diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts index 5d4b58561bb9..37d775b89740 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts @@ -401,7 +401,7 @@ describe("AutofillInlineMenuList", () => { firstFillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowDown" })); - expect((secondFillCipherElement as HTMLElement).focus).toBeCalled(); + expect((secondFillCipherElement as HTMLElement).focus).toHaveBeenCalled(); }); it("directs focus to the first item in the cipher list if no cipher is present after the current one when pressing ArrowDown and no new item button exists", () => { @@ -415,7 +415,7 @@ describe("AutofillInlineMenuList", () => { lastFillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowDown" })); - expect((firstFillCipherElement as HTMLElement).focus).toBeCalled(); + expect((firstFillCipherElement as HTMLElement).focus).toHaveBeenCalled(); }); it("directs focus to the new item button if no cipher is present after the current one when pressing ArrowDown", async () => { @@ -435,7 +435,7 @@ describe("AutofillInlineMenuList", () => { lastFillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowDown" })); - expect(autofillInlineMenuList["newItemButtonElement"].focus).toBeCalled(); + expect(autofillInlineMenuList["newItemButtonElement"].focus).toHaveBeenCalled(); }); it("allows the user to move keyboard focus to the previous cipher element on ArrowUp", () => { @@ -449,7 +449,7 @@ describe("AutofillInlineMenuList", () => { secondFillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); - expect((firstFillCipherElement as HTMLElement).focus).toBeCalled(); + expect((firstFillCipherElement as HTMLElement).focus).toHaveBeenCalled(); }); it("directs focus to the last item in the cipher list if no cipher is present before the current one when pressing ArrowUp", () => { @@ -463,7 +463,7 @@ describe("AutofillInlineMenuList", () => { firstFillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); - expect((lastFillCipherElement as HTMLElement).focus).toBeCalled(); + expect((lastFillCipherElement as HTMLElement).focus).toHaveBeenCalled(); }); it("directs focus to the new item button if no cipher is present before the current one when pressing ArrowUp", async () => { @@ -483,7 +483,7 @@ describe("AutofillInlineMenuList", () => { firstFillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); - expect(autofillInlineMenuList["newItemButtonElement"].focus).toBeCalled(); + expect(autofillInlineMenuList["newItemButtonElement"].focus).toHaveBeenCalled(); }); it("allows the user to move keyboard focus to the view cipher button on ArrowRight", () => { @@ -495,7 +495,7 @@ describe("AutofillInlineMenuList", () => { fillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowRight" })); - expect((viewCipherButton as HTMLElement).focus).toBeCalled(); + expect((viewCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); it("ignores keyup events that do not include ArrowUp, ArrowDown, or ArrowRight", () => { @@ -505,7 +505,7 @@ describe("AutofillInlineMenuList", () => { fillCipherElement.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowLeft" })); - expect((fillCipherElement as HTMLElement).focus).not.toBeCalled(); + expect((fillCipherElement as HTMLElement).focus).not.toHaveBeenCalled(); }); }); @@ -540,7 +540,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowLeft" })); - expect((fillCipherButton as HTMLElement).focus).toBeCalled(); + expect((fillCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); it("allows the user to move keyboard to the next cipher element on ArrowDown", () => { @@ -553,7 +553,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowDown" })); - expect((secondFillCipherButton as HTMLElement).focus).toBeCalled(); + expect((secondFillCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); it("allows the user to move keyboard focus to the previous cipher element on ArrowUp", () => { @@ -566,7 +566,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); - expect((firstFillCipherButton as HTMLElement).focus).toBeCalled(); + expect((firstFillCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); it("ignores keyup events that do not include ArrowUp, ArrowDown, or ArrowRight", () => { @@ -576,7 +576,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowRight" })); - expect((viewCipherButton as HTMLElement).focus).not.toBeCalled(); + expect((viewCipherButton as HTMLElement).focus).not.toHaveBeenCalled(); }); }); @@ -808,7 +808,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowDown" })); - expect((fillCipherButton as HTMLElement).focus).toBeCalled(); + expect((fillCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); it("skips the passkeys heading when the user presses ArrowDown to focus the first list item", () => { @@ -820,7 +820,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowDown" })); - expect((fillCipherButton as HTMLElement).focus).toBeCalled(); + expect((fillCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); it("skips the logins heading when the user presses ArrowUp to focus the previous list item", () => { @@ -832,7 +832,7 @@ describe("AutofillInlineMenuList", () => { viewCipherButton.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); - expect((fillCipherButton as HTMLElement).focus).toBeCalled(); + expect((fillCipherButton as HTMLElement).focus).toHaveBeenCalled(); }); }); }); @@ -914,7 +914,7 @@ describe("AutofillInlineMenuList", () => { new KeyboardEvent("keyup", { code: "ArrowRight" }), ); - expect((refreshGeneratedPasswordButton as HTMLElement).focus).toBeCalled(); + expect((refreshGeneratedPasswordButton as HTMLElement).focus).toHaveBeenCalled(); }); }); }); @@ -984,7 +984,7 @@ describe("AutofillInlineMenuList", () => { new KeyboardEvent("keyup", { code: "ArrowLeft" }), ); - expect((fillGeneratedPasswordButton as HTMLElement).focus).toBeCalled(); + expect((fillGeneratedPasswordButton as HTMLElement).focus).toHaveBeenCalled(); }); }); }); @@ -1207,7 +1207,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); - expect((unlockButton as HTMLElement).focus).toBeCalled(); + expect((unlockButton as HTMLElement).focus).toHaveBeenCalled(); }); it("focuses the new item button element if the cipher list is empty", async () => { @@ -1219,7 +1219,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); - expect((newItemButton as HTMLElement).focus).toBeCalled(); + expect((newItemButton as HTMLElement).focus).toHaveBeenCalled(); }); it("focuses the first cipher button element if the cipher list is populated", () => { @@ -1230,7 +1230,7 @@ describe("AutofillInlineMenuList", () => { postWindowMessage({ command: "focusAutofillInlineMenuList", token: "test-token" }); - expect((firstCipherItem as HTMLElement).focus).toBeCalled(); + expect((firstCipherItem as HTMLElement).focus).toHaveBeenCalled(); }); }); diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts index 70dd23037f47..4b6dafb85db5 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts @@ -2194,7 +2194,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["setupMutationObserver"](); expect(collectAutofillContentService["mutationObserver"]).toBeInstanceOf(MutationObserver); - expect(collectAutofillContentService["mutationObserver"].observe).toBeCalled(); + expect(collectAutofillContentService["mutationObserver"].observe).toHaveBeenCalled(); }); }); @@ -2232,11 +2232,11 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["domRecentlyMutated"]).toEqual(true); expect(collectAutofillContentService["noFieldsFound"]).toEqual(false); - expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toBeCalledWith( + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toHaveBeenCalledWith( removedNodes, true, ); - expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toBeCalledWith( + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toHaveBeenCalledWith( addedNodes, ); }); @@ -2301,8 +2301,10 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["domRecentlyMutated"]).toEqual(false); expect(collectAutofillContentService["noFieldsFound"]).toEqual(true); - expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); - expect(collectAutofillContentService["handleAutofillElementAttributeMutation"]).toBeCalled(); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toHaveBeenCalled(); + expect( + collectAutofillContentService["handleAutofillElementAttributeMutation"], + ).toHaveBeenCalled(); }); it("will handle window location mutations", () => { @@ -2324,11 +2326,11 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]); - expect(collectAutofillContentService["handleWindowLocationMutation"]).toBeCalled(); - expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); + expect(collectAutofillContentService["handleWindowLocationMutation"]).toHaveBeenCalled(); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toHaveBeenCalled(); expect( collectAutofillContentService["handleAutofillElementAttributeMutation"], - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); it("will setup the overlay listeners on mutated elements", async () => { @@ -2356,7 +2358,9 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]); jest.runAllTimers(); - expect(collectAutofillContentService["setupOverlayListenersOnMutatedElements"]).toBeCalled(); + expect( + collectAutofillContentService["setupOverlayListenersOnMutatedElements"], + ).toHaveBeenCalled(); }); it("triggers debounced page details update when mutations occur in shadow roots", () => { @@ -2549,7 +2553,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes); - expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toBeCalled(); + expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toHaveBeenCalled(); }); it("skips building the autofill field item if the node is already a field element", () => { @@ -2563,7 +2567,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes); - expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toBeCalled(); + expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toHaveBeenCalled(); }); }); @@ -2644,7 +2648,9 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["currentLocationHref"]).toEqual(window.location.href); expect(collectAutofillContentService["domRecentlyMutated"]).toEqual(true); expect(collectAutofillContentService["noFieldsFound"]).toEqual(false); - expect(collectAutofillContentService["updateAutofillElementsAfterMutation"]).toBeCalled(); + expect( + collectAutofillContentService["updateAutofillElementsAfterMutation"], + ).toHaveBeenCalled(); expect(collectAutofillContentService["_autofillFormElements"].size).toEqual(0); expect(collectAutofillContentService["autofillFieldElements"].size).toEqual(0); }); @@ -2667,7 +2673,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["handleAutofillElementAttributeMutation"](mutationRecord); - expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toHaveBeenCalled(); }); it("will update the autofill form element data if the target node can be found in the autofillFormElements map", () => { @@ -2699,7 +2705,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["handleAutofillElementAttributeMutation"](mutationRecord); - expect(collectAutofillContentService["updateAutofillFormElementData"]).toBeCalledWith( + expect(collectAutofillContentService["updateAutofillFormElementData"]).toHaveBeenCalledWith( mutationRecord.attributeName, mutationRecord.target, autofillForm, @@ -2746,7 +2752,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["handleAutofillElementAttributeMutation"](mutationRecord); - expect(collectAutofillContentService["updateAutofillFieldElementData"]).toBeCalledWith( + expect(collectAutofillContentService["updateAutofillFieldElementData"]).toHaveBeenCalledWith( mutationRecord.attributeName, mutationRecord.target, autofillField, @@ -2781,7 +2787,7 @@ describe("CollectAutofillContentService", () => { autofillForm, ); - expect(collectAutofillContentService["_autofillFormElements"].set).toBeCalledWith( + expect(collectAutofillContentService["_autofillFormElements"].set).toHaveBeenCalledWith( formElement, autofillForm, ); @@ -2797,7 +2803,7 @@ describe("CollectAutofillContentService", () => { autofillForm, ); - expect(collectAutofillContentService["_autofillFormElements"].set).not.toBeCalled(); + expect(collectAutofillContentService["_autofillFormElements"].set).not.toHaveBeenCalled(); }); }); @@ -2850,7 +2856,7 @@ describe("CollectAutofillContentService", () => { autofillField, ); - expect(collectAutofillContentService["autofillFieldElements"].set).toBeCalledWith( + expect(collectAutofillContentService["autofillFieldElements"].set).toHaveBeenCalledWith( fieldElement, autofillField, ); @@ -2866,7 +2872,7 @@ describe("CollectAutofillContentService", () => { autofillField, ); - expect(collectAutofillContentService["autofillFieldElements"].set).not.toBeCalled(); + expect(collectAutofillContentService["autofillFieldElements"].set).not.toHaveBeenCalled(); }); }); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts index d3de41d2053f..c27cd16ae6fa 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts @@ -482,7 +482,7 @@ describe("InsertAutofillContentService", () => { expect( insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid, - ).toBeCalledWith("__1"); + ).toHaveBeenCalledWith("__1"); expect((insertAutofillContentService as any)["triggerClickOnElement"]).toHaveBeenCalledWith( textInput, ); @@ -547,7 +547,7 @@ describe("InsertAutofillContentService", () => { expect( insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid, - ).toBeCalledWith("__0"); + ).toHaveBeenCalledWith("__0"); expect(targetInput.blur).not.toHaveBeenCalled(); expect( insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"], diff --git a/apps/browser/src/platform/decorators/dev-flag.decorator.spec.ts b/apps/browser/src/platform/decorators/dev-flag.decorator.spec.ts index da00bc6fe302..98a55087865a 100644 --- a/apps/browser/src/platform/decorators/dev-flag.decorator.spec.ts +++ b/apps/browser/src/platform/decorators/dev-flag.decorator.spec.ts @@ -23,13 +23,13 @@ describe("devFlag decorator", () => { devFlagEnabledMock.mockReturnValue(false); expect(() => { new TestClass().test(); - }).toThrowError("This method should not be called, it is protected by a disabled dev flag."); + }).toThrow("This method should not be called, it is protected by a disabled dev flag."); }); it("should not throw an error if the dev flag is enabled", () => { devFlagEnabledMock.mockReturnValue(true); expect(() => { new TestClass().test(); - }).not.toThrowError(); + }).not.toThrow(); }); }); diff --git a/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts b/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts index b01e62d2af3b..35cf4c006eea 100644 --- a/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts +++ b/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts @@ -147,7 +147,7 @@ describe("DesktopLockComponentService", () => { it("throws an error for an unsupported platform", () => { platformUtilsService.getDevice.mockReturnValue("unsupported" as any); - expect(() => service.getBiometricsUnlockBtnText()).toThrowError("Unsupported platform"); + expect(() => service.getBiometricsUnlockBtnText()).toThrow("Unsupported platform"); }); }); diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts index 717e21e246c1..633fd7d39785 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.spec.ts @@ -447,7 +447,7 @@ describe("EmergencyAccessService", () => { params.email, params.activeUserId, ), - ).rejects.toThrowError("Failed to unwrap grantor key"); + ).rejects.toThrow("Failed to unwrap grantor key"); expect(keyService.userPrivateKey$).toHaveBeenCalledWith(params.activeUserId); expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith( diff --git a/eslint.config.mjs b/eslint.config.mjs index 2a78a0362792..5bb8ccb03792 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,6 +10,7 @@ import eslintPluginTailwindCSS from "eslint-plugin-tailwindcss"; import rxjs from "eslint-plugin-rxjs"; import angularRxjs from "eslint-plugin-rxjs-angular"; import storybook from "eslint-plugin-storybook"; +import jest from "eslint-plugin-jest"; import platformPlugins from "./libs/eslint/platform/index.mjs"; import componentPlugins from "./libs/eslint/components/index.mjs"; @@ -677,6 +678,17 @@ export default tseslint.config( }, }, + // Jest test files configuration + { + files: ["**/*.spec.ts", "**/*.spec.js"], + plugins: { + jest, + }, + rules: { + "jest/no-alias-methods": "error", + }, + }, + // Keep ignores at the end { ignores: [ diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index a3f79f45ad5e..b73048adbdfd 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -146,11 +146,11 @@ describe("AuthRequestService", () => { ); // Assert - expect(sut.decryptPubKeyEncryptedUserKey).toBeCalledWith( + expect(sut.decryptPubKeyEncryptedUserKey).toHaveBeenCalledWith( mockAuthReqResponse.key, mockPrivateKey, ); - expect(keyService.setUserKey).toBeCalledWith(mockDecryptedUserKey, mockUserId); + expect(keyService.setUserKey).toHaveBeenCalledWith(mockDecryptedUserKey, mockUserId); }); }); @@ -172,7 +172,7 @@ describe("AuthRequestService", () => { ); // Assert - expect(encryptService.decapsulateKeyUnsigned).toBeCalledWith( + expect(encryptService.decapsulateKeyUnsigned).toHaveBeenCalledWith( new EncString(mockPubKeyEncryptedUserKey), mockPrivateKey, ); diff --git a/libs/common/src/auth/services/account.service.spec.ts b/libs/common/src/auth/services/account.service.spec.ts index 6668b9c39de6..5832b9e4c46e 100644 --- a/libs/common/src/auth/services/account.service.spec.ts +++ b/libs/common/src/auth/services/account.service.spec.ts @@ -416,7 +416,7 @@ describe("accountService", () => { it("should throw if the account does not exist", () => { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises - expect(sut.switchAccount("unknown" as UserId)).rejects.toThrowError("Account does not exist"); + expect(sut.switchAccount("unknown" as UserId)).rejects.toThrow("Account does not exist"); }); it("should change active account when switched to the new account", async () => { diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts index 693992d4c4a3..f8bea7944761 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts @@ -86,7 +86,7 @@ describe("PasswordResetEnrollmentServiceImplementation", () => { const result = () => service.enroll("orgId"); - await expect(result).rejects.toThrowError("resetPasswordOrgKeysError"); + await expect(result).rejects.toThrow("resetPasswordOrgKeysError"); }); it("should enroll the user when no user id or key is provided", async () => { diff --git a/libs/common/src/platform/misc/range-with-default.spec.ts b/libs/common/src/platform/misc/range-with-default.spec.ts index b400439c60e2..6e330bf6c5ff 100644 --- a/libs/common/src/platform/misc/range-with-default.spec.ts +++ b/libs/common/src/platform/misc/range-with-default.spec.ts @@ -3,11 +3,11 @@ import { RangeWithDefault } from "./range-with-default"; describe("RangeWithDefault", () => { describe("constructor", () => { it("should throw an error when min is greater than max", () => { - expect(() => new RangeWithDefault(10, 5, 0)).toThrowError("10 is greater than 5."); + expect(() => new RangeWithDefault(10, 5, 0)).toThrow("10 is greater than 5."); }); it("should throw an error when default value is not in range", () => { - expect(() => new RangeWithDefault(0, 10, 20)).toThrowError("Default value is not in range."); + expect(() => new RangeWithDefault(0, 10, 20)).toThrow("Default value is not in range."); }); }); diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts index 9246652b4c8c..2297d3d22d71 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts @@ -10,7 +10,7 @@ describe("SymmetricCryptoKey", () => { new SymmetricCryptoKey(null); }; - expect(t).toThrowError("Must provide key"); + expect(t).toThrow("Must provide key"); }); describe("guesses encKey from key length", () => { @@ -47,7 +47,7 @@ describe("SymmetricCryptoKey", () => { new SymmetricCryptoKey(makeStaticByteArray(30)); }; - expect(t).toThrowError(`Unsupported encType/key length 30`); + expect(t).toThrow(`Unsupported encType/key length 30`); }); }); diff --git a/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts b/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts index d76bf74f4e98..2d1094848392 100644 --- a/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts +++ b/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts @@ -85,21 +85,21 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.makeCredential(invalidParams.unsupportedAlgorithm, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotSupported); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.NotSupported); }); it("should throw error when requireResidentKey has invalid value", async () => { const result = async () => await authenticator.makeCredential(invalidParams.invalidRk, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Unknown); }); it("should throw error when requireUserVerification has invalid value", async () => { const result = async () => await authenticator.makeCredential(invalidParams.invalidUv, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Unknown); }); /** @@ -112,7 +112,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.makeCredential(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Constraint); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Constraint); }); it("should not request confirmation from user", async () => { @@ -179,7 +179,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.makeCredential(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.NotAllowed); }); /** Devation: Organization ciphers are not checked against excluded credentials, even if the user has access to them. */ @@ -300,7 +300,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.makeCredential(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.NotAllowed); }); it("should throw error if user verification fails and cipher requires reprompt", async () => { @@ -319,7 +319,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.makeCredential(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Unknown); }); /** Spec: If any error occurred while creating the new credential object, return an error code equivalent to "UnknownError" and terminate the operation. */ @@ -334,7 +334,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.makeCredential(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Unknown); }); }); @@ -472,7 +472,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.getAssertion(invalidParams.invalidUv, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Unknown); }); /** @@ -485,7 +485,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.getAssertion(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Constraint); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Constraint); }); }); @@ -551,7 +551,7 @@ describe("FidoAuthenticatorService", () => { it("should throw error", async () => { const result = async () => await authenticator.getAssertion(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.NotAllowed); }); }); @@ -642,7 +642,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.getAssertion(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.NotAllowed); }); it("should throw error if user verification fails and cipher requires reprompt", async () => { @@ -654,7 +654,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.getAssertion(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.NotAllowed); }); }); @@ -786,7 +786,7 @@ describe("FidoAuthenticatorService", () => { const result = async () => await authenticator.getAssertion(params, windowReference); - await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); + await expect(result).rejects.toThrow(Fido2AuthenticatorErrorCode.Unknown); }); }); diff --git a/libs/state/src/state-migrations/migration-builder.spec.ts b/libs/state/src/state-migrations/migration-builder.spec.ts index 15e526b94560..41e95b511182 100644 --- a/libs/state/src/state-migrations/migration-builder.spec.ts +++ b/libs/state/src/state-migrations/migration-builder.spec.ts @@ -91,28 +91,28 @@ describe("MigrationBuilder", () => { const helper = new MigrationHelper(0, mock(), mock(), "general", clientType); const spy = jest.spyOn(migrator, "migrate"); await sut.migrate(helper); - expect(spy).toBeCalledWith(helper); + expect(spy).toHaveBeenCalledWith(helper); }); it("should rollback", async () => { const helper = new MigrationHelper(1, mock(), mock(), "general", clientType); const spy = jest.spyOn(rollback_migrator, "rollback"); await sut.migrate(helper); - expect(spy).toBeCalledWith(helper); + expect(spy).toHaveBeenCalledWith(helper); }); it("should update version on migrate", async () => { const helper = new MigrationHelper(0, mock(), mock(), "general", clientType); const spy = jest.spyOn(migrator, "updateVersion"); await sut.migrate(helper); - expect(spy).toBeCalledWith(helper, "up"); + expect(spy).toHaveBeenCalledWith(helper, "up"); }); it("should update version on rollback", async () => { const helper = new MigrationHelper(1, mock(), mock(), "general", clientType); const spy = jest.spyOn(rollback_migrator, "updateVersion"); await sut.migrate(helper); - expect(spy).toBeCalledWith(helper, "down"); + expect(spy).toHaveBeenCalledWith(helper, "down"); }); it("should not run the migrator if the current version does not match the from version", async () => { @@ -120,8 +120,8 @@ describe("MigrationBuilder", () => { const migrate = jest.spyOn(migrator, "migrate"); const rollback = jest.spyOn(rollback_migrator, "rollback"); await sut.migrate(helper); - expect(migrate).not.toBeCalled(); - expect(rollback).not.toBeCalled(); + expect(migrate).not.toHaveBeenCalled(); + expect(rollback).not.toHaveBeenCalled(); }); it("should not update version if the current version does not match the from version", async () => { @@ -129,8 +129,8 @@ describe("MigrationBuilder", () => { const migrate = jest.spyOn(migrator, "updateVersion"); const rollback = jest.spyOn(rollback_migrator, "updateVersion"); await sut.migrate(helper); - expect(migrate).not.toBeCalled(); - expect(rollback).not.toBeCalled(); + expect(migrate).not.toHaveBeenCalled(); + expect(rollback).not.toHaveBeenCalled(); }); }); diff --git a/libs/state/src/state-migrations/migration-helper.spec.ts b/libs/state/src/state-migrations/migration-helper.spec.ts index d811354b0b04..a2f9fa8836d4 100644 --- a/libs/state/src/state-migrations/migration-helper.spec.ts +++ b/libs/state/src/state-migrations/migration-helper.spec.ts @@ -134,7 +134,7 @@ describe("RemoveLegacyEtmKeyMigrator", () => { it("should throw if the current version is less than 9", () => { expect(() => sut.getFromGlobal({ stateDefinition: { name: "serviceName" }, key: "key" }), - ).toThrowError("No key builder should be used for versions prior to 9."); + ).toThrow("No key builder should be used for versions prior to 9."); }); }); @@ -154,7 +154,7 @@ describe("RemoveLegacyEtmKeyMigrator", () => { { stateDefinition: { name: "serviceName" }, key: "key" }, "global_serviceName_key", ), - ).toThrowError("No key builder should be used for versions prior to 9."); + ).toThrow("No key builder should be used for versions prior to 9."); }); }); @@ -171,7 +171,7 @@ describe("RemoveLegacyEtmKeyMigrator", () => { it("should throw if the current version is less than 9", () => { expect(() => sut.getFromUser("userId", { stateDefinition: { name: "serviceName" }, key: "key" }), - ).toThrowError("No key builder should be used for versions prior to 9."); + ).toThrow("No key builder should be used for versions prior to 9."); }); }); @@ -193,7 +193,7 @@ describe("RemoveLegacyEtmKeyMigrator", () => { { stateDefinition: { name: "serviceName" }, key: "key" }, "new_value", ), - ).toThrowError("No key builder should be used for versions prior to 9."); + ).toThrow("No key builder should be used for versions prior to 9."); }); }); }); diff --git a/libs/state/src/state-migrations/migrator.spec.ts b/libs/state/src/state-migrations/migrator.spec.ts index 762a608dba74..4c03930b1cd4 100644 --- a/libs/state/src/state-migrations/migrator.spec.ts +++ b/libs/state/src/state-migrations/migrator.spec.ts @@ -60,7 +60,7 @@ describe("migrator default methods", () => { describe("up", () => { it("should update the version", async () => { await sut.updateVersion(helper, "up"); - expect(storage.save).toBeCalledWith("stateVersion", 1); + expect(storage.save).toHaveBeenCalledWith("stateVersion", 1); expect(helper.currentVersion).toBe(1); }); }); @@ -69,7 +69,7 @@ describe("migrator default methods", () => { it("should update the version", async () => { helper.currentVersion = 1; await sut.updateVersion(helper, "down"); - expect(storage.save).toBeCalledWith("stateVersion", 0); + expect(storage.save).toHaveBeenCalledWith("stateVersion", 0); expect(helper.currentVersion).toBe(0); }); }); diff --git a/package-lock.json b/package-lock.json index 2a9dc8176570..eb5ecfbc21b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -143,6 +143,7 @@ "eslint-config-prettier": "10.1.8", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-import": "2.32.0", + "eslint-plugin-jest": "29.15.2", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", "eslint-plugin-storybook": "9.1.20", @@ -23637,6 +23638,36 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-jest": { + "version": "29.15.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.2.tgz", + "integrity": "sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.0.0" + }, + "engines": { + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "jest": "*", + "typescript": ">=4.8.4 <7.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/eslint-plugin-rxjs": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-rxjs/-/eslint-plugin-rxjs-5.0.3.tgz", diff --git a/package.json b/package.json index fa5c0d0b4caf..567c2bae41ae 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "eslint-config-prettier": "10.1.8", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-import": "2.32.0", + "eslint-plugin-jest": "29.15.2", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", "eslint-plugin-storybook": "9.1.20",