mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-24 21:41:33 +01:00
[PM-5189] Implementing jest tests for the OverlayBackground
This commit is contained in:
parent
2efbc5ce51
commit
5e70d042b4
@ -3,7 +3,10 @@ import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
||||
import {
|
||||
AutofillOverlayVisibility,
|
||||
SHOW_AUTOFILL_BUTTON,
|
||||
} from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction as AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import {
|
||||
DefaultDomainSettingsService,
|
||||
@ -44,7 +47,14 @@ import {
|
||||
createPortSpyMock,
|
||||
createFocusedFieldDataMock,
|
||||
} from "../spec/autofill-mocks";
|
||||
import { flushPromises, sendMockExtensionMessage, sendPortMessage } from "../spec/testing-utils";
|
||||
import {
|
||||
flushPromises,
|
||||
sendMockExtensionMessage,
|
||||
sendPortMessage,
|
||||
triggerPortOnConnectEvent,
|
||||
triggerPortOnDisconnectEvent,
|
||||
triggerPortOnMessageEvent,
|
||||
} from "../spec/testing-utils";
|
||||
|
||||
import {
|
||||
FocusedFieldData,
|
||||
@ -93,19 +103,19 @@ describe("OverlayBackground", () => {
|
||||
async function initOverlayElementPorts(options = { initList: true, initButton: true }) {
|
||||
const { initList, initButton } = options;
|
||||
if (initButton) {
|
||||
await overlayBackground["handlePortOnConnect"](createPortSpyMock(AutofillOverlayPort.Button));
|
||||
triggerPortOnConnectEvent(createPortSpyMock(AutofillOverlayPort.Button));
|
||||
buttonPortSpy = overlayBackground["inlineMenuButtonPort"];
|
||||
|
||||
buttonMessageConnectorSpy = createPortSpyMock(AutofillOverlayPort.ButtonMessageConnector);
|
||||
await overlayBackground["handlePortOnConnect"](buttonMessageConnectorSpy);
|
||||
triggerPortOnConnectEvent(buttonMessageConnectorSpy);
|
||||
}
|
||||
|
||||
if (initList) {
|
||||
await overlayBackground["handlePortOnConnect"](createPortSpyMock(AutofillOverlayPort.List));
|
||||
triggerPortOnConnectEvent(createPortSpyMock(AutofillOverlayPort.List));
|
||||
listPortSpy = overlayBackground["inlineMenuListPort"];
|
||||
|
||||
listMessageConnectorSpy = createPortSpyMock(AutofillOverlayPort.ListMessageConnector);
|
||||
await overlayBackground["handlePortOnConnect"](listMessageConnectorSpy);
|
||||
triggerPortOnConnectEvent(listMessageConnectorSpy);
|
||||
}
|
||||
|
||||
return { buttonPortSpy, listPortSpy };
|
||||
@ -1269,6 +1279,25 @@ describe("OverlayBackground", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("handle extension onMessage", () => {
|
||||
it("will return early if the message command is not present within the extensionMessageHandlers", () => {
|
||||
const message = {
|
||||
command: "not-a-command",
|
||||
};
|
||||
const sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
|
||||
const sendResponse = jest.fn();
|
||||
|
||||
const returnValue = overlayBackground["handleExtensionMessage"](
|
||||
message,
|
||||
sender,
|
||||
sendResponse,
|
||||
);
|
||||
|
||||
expect(returnValue).toBe(undefined);
|
||||
expect(sendResponse).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("inline menu button message handlers", () => {
|
||||
let sender: chrome.runtime.MessageSender;
|
||||
const portKey = "inlineMenuButtonPort";
|
||||
@ -1613,5 +1642,152 @@ describe("OverlayBackground", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("viewSelectedCipher message handler", () => {
|
||||
let openViewVaultItemPopoutSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
openViewVaultItemPopoutSpy = jest
|
||||
.spyOn(overlayBackground as any, "openViewVaultItemPopout")
|
||||
.mockImplementation();
|
||||
});
|
||||
|
||||
it("returns early if the passed cipher ID does not match one of the inline menu ciphers", async () => {
|
||||
overlayBackground["inlineMenuCiphers"] = new Map([
|
||||
["overlay-cipher-0", mock<CipherView>({ id: "overlay-cipher-0" })],
|
||||
]);
|
||||
|
||||
sendPortMessage(listMessageConnectorSpy, {
|
||||
command: "viewSelectedCipher",
|
||||
overlayCipherId: "overlay-cipher-1",
|
||||
portKey,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(openViewVaultItemPopoutSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("will open the view vault item popout with the selected cipher", async () => {
|
||||
const cipher = mock<CipherView>({ id: "overlay-cipher-1" });
|
||||
overlayBackground["inlineMenuCiphers"] = new Map([
|
||||
["overlay-cipher-0", mock<CipherView>({ id: "overlay-cipher-0" })],
|
||||
["overlay-cipher-1", cipher],
|
||||
]);
|
||||
|
||||
sendPortMessage(listMessageConnectorSpy, {
|
||||
command: "viewSelectedCipher",
|
||||
overlayCipherId: "overlay-cipher-1",
|
||||
portKey,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(openViewVaultItemPopoutSpy).toHaveBeenCalledWith(sender.tab, {
|
||||
cipherId: cipher.id,
|
||||
action: SHOW_AUTOFILL_BUTTON,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("redirectAutofillInlineMenuFocusOut message handler", () => {
|
||||
it("redirects focus out of the inline menu list", async () => {
|
||||
sendPortMessage(listMessageConnectorSpy, {
|
||||
command: "redirectAutofillInlineMenuFocusOut",
|
||||
direction: RedirectFocusDirection.Next,
|
||||
portKey,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(tabSendMessageDataSpy).toHaveBeenCalledWith(
|
||||
sender.tab,
|
||||
"redirectAutofillInlineMenuFocusOut",
|
||||
{ direction: RedirectFocusDirection.Next },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateAutofillInlineMenuListHeight message handler", () => {
|
||||
it("sends a message to the list port to update the menu iframe position", () => {
|
||||
sendPortMessage(listMessageConnectorSpy, {
|
||||
command: "updateAutofillInlineMenuListHeight",
|
||||
styles: { height: "100px" },
|
||||
portKey,
|
||||
});
|
||||
|
||||
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
|
||||
command: "updateInlineMenuIframePosition",
|
||||
styles: { height: "100px" },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("handle port onConnect", () => {
|
||||
it("skips setting up the overlay port if the port connection is not for an overlay element", async () => {
|
||||
const port = createPortSpyMock("not-an-overlay-element");
|
||||
|
||||
triggerPortOnConnectEvent(port);
|
||||
await flushPromises();
|
||||
|
||||
expect(port.onMessage.addListener).not.toHaveBeenCalled();
|
||||
expect(port.postMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("stores an existing overlay port so that it can be disconnected at a later time", async () => {
|
||||
overlayBackground["inlineMenuButtonPort"] = mock<chrome.runtime.Port>();
|
||||
|
||||
await initOverlayElementPorts({ initList: false, initButton: true });
|
||||
await flushPromises();
|
||||
|
||||
expect(overlayBackground["expiredPorts"].length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("handle overlay element port onMessage", () => {
|
||||
let sender: chrome.runtime.MessageSender;
|
||||
const portKey = "inlineMenuListPort";
|
||||
|
||||
beforeEach(async () => {
|
||||
sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
|
||||
portKeyForTabSpy[sender.tab.id] = portKey;
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
|
||||
await initOverlayElementPorts();
|
||||
listMessageConnectorSpy.sender = sender;
|
||||
openUnlockPopoutSpy.mockImplementation();
|
||||
});
|
||||
|
||||
it("ignores messages that do not contain a valid portKey", async () => {
|
||||
triggerPortOnMessageEvent(buttonMessageConnectorSpy, {
|
||||
command: "autofillInlineMenuBlurred",
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(listPortSpy.postMessage).not.toHaveBeenCalledWith({
|
||||
command: "checkAutofillInlineMenuListFocused",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores messages from ports that are not listened to", () => {
|
||||
triggerPortOnMessageEvent(buttonPortSpy, {
|
||||
command: "autofillInlineMenuBlurred",
|
||||
portKey,
|
||||
});
|
||||
|
||||
expect(listPortSpy.postMessage).not.toHaveBeenCalledWith({
|
||||
command: "checkAutofillInlineMenuListFocused",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("handle port onDisconnect", () => {
|
||||
it("sets the disconnected port to a `null` value", async () => {
|
||||
await initOverlayElementPorts();
|
||||
|
||||
triggerPortOnDisconnectEvent(buttonPortSpy);
|
||||
triggerPortOnDisconnectEvent(listPortSpy);
|
||||
await flushPromises();
|
||||
|
||||
expect(overlayBackground["inlineMenuListPort"]).toBeNull();
|
||||
expect(overlayBackground["inlineMenuButtonPort"]).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1170,7 +1170,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
message: OverlayBackgroundExtensionMessage,
|
||||
port: chrome.runtime.Port,
|
||||
) => {
|
||||
if (this.portKeyForTab[port.sender.tab.id] !== message?.portKey) {
|
||||
const tabPortKey = this.portKeyForTab[port.sender.tab.id];
|
||||
if (!tabPortKey || tabPortKey !== message?.portKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,21 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
function triggerTestFailure() {
|
||||
export function triggerTestFailure() {
|
||||
expect(true).toBe("Test has failed.");
|
||||
}
|
||||
|
||||
const scheduler = typeof setImmediate === "function" ? setImmediate : setTimeout;
|
||||
function flushPromises() {
|
||||
export function flushPromises() {
|
||||
return new Promise(function (resolve) {
|
||||
scheduler(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function postWindowMessage(data: any, origin = "https://localhost/", source = window) {
|
||||
export function postWindowMessage(data: any, origin = "https://localhost/", source = window) {
|
||||
globalThis.dispatchEvent(new MessageEvent("message", { data, origin, source }));
|
||||
}
|
||||
|
||||
function sendMockExtensionMessage(
|
||||
export function sendMockExtensionMessage(
|
||||
message: any,
|
||||
sender?: chrome.runtime.MessageSender,
|
||||
sendResponse?: CallableFunction,
|
||||
@ -32,7 +32,7 @@ function sendMockExtensionMessage(
|
||||
);
|
||||
}
|
||||
|
||||
function triggerRuntimeOnConnectEvent(port: chrome.runtime.Port) {
|
||||
export function triggerRuntimeOnConnectEvent(port: chrome.runtime.Port) {
|
||||
(chrome.runtime.onConnect.addListener as unknown as jest.SpyInstance).mock.calls.forEach(
|
||||
(call) => {
|
||||
const callback = call[0];
|
||||
@ -41,21 +41,37 @@ function triggerRuntimeOnConnectEvent(port: chrome.runtime.Port) {
|
||||
);
|
||||
}
|
||||
|
||||
function sendPortMessage(port: chrome.runtime.Port, message: any) {
|
||||
export function sendPortMessage(port: chrome.runtime.Port, message: any) {
|
||||
(port.onMessage.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => {
|
||||
const callback = call[0];
|
||||
callback(message || {}, port);
|
||||
});
|
||||
}
|
||||
|
||||
function triggerPortOnDisconnectEvent(port: chrome.runtime.Port) {
|
||||
export function triggerPortOnConnectEvent(port: chrome.runtime.Port) {
|
||||
(chrome.runtime.onConnect.addListener as unknown as jest.SpyInstance).mock.calls.forEach(
|
||||
(call) => {
|
||||
const callback = call[0];
|
||||
callback(port);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function triggerPortOnMessageEvent(port: chrome.runtime.Port, message: any) {
|
||||
(port.onMessage.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => {
|
||||
const callback = call[0];
|
||||
callback(message, port);
|
||||
});
|
||||
}
|
||||
|
||||
export function triggerPortOnDisconnectEvent(port: chrome.runtime.Port) {
|
||||
(port.onDisconnect.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => {
|
||||
const callback = call[0];
|
||||
callback(port);
|
||||
});
|
||||
}
|
||||
|
||||
function triggerWindowOnFocusedChangedEvent(windowId: number) {
|
||||
export function triggerWindowOnFocusedChangedEvent(windowId: number) {
|
||||
(chrome.windows.onFocusChanged.addListener as unknown as jest.SpyInstance).mock.calls.forEach(
|
||||
(call) => {
|
||||
const callback = call[0];
|
||||
@ -64,7 +80,7 @@ function triggerWindowOnFocusedChangedEvent(windowId: number) {
|
||||
);
|
||||
}
|
||||
|
||||
function triggerTabOnActivatedEvent(activeInfo: chrome.tabs.TabActiveInfo) {
|
||||
export function triggerTabOnActivatedEvent(activeInfo: chrome.tabs.TabActiveInfo) {
|
||||
(chrome.tabs.onActivated.addListener as unknown as jest.SpyInstance).mock.calls.forEach(
|
||||
(call) => {
|
||||
const callback = call[0];
|
||||
@ -73,14 +89,14 @@ function triggerTabOnActivatedEvent(activeInfo: chrome.tabs.TabActiveInfo) {
|
||||
);
|
||||
}
|
||||
|
||||
function triggerTabOnReplacedEvent(addedTabId: number, removedTabId: number) {
|
||||
export function triggerTabOnReplacedEvent(addedTabId: number, removedTabId: number) {
|
||||
(chrome.tabs.onReplaced.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => {
|
||||
const callback = call[0];
|
||||
callback(addedTabId, removedTabId);
|
||||
});
|
||||
}
|
||||
|
||||
function triggerTabOnUpdatedEvent(
|
||||
export function triggerTabOnUpdatedEvent(
|
||||
tabId: number,
|
||||
changeInfo: chrome.tabs.TabChangeInfo,
|
||||
tab: chrome.tabs.Tab,
|
||||
@ -91,14 +107,14 @@ function triggerTabOnUpdatedEvent(
|
||||
});
|
||||
}
|
||||
|
||||
function triggerTabOnRemovedEvent(tabId: number, removeInfo: chrome.tabs.TabRemoveInfo) {
|
||||
export function triggerTabOnRemovedEvent(tabId: number, removeInfo: chrome.tabs.TabRemoveInfo) {
|
||||
(chrome.tabs.onRemoved.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => {
|
||||
const callback = call[0];
|
||||
callback(tabId, removeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
function mockQuerySelectorAllDefinedCall() {
|
||||
export function mockQuerySelectorAllDefinedCall() {
|
||||
const originalDocumentQuerySelectorAll = document.querySelectorAll;
|
||||
document.querySelectorAll = function (selector: string) {
|
||||
return originalDocumentQuerySelectorAll.call(
|
||||
@ -125,19 +141,3 @@ function mockQuerySelectorAllDefinedCall() {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
triggerTestFailure,
|
||||
flushPromises,
|
||||
postWindowMessage,
|
||||
sendMockExtensionMessage,
|
||||
triggerRuntimeOnConnectEvent,
|
||||
sendPortMessage,
|
||||
triggerPortOnDisconnectEvent,
|
||||
triggerWindowOnFocusedChangedEvent,
|
||||
triggerTabOnActivatedEvent,
|
||||
triggerTabOnReplacedEvent,
|
||||
triggerTabOnUpdatedEvent,
|
||||
triggerTabOnRemovedEvent,
|
||||
mockQuerySelectorAllDefinedCall,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user