diff --git a/apps/browser/src/autofill/content/autofill-init.spec.ts b/apps/browser/src/autofill/content/autofill-init.spec.ts index 63d5692ab10..6b8420835fa 100644 --- a/apps/browser/src/autofill/content/autofill-init.spec.ts +++ b/apps/browser/src/autofill/content/autofill-init.spec.ts @@ -12,6 +12,7 @@ describe("AutofillInit", () => { let autofillInit: AutofillInit; const autofillOverlayContentService = mock(); const originalDocumentReadyState = document.readyState; + let sendExtensionMessageSpy: jest.SpyInstance; beforeEach(() => { chrome.runtime.connect = jest.fn().mockReturnValue({ @@ -20,6 +21,9 @@ describe("AutofillInit", () => { }, }); autofillInit = new AutofillInit(autofillOverlayContentService); + sendExtensionMessageSpy = jest + .spyOn(autofillInit as any, "sendExtensionMessage") + .mockImplementation(); window.IntersectionObserver = jest.fn(() => mock()); }); @@ -48,13 +52,9 @@ describe("AutofillInit", () => { autofillInit.init(); jest.advanceTimersByTime(250); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - { - command: "bgCollectPageDetails", - sender: "autofillInit", - }, - expect.any(Function), - ); + expect(sendExtensionMessageSpy).toHaveBeenCalledWith("bgCollectPageDetails", { + sender: "autofillInit", + }); }); it("registers a window load listener to collect the page details if the document is not in a `complete` ready state", () => { @@ -250,10 +250,6 @@ describe("AutofillInit", () => { it("updates the isCurrentlyFilling property of the overlay to true after filling", async () => { jest.useFakeTimers(); - jest.spyOn(autofillInit as any, "updateOverlayIsCurrentlyFilling"); - // jest - // .spyOn(autofillInit["autofillOverlayContentService"], "focusMostRecentOverlayField") - // .mockImplementation(); sendMockExtensionMessage({ command: "fillForm", @@ -263,269 +259,21 @@ describe("AutofillInit", () => { await flushPromises(); jest.advanceTimersByTime(300); - // expect(autofillInit["updateOverlayIsCurrentlyFilling"]).toHaveBeenNthCalledWith(1, true); + expect(sendExtensionMessageSpy).toHaveBeenNthCalledWith( + 1, + "updateIsFieldCurrentlyFilling", + { isFieldCurrentlyFilling: true }, + ); expect(autofillInit["insertAutofillContentService"].fillForm).toHaveBeenCalledWith( fillScript, ); - // expect(autofillInit["updateOverlayIsCurrentlyFilling"]).toHaveBeenNthCalledWith(2, false); - }); - - it("skips attempting to focus the most recent field if the autofillOverlayContentService is not present", async () => { - jest.useFakeTimers(); - const newAutofillInit = new AutofillInit(undefined); - newAutofillInit.init(); - jest.spyOn(newAutofillInit as any, "updateOverlayIsCurrentlyFilling"); - jest - .spyOn(newAutofillInit["insertAutofillContentService"], "fillForm") - .mockImplementation(); - - sendMockExtensionMessage({ - command: "fillForm", - fillScript, - pageDetailsUrl: window.location.href, - }); - await flushPromises(); - jest.advanceTimersByTime(300); - - // expect(newAutofillInit["updateOverlayIsCurrentlyFilling"]).toHaveBeenNthCalledWith( - // 1, - // true, - // ); - expect(newAutofillInit["insertAutofillContentService"].fillForm).toHaveBeenCalledWith( - fillScript, + expect(sendExtensionMessageSpy).toHaveBeenNthCalledWith( + 2, + "updateIsFieldCurrentlyFilling", + { isFieldCurrentlyFilling: false }, ); - // expect(newAutofillInit["updateOverlayIsCurrentlyFilling"]).not.toHaveBeenNthCalledWith( - // 2, - // false, - // ); }); }); - - // describe("openAutofillOverlay", () => { - // const message = { - // command: "openAutofillOverlay", - // data: { - // isFocusingFieldElement: true, - // isOpeningFullOverlay: true, - // authStatus: AuthenticationStatus.Unlocked, - // }, - // }; - // - // it("skips attempting to open the autofill overlay if the autofillOverlayContentService is not present", () => { - // const newAutofillInit = new AutofillInit(undefined); - // newAutofillInit.init(); - // - // sendMockExtensionMessage(message); - // - // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); - // }); - // - // it("opens the autofill overlay", () => { - // sendMockExtensionMessage(message); - // - // expect( - // autofillInit["autofillOverlayContentService"].openAutofillOverlay, - // ).toHaveBeenCalledWith({ - // isFocusingFieldElement: message.data.isFocusingFieldElement, - // isOpeningFullOverlay: message.data.isOpeningFullOverlay, - // authStatus: message.data.authStatus, - // }); - // }); - // }); - - // describe("closeAutofillOverlay", () => { - // beforeEach(() => { - // autofillInit["autofillOverlayContentService"].isFieldCurrentlyFocused = false; - // autofillInit["autofillOverlayContentService"].isCurrentlyFilling = false; - // }); - // - // it("skips attempting to remove the overlay if the autofillOverlayContentService is not present", () => { - // const newAutofillInit = new AutofillInit(undefined); - // newAutofillInit.init(); - // jest.spyOn(newAutofillInit as any, "removeAutofillOverlay"); - // - // sendMockExtensionMessage({ - // command: "closeAutofillOverlay", - // data: { forceCloseOverlay: false }, - // }); - // - // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); - // }); - // - // it("removes the autofill overlay if the message flags a forced closure", () => { - // sendMockExtensionMessage({ - // command: "closeAutofillOverlay", - // data: { forceCloseOverlay: true }, - // }); - // - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlay, - // ).toHaveBeenCalled(); - // }); - // - // it("ignores the message if a field is currently focused", () => { - // autofillInit["autofillOverlayContentService"].isFieldCurrentlyFocused = true; - // - // sendMockExtensionMessage({ command: "closeAutofillOverlay" }); - // - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlayList, - // ).not.toHaveBeenCalled(); - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlay, - // ).not.toHaveBeenCalled(); - // }); - // - // it("removes the autofill overlay list if the overlay is currently filling", () => { - // autofillInit["autofillOverlayContentService"].isCurrentlyFilling = true; - // - // sendMockExtensionMessage({ command: "closeAutofillOverlay" }); - // - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlayList, - // ).toHaveBeenCalled(); - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlay, - // ).not.toHaveBeenCalled(); - // }); - // - // it("removes the entire overlay if the overlay is not currently filling", () => { - // sendMockExtensionMessage({ command: "closeAutofillOverlay" }); - // - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlayList, - // ).not.toHaveBeenCalled(); - // expect( - // autofillInit["autofillOverlayContentService"].removeAutofillOverlay, - // ).toHaveBeenCalled(); - // }); - // }); - - // describe("addNewVaultItemFromOverlay", () => { - // it("will not add a new vault item if the autofillOverlayContentService is not present", () => { - // const newAutofillInit = new AutofillInit(undefined); - // newAutofillInit.init(); - // - // sendMockExtensionMessage({ command: "addNewVaultItemFromOverlay" }); - // - // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); - // }); - // - // it("will add a new vault item", () => { - // sendMockExtensionMessage({ command: "addNewVaultItemFromOverlay" }); - // - // expect(autofillInit["autofillOverlayContentService"].addNewVaultItem).toHaveBeenCalled(); - // }); - // }); - - // describe("updateIsOverlayCiphersPopulated", () => { - // const message = { - // command: "updateIsOverlayCiphersPopulated", - // data: { - // isOverlayCiphersPopulated: true, - // }, - // }; - // - // it("skips updating whether the ciphers are populated if the autofillOverlayContentService does note exist", () => { - // const newAutofillInit = new AutofillInit(undefined); - // newAutofillInit.init(); - // - // sendMockExtensionMessage(message); - // - // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); - // }); - // - // it("updates whether the overlay ciphers are populated", () => { - // sendMockExtensionMessage(message); - // - // expect(autofillInit["autofillOverlayContentService"].isOverlayCiphersPopulated).toEqual( - // message.data.isOverlayCiphersPopulated, - // ); - // }); - // }); - - // describe("bgUnlockPopoutOpened", () => { - // it("skips attempting to blur and remove the overlay if the autofillOverlayContentService is not present", () => { - // const newAutofillInit = new AutofillInit(undefined); - // newAutofillInit.init(); - // jest.spyOn(newAutofillInit as any, "removeAutofillOverlay"); - // - // sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" }); - // - // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); - // // expect(newAutofillInit["removeAutofillOverlay"]).not.toHaveBeenCalled(); - // }); - // - // it("blurs the most recently focused feel and remove the autofill overlay", () => { - // jest.spyOn(autofillInit["autofillOverlayContentService"], "blurMostRecentOverlayField"); - // jest.spyOn(autofillInit as any, "removeAutofillOverlay"); - // - // sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" }); - // - // expect( - // autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField, - // ).toHaveBeenCalled(); - // // expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled(); - // }); - // }); - // - // describe("bgVaultItemRepromptPopoutOpened", () => { - // it("skips attempting to blur and remove the overlay if the autofillOverlayContentService is not present", () => { - // const newAutofillInit = new AutofillInit(undefined); - // newAutofillInit.init(); - // jest.spyOn(newAutofillInit as any, "removeAutofillOverlay"); - // - // sendMockExtensionMessage({ command: "bgVaultItemRepromptPopoutOpened" }); - // - // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); - // // expect(newAutofillInit["removeAutofillOverlay"]).not.toHaveBeenCalled(); - // }); - // - // it("blurs the most recently focused feel and remove the autofill overlay", () => { - // jest.spyOn(autofillInit["autofillOverlayContentService"], "blurMostRecentOverlayField"); - // jest.spyOn(autofillInit as any, "removeAutofillOverlay"); - // - // sendMockExtensionMessage({ command: "bgVaultItemRepromptPopoutOpened" }); - // - // expect( - // autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField, - // ).toHaveBeenCalled(); - // // expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled(); - // }); - // }); - - // describe("updateAutofillOverlayVisibility", () => { - // beforeEach(() => { - // autofillInit["autofillOverlayContentService"].autofillOverlayVisibility = - // AutofillOverlayVisibility.OnButtonClick; - // }); - // - // it("skips attempting to update the overlay visibility if the autofillOverlayVisibility data value is not present", () => { - // sendMockExtensionMessage({ - // command: "updateAutofillOverlayVisibility", - // data: {}, - // }); - // - // expect(autofillInit["autofillOverlayContentService"].autofillOverlayVisibility).toEqual( - // AutofillOverlayVisibility.OnButtonClick, - // ); - // }); - // - // it("updates the overlay visibility value", () => { - // const message = { - // command: "updateAutofillOverlayVisibility", - // data: { - // autofillOverlayVisibility: AutofillOverlayVisibility.Off, - // }, - // }; - // - // sendMockExtensionMessage(message); - // - // expect(autofillInit["autofillOverlayContentService"].autofillOverlayVisibility).toEqual( - // message.data.autofillOverlayVisibility, - // ); - // }); - // }); }); }); diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts index ca175ff1672..49a2c1bd8ac 100644 --- a/apps/browser/src/autofill/content/autofill-init.ts +++ b/apps/browser/src/autofill/content/autofill-init.ts @@ -15,6 +15,7 @@ import { } from "./abstractions/autofill-init"; class AutofillInit implements AutofillInitInterface { + private readonly sendExtensionMessage = sendExtensionMessage; private readonly autofillOverlayContentService: AutofillOverlayContentService | undefined; private readonly inlineMenuElements: InlineMenuElements | undefined; private readonly domElementVisibilityService: DomElementVisibilityService; @@ -85,7 +86,7 @@ class AutofillInit implements AutofillInitInterface { const sendCollectDetailsMessage = () => { this.clearSendCollectDetailsMessageTimeout(); this.sendCollectDetailsMessageTimeout = setTimeout( - () => sendExtensionMessage("bgCollectPageDetails", { sender: "autofillInit" }), + () => this.sendExtensionMessage("bgCollectPageDetails", { sender: "autofillInit" }), 250, ); }; @@ -136,16 +137,16 @@ class AutofillInit implements AutofillInitInterface { } this.blurAndRemoveOverlay(); - await sendExtensionMessage("updateIsFieldCurrentlyFilling", { isFieldCurrentlyFilling: true }); + await this.sendExtensionMessage("updateIsFieldCurrentlyFilling", { + isFieldCurrentlyFilling: true, + }); await this.insertAutofillContentService.fillForm(fillScript); - if (!this.autofillOverlayContentService) { - return; - } - setTimeout( () => - sendExtensionMessage("updateIsFieldCurrentlyFilling", { isFieldCurrentlyFilling: false }), + this.sendExtensionMessage("updateIsFieldCurrentlyFilling", { + isFieldCurrentlyFilling: false, + }), 250, ); } diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts index a7c11ea472f..51dcbdf9433 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts @@ -20,7 +20,7 @@ describe("AutofillOverlayIframeService", () => { let shadowAppendSpy: jest.SpyInstance; let handlePortDisconnectSpy: jest.SpyInstance; let handlePortMessageSpy: jest.SpyInstance; - let handleWindowMessageSpy: jest.SpyInstance; + let sendExtensionMessageSpy: jest.SpyInstance; beforeEach(() => { const shadow = document.createElement("div").attachShadow({ mode: "open" }); @@ -35,10 +35,13 @@ describe("AutofillOverlayIframeService", () => { "handlePortDisconnect", ); handlePortMessageSpy = jest.spyOn(autofillOverlayIframeService as any, "handlePortMessage"); - handleWindowMessageSpy = jest.spyOn(autofillOverlayIframeService as any, "handleWindowMessage"); chrome.runtime.connect = jest.fn((connectInfo: chrome.runtime.ConnectInfo) => createPortSpyMock(connectInfo.name), ) as unknown as typeof chrome.runtime.connect; + sendExtensionMessageSpy = jest.spyOn( + autofillOverlayIframeService as any, + "sendExtensionMessage", + ); }); afterEach(() => { @@ -86,7 +89,6 @@ describe("AutofillOverlayIframeService", () => { expect(chrome.runtime.connect).toBeCalledWith({ name: AutofillOverlayPort.Button }); expect(portSpy.onDisconnect.addListener).toBeCalledWith(handlePortDisconnectSpy); expect(portSpy.onMessage.addListener).toBeCalledWith(handlePortMessageSpy); - expect(globalThis.addEventListener).toBeCalledWith(EVENTS.MESSAGE, handleWindowMessageSpy); }); it("skips announcing the aria alert if the aria alert element is not populated", () => { @@ -144,17 +146,6 @@ describe("AutofillOverlayIframeService", () => { expect(autofillOverlayIframeService["iframe"].style.display).toBe("block"); }); - it("removes the global message listener", () => { - jest.spyOn(globalThis, "removeEventListener"); - - triggerPortOnDisconnectEvent(portSpy); - - expect(globalThis.removeEventListener).toBeCalledWith( - EVENTS.MESSAGE, - handleWindowMessageSpy, - ); - }); - it("removes the port's onMessage listener", () => { triggerPortOnDisconnectEvent(portSpy); @@ -371,103 +362,6 @@ describe("AutofillOverlayIframeService", () => { expect(autofillOverlayIframeService["iframe"].style.display).toBe("none"); }); }); - - describe("handleWindowMessage", () => { - it("ignores window messages when the port is not set", () => { - autofillOverlayIframeService["port"] = null; - - globalThis.dispatchEvent(new MessageEvent("message", { data: {} })); - - expect(autofillOverlayIframeService["port"]).toBeNull(); - }); - - it("ignores window messages whose source is not the iframe's content window", () => { - globalThis.dispatchEvent( - new MessageEvent("message", { - data: {}, - source: window, - }), - ); - - expect(portSpy.postMessage).not.toBeCalled(); - }); - - it("ignores window messages whose origin is not from the extension origin", () => { - globalThis.dispatchEvent( - new MessageEvent("message", { - data: {}, - source: autofillOverlayIframeService["iframe"].contentWindow, - origin: "https://www.google.com", - }), - ); - - expect(portSpy.postMessage).not.toBeCalled(); - }); - - it("passes the window message from an iframe element to the background port", () => { - globalThis.dispatchEvent( - new MessageEvent("message", { - data: { command: "not-a-handled-command" }, - source: autofillOverlayIframeService["iframe"].contentWindow, - origin: "chrome-extension://id", - }), - ); - - expect(portSpy.postMessage).toBeCalledWith({ command: "not-a-handled-command" }); - }); - - it("updates the overlay list height", () => { - globalThis.dispatchEvent( - new MessageEvent("message", { - data: { command: "updateAutofillOverlayListHeight", styles: { height: "300px" } }, - source: autofillOverlayIframeService["iframe"].contentWindow, - origin: "chrome-extension://id", - }), - ); - - expect(autofillOverlayIframeService["iframe"].style.height).toBe("300px"); - }); - - describe("getPageColorScheme window message", () => { - afterEach(() => { - globalThis.document.head.innerHTML = ""; - }); - - it("gets and updates the overlay page color scheme", () => { - const colorSchemeMetaTag = globalThis.document.createElement("meta"); - colorSchemeMetaTag.setAttribute("name", "color-scheme"); - colorSchemeMetaTag.setAttribute("content", "dark"); - globalThis.document.head.append(colorSchemeMetaTag); - globalThis.dispatchEvent( - new MessageEvent("message", { - data: { command: "getPageColorScheme" }, - source: autofillOverlayIframeService["iframe"].contentWindow, - origin: "chrome-extension://id", - }), - ); - - expect(autofillOverlayIframeService["iframe"].contentWindow.postMessage).toBeCalledWith( - { command: "updateOverlayPageColorScheme", colorScheme: "dark" }, - "*", - ); - }); - - it("sends a normal color scheme if the color scheme meta tag is not present", () => { - globalThis.dispatchEvent( - new MessageEvent("message", { - data: { command: "getPageColorScheme" }, - source: autofillOverlayIframeService["iframe"].contentWindow, - origin: "chrome-extension://id", - }), - ); - - expect(autofillOverlayIframeService["iframe"].contentWindow.postMessage).toBeCalledWith( - { command: "updateOverlayPageColorScheme", colorScheme: "normal" }, - "*", - ); - }); - }); - }); }); describe("mutation observer", () => { @@ -509,7 +403,7 @@ describe("AutofillOverlayIframeService", () => { autofillOverlayIframeService["iframe"].src = "http://malicious-site.com"; await flushPromises(); - expect(portSpy.postMessage).toBeCalledWith({ command: "forceCloseAutofillOverlay" }); + expect(sendExtensionMessageSpy).toBeCalledWith("closeAutofillOverlay", { forceClose: true }); }); it("force closes the autofill overlay if excessive mutations are being triggered", async () => { @@ -519,7 +413,7 @@ describe("AutofillOverlayIframeService", () => { autofillOverlayIframeService["iframe"].src = "http://malicious-site.com"; await flushPromises(); - expect(portSpy.postMessage).toBeCalledWith({ command: "forceCloseAutofillOverlay" }); + expect(sendExtensionMessageSpy).toBeCalledWith("closeAutofillOverlay", { forceClose: true }); }); it("resets the excessive mutations and foreign mutation counters", async () => { diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts index 7bfa676d535..4e0c75d56b0 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts @@ -9,6 +9,7 @@ import { } from "../abstractions/autofill-overlay-iframe.service"; class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterface { + private sendExtensionMessage = sendExtensionMessage; private port: chrome.runtime.Port | null = null; private iframeMutationObserver: MutationObserver; private iframe: HTMLIFrameElement; @@ -304,7 +305,7 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf }; private forceCloseAutofillOverlay() { - void sendExtensionMessage("closeAutofillOverlay", { forceClose: true }); + void this.sendExtensionMessage("closeAutofillOverlay", { forceClose: true }); } /**