1
0
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:
Cesar Gonzalez 2024-06-05 10:24:20 -05:00
parent 81e2cd3e1f
commit d3ba598a45
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
3 changed files with 220 additions and 14 deletions

View File

@ -30,6 +30,13 @@ export type WebsiteIconData = {
icon: string;
};
export type FocusedFieldData = {
focusedFieldStyles: Partial<CSSStyleDeclaration>;
focusedFieldRects: Partial<DOMRect>;
tabId?: number;
frameId?: number;
};
export type OverlayAddNewItemMessage = {
login?: {
uri?: string;
@ -39,11 +46,9 @@ export type OverlayAddNewItemMessage = {
};
};
export type FocusedFieldData = {
focusedFieldStyles: Partial<CSSStyleDeclaration>;
focusedFieldRects: Partial<DOMRect>;
tabId?: number;
frameId?: number;
export type CloseInlineMenuMessage = {
forceCloseAutofillInlineMenu?: boolean;
overlayElement?: string;
};
export type OverlayBackgroundExtensionMessage = {
@ -52,8 +57,6 @@ export type OverlayBackgroundExtensionMessage = {
tab?: chrome.tabs.Tab;
sender?: string;
details?: AutofillPageDetails;
overlayElement?: string;
forceCloseAutofillInlineMenu?: boolean;
isAutofillInlineMenuHidden?: boolean;
setTransparentInlineMenu?: boolean;
isFieldCurrentlyFocused?: boolean;
@ -62,7 +65,8 @@ export type OverlayBackgroundExtensionMessage = {
focusedFieldData?: FocusedFieldData;
styles?: Partial<CSSStyleDeclaration>;
data?: LockedVaultPendingNotificationsData;
} & OverlayAddNewItemMessage;
} & OverlayAddNewItemMessage &
CloseInlineMenuMessage;
export type OverlayPortMessage = {
[key: string]: any;
@ -116,7 +120,9 @@ export type OverlayBackgroundExtensionMessageHandlers = {
rebuildSubFrameOffsets: ({ sender }: BackgroundSenderParam) => void;
collectPageDetailsResponse: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
unlockCompleted: ({ message }: BackgroundMessageParam) => void;
addedCipher: () => void;
addEditCipherSubmitted: () => void;
editedCipher: () => void;
deletedCipher: () => void;
};

View File

@ -742,7 +742,7 @@ describe("OverlayBackground", () => {
});
});
describe("openAutofillInlineMenu", () => {
describe("openAutofillInlineMenu message handler", () => {
let sender: chrome.runtime.MessageSender;
beforeEach(() => {
@ -787,6 +787,161 @@ describe("OverlayBackground", () => {
);
});
});
describe("closeAutofillInlineMenu", () => {
let sender: chrome.runtime.MessageSender;
beforeEach(() => {
sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
sendMockExtensionMessage({
command: "updateIsFieldCurrentlyFilling",
isFieldCurrentlyFilling: false,
});
sendMockExtensionMessage({
command: "updateIsFieldCurrentlyFocused",
isFieldCurrentlyFocused: false,
});
});
it("sends a message to close the inline menu without checking field focus state if forcing the closure", async () => {
sendMockExtensionMessage({
command: "updateIsFieldCurrentlyFocused",
isFieldCurrentlyFocused: true,
});
await flushPromises();
sendMockExtensionMessage(
{
command: "closeAutofillInlineMenu",
forceCloseAutofillInlineMenu: true,
overlayElement: AutofillOverlayElement.Button,
},
sender,
);
await flushPromises();
expect(tabsSendMessageSpy).toHaveBeenCalledWith(
sender.tab,
{
command: "closeInlineMenu",
overlayElement: AutofillOverlayElement.Button,
},
{ frameId: 0 },
);
});
it("skips sending a message to close the inline menu if a form field is currently focused", async () => {
sendMockExtensionMessage({
command: "updateIsFieldCurrentlyFocused",
isFieldCurrentlyFocused: true,
});
await flushPromises();
sendMockExtensionMessage(
{
command: "closeAutofillInlineMenu",
forceCloseAutofillInlineMenu: false,
overlayElement: AutofillOverlayElement.Button,
},
sender,
);
await flushPromises();
expect(tabsSendMessageSpy).not.toHaveBeenCalled();
});
it("sends a message to close the inline menu list only if the field is currently filling", async () => {
sendMockExtensionMessage({
command: "updateIsFieldCurrentlyFilling",
isFieldCurrentlyFilling: true,
});
await flushPromises();
sendMockExtensionMessage({ command: "closeAutofillInlineMenu" }, sender);
await flushPromises();
expect(tabsSendMessageSpy).toHaveBeenCalledWith(
sender.tab,
{
command: "closeInlineMenu",
overlayElement: AutofillOverlayElement.List,
},
{ frameId: 0 },
);
expect(tabsSendMessageSpy).not.toHaveBeenCalledWith(
sender.tab,
{
command: "closeInlineMenu",
overlayElement: AutofillOverlayElement.Button,
},
{ frameId: 0 },
);
});
it("sends a message to close the inline menu if the form field is not focused and not filling", async () => {
sendMockExtensionMessage({ command: "closeAutofillInlineMenu" }, sender);
await flushPromises();
expect(tabsSendMessageSpy).toHaveBeenCalledWith(
sender.tab,
{
command: "closeInlineMenu",
overlayElement: undefined,
},
{ frameId: 0 },
);
});
});
describe("checkAutofillInlineMenuFocused message handler", () => {
beforeEach(async () => {
await initOverlayElementPorts();
});
it("will check if the inline menu list is focused if the list port is open", () => {
sendMockExtensionMessage({ command: "checkAutofillInlineMenuFocused" });
expect(listPortSpy.postMessage).toHaveBeenCalledWith({
command: "checkAutofillInlineMenuListFocused",
});
expect(buttonPortSpy.postMessage).not.toHaveBeenCalledWith({
command: "checkAutofillInlineMenuButtonFocused",
});
});
it("will check if the overlay button is focused if the list port is not open", () => {
overlayBackground["inlineMenuListPort"] = undefined;
sendMockExtensionMessage({ command: "checkAutofillInlineMenuFocused" });
expect(buttonPortSpy.postMessage).toHaveBeenCalledWith({
command: "checkAutofillInlineMenuButtonFocused",
});
expect(listPortSpy.postMessage).not.toHaveBeenCalledWith({
command: "checkAutofillInlineMenuListFocused",
});
});
});
describe("extension messages that trigger an update of the inline menu ciphers", () => {
const extensionMessages = [
"addedCipher",
"addEditCipherSubmitted",
"editedCipher",
"deletedCipher",
];
beforeEach(() => {
jest.spyOn(overlayBackground, "updateOverlayCiphers").mockImplementation();
});
extensionMessages.forEach((message) => {
it(`triggers an update of the overlay ciphers when the ${message} message is received`, () => {
sendMockExtensionMessage({ command: message });
expect(overlayBackground.updateOverlayCiphers).toHaveBeenCalled();
});
});
});
});
describe("inline menu button message handlers", () => {});

View File

@ -43,6 +43,7 @@ import {
PageDetailsForTab,
SubFrameOffsetData,
SubFrameOffsetsForTab,
CloseInlineMenuMessage,
} from "./abstractions/overlay.background";
export class OverlayBackground implements OverlayBackgroundInterface {
@ -91,7 +92,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
rebuildSubFrameOffsets: ({ sender }) => this.rebuildSubFrameOffsets(sender),
collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender),
unlockCompleted: ({ message }) => this.unlockCompleted(message),
addedCipher: () => this.updateOverlayCiphers(),
addEditCipherSubmitted: () => this.updateOverlayCiphers(),
editedCipher: () => this.updateOverlayCiphers(),
deletedCipher: () => this.updateOverlayCiphers(),
};
private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = {
@ -470,10 +473,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
*/
private closeInlineMenu(
sender: chrome.runtime.MessageSender,
{
forceCloseAutofillInlineMenu,
overlayElement,
}: { forceCloseAutofillInlineMenu?: boolean; overlayElement?: string } = {},
{ forceCloseAutofillInlineMenu, overlayElement }: CloseInlineMenuMessage = {},
) {
if (forceCloseAutofillInlineMenu) {
void BrowserApi.tabSendMessage(
@ -507,6 +507,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
);
}
/**
* Sends a message to the sender tab to trigger a delayed closure of the inline menu.
* This is used to ensure that we capture click events on the inline menu in the case
* that some on page programmatic method attempts to force focus redirection.
*
* @param sender - The sender of the port message
*/
private triggerDelayedInlineMenuClosure(sender: chrome.runtime.MessageSender) {
if (this.isFieldCurrentlyFocused) {
return;
@ -589,6 +596,10 @@ export class OverlayBackground implements OverlayBackgroundInterface {
});
}
/**
* Handles updating the opacity of both the inline menu button and list.
* This is used to simultaneously fade in the inline menu elements.
*/
private setInlineMenuFadeInTimeout() {
if (this.inlineMenuFadeInTimeout) {
globalThis.clearTimeout(this.inlineMenuFadeInTimeout);
@ -598,7 +609,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.inlineMenuFadeInTimeout = globalThis.setTimeout(() => {
this.inlineMenuButtonPort?.postMessage(message);
this.inlineMenuListPort?.postMessage(message);
}, 75);
}, 50);
}
/**
@ -977,6 +988,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
return this.isFieldCurrentlyFilling;
}
/**
* Sends a message to the top level frame of the sender to check if the inline menu button is visible.
*
* @param sender - The sender of the message
*/
private async checkIsAutofillInlineMenuButtonVisible(sender: chrome.runtime.MessageSender) {
return await BrowserApi.tabSendMessage(
sender.tab,
@ -985,6 +1001,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
);
}
/**
* Sends a message to the top level frame of the sender to check if the inline menu list is visible.
*
* @param sender - The sender of the message
*/
private async checkIsAutofillInlineMenuListVisible(sender: chrome.runtime.MessageSender) {
return await BrowserApi.tabSendMessage(
sender.tab,
@ -993,16 +1014,34 @@ export class OverlayBackground implements OverlayBackgroundInterface {
);
}
/**
* Responds to the content script's request to check if the inline menu ciphers are populated.
* This will return true only if the sender is the focused field's tab and the inline menu
* ciphers are populated.
*
* @param sender - The sender of the message
*/
private checkIsInlineMenuCiphersPopulated(sender: chrome.runtime.MessageSender) {
return sender.tab.id === this.focusedFieldData.tabId && this.inlineMenuCiphers.size > 0;
}
/**
* Triggers an update in the meta "color-scheme" value within the inline menu button.
* This is done to ensure that the button element has a transparent background, which
* is accomplished by setting the "color-scheme" meta value of the button iframe to
* the same value as the page's meta "color-scheme" value.
*/
private updateInlineMenuButtonColorScheme() {
this.inlineMenuButtonPort?.postMessage({
command: "updateAutofillInlineMenuColorScheme",
});
}
/**
* Triggers an update in the inline menu list's height.
*
* @param message - Contains the dimensions of the inline menu list
*/
private updateInlineMenuListHeight(message: OverlayBackgroundExtensionMessage) {
this.inlineMenuListPort?.postMessage({
command: "updateInlineMenuIframePosition",
@ -1160,6 +1199,12 @@ export class OverlayBackground implements OverlayBackgroundInterface {
handler({ message, port });
};
/**
* Ensures that the inline menu list and button port
* references are reset when they are disconnected.
*
* @param port - The port that was disconnected
*/
private handlePortOnDisconnect = (port: chrome.runtime.Port) => {
if (port.name === AutofillOverlayPort.List) {
this.inlineMenuListPort = null;