1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 17:23:37 +00:00

[PM-5189] Fixing jest tests within AutofillInit

This commit is contained in:
Cesar Gonzalez
2024-04-08 15:13:46 -05:00
parent a93ec6610e
commit f1e90edb9e
4 changed files with 33 additions and 389 deletions

View File

@@ -12,6 +12,7 @@ describe("AutofillInit", () => {
let autofillInit: AutofillInit;
const autofillOverlayContentService = mock<AutofillOverlayContentService>();
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<IntersectionObserver>());
});
@@ -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,
// );
// });
// });
});
});

View File

@@ -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,
);
}

View File

@@ -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 () => {

View File

@@ -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 });
}
/**