1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-02-13 00:51:45 +01:00

[PM-5189] Working through jest tests for OverlayBackground

This commit is contained in:
Cesar Gonzalez 2024-06-04 16:54:14 -05:00
parent b9d257046c
commit 07713511e4
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
2 changed files with 136 additions and 22 deletions

View File

@ -35,6 +35,7 @@ import { createChromeTabMock, createAutofillPageDetailsMock } from "../spec/auto
import { flushPromises, sendMockExtensionMessage } from "../spec/testing-utils"; import { flushPromises, sendMockExtensionMessage } from "../spec/testing-utils";
import { import {
FocusedFieldData,
PageDetailsForTab, PageDetailsForTab,
SubFrameOffsetData, SubFrameOffsetData,
SubFrameOffsetsForTab, SubFrameOffsetsForTab,
@ -66,6 +67,7 @@ describe("OverlayBackground", () => {
let subFrameOffsetsSpy: SubFrameOffsetsForTab; let subFrameOffsetsSpy: SubFrameOffsetsForTab;
let getFrameDetailsSpy: jest.SpyInstance; let getFrameDetailsSpy: jest.SpyInstance;
let tabsSendMessageSpy: jest.SpyInstance; let tabsSendMessageSpy: jest.SpyInstance;
let getFrameCounter: number = 2;
beforeEach(() => { beforeEach(() => {
accountService = mockAccountServiceWith(mockUserId); accountService = mockAccountServiceWith(mockUserId);
@ -112,13 +114,21 @@ describe("OverlayBackground", () => {
pageDetailsForTabSpy = overlayBackground["pageDetailsForTab"]; pageDetailsForTabSpy = overlayBackground["pageDetailsForTab"];
subFrameOffsetsSpy = overlayBackground["subFrameOffsetsForTab"]; subFrameOffsetsSpy = overlayBackground["subFrameOffsetsForTab"];
getFrameDetailsSpy = jest.spyOn(BrowserApi, "getFrameDetails"); getFrameDetailsSpy = jest.spyOn(BrowserApi, "getFrameDetails");
getFrameDetailsSpy.mockImplementation((_details: chrome.webNavigation.GetFrameDetails) => {
getFrameCounter--;
return mock<chrome.webNavigation.GetFrameResultDetails>({
parentFrameId: getFrameCounter,
});
});
tabsSendMessageSpy = jest.spyOn(BrowserApi, "tabSendMessage"); tabsSendMessageSpy = jest.spyOn(BrowserApi, "tabSendMessage");
void overlayBackground.init(); void overlayBackground.init();
}); });
afterEach(() => { afterEach(() => {
getFrameCounter = 2;
jest.clearAllMocks(); jest.clearAllMocks();
jest.useRealTimers();
mockReset(cipherService); mockReset(cipherService);
}); });
@ -141,23 +151,17 @@ describe("OverlayBackground", () => {
}); });
describe("building sub frame offsets", () => { describe("building sub frame offsets", () => {
let getFrameCounter: number = 2;
beforeEach(() => { beforeEach(() => {
getFrameDetailsSpy.mockImplementation((_details: chrome.webNavigation.GetFrameDetails) => { tabsSendMessageSpy.mockResolvedValue(
getFrameCounter--; mock<SubFrameOffsetData>({
return mock<chrome.webNavigation.GetFrameResultDetails>({ left: getFrameCounter,
parentFrameId: getFrameCounter, top: getFrameCounter,
}); url: "url",
}); }),
tabsSendMessageSpy.mockResolvedValue(mock<SubFrameOffsetData>()); );
}); });
afterEach(() => { it("builds the offset values for a sub frame within the tab", async () => {
getFrameCounter = 2;
});
it("builds the offset values for a sub frame within the tab", () => {
sendMockExtensionMessage( sendMockExtensionMessage(
{ command: "collectPageDetailsResponse", details: createAutofillPageDetailsMock() }, { command: "collectPageDetailsResponse", details: createAutofillPageDetailsMock() },
mock<chrome.runtime.MessageSender>({ mock<chrome.runtime.MessageSender>({
@ -165,8 +169,11 @@ describe("OverlayBackground", () => {
frameId: 1, frameId: 1,
}), }),
); );
await flushPromises();
expect(subFrameOffsetsSpy[tabId]).toBeDefined(); expect(subFrameOffsetsSpy[tabId]).toStrictEqual(
new Map([[1, { left: 4, top: 4, url: "url" }]]),
);
expect(pageDetailsForTabSpy[tabId].size).toBe(2); expect(pageDetailsForTabSpy[tabId].size).toBe(2);
}); });
@ -202,10 +209,7 @@ describe("OverlayBackground", () => {
tabsSendMessageSpy.mockResolvedValueOnce(null); tabsSendMessageSpy.mockResolvedValueOnce(null);
sendMockExtensionMessage( sendMockExtensionMessage(
{ command: "collectPageDetailsResponse", details: createAutofillPageDetailsMock() }, { command: "collectPageDetailsResponse", details: createAutofillPageDetailsMock() },
mock<chrome.runtime.MessageSender>({ mock<chrome.runtime.MessageSender>({ tab, frameId }),
tab,
frameId,
}),
); );
await flushPromises(); await flushPromises();
@ -219,6 +223,22 @@ describe("OverlayBackground", () => {
); );
expect(subFrameOffsetsSpy[tabId]).toStrictEqual(new Map([[frameId, null]])); expect(subFrameOffsetsSpy[tabId]).toStrictEqual(new Map([[frameId, null]]));
}); });
it("updates sub frame data that has been calculated using window messages", async () => {
const tab = createChromeTabMock({ id: tabId });
const frameId = 1;
const subFrameData = mock<SubFrameOffsetData>({ frameId, left: 10, top: 10, url: "url" });
tabsSendMessageSpy.mockResolvedValueOnce(null);
subFrameOffsetsSpy[tabId] = new Map([[frameId, null]]);
sendMockExtensionMessage(
{ command: "updateSubFrameData", subFrameData },
mock<chrome.runtime.MessageSender>({ tab, frameId }),
);
await flushPromises();
expect(subFrameOffsetsSpy[tabId]).toStrictEqual(new Map([[frameId, subFrameData]]));
});
}); });
}); });
@ -237,4 +257,81 @@ describe("OverlayBackground", () => {
expect(portKeyForTabSpy[tabId]).toBeUndefined(); expect(portKeyForTabSpy[tabId]).toBeUndefined();
}); });
}); });
describe("re-positioning the inline menu within sub frames", () => {
describe("rebuildSubFrameOffsets", () => {
const tabId = 1;
const topFrameId = 0;
const middleFrameId = 10;
const bottomFrameId = 20;
let tab: chrome.tabs.Tab;
beforeEach(() => {
tab = createChromeTabMock({ id: tabId });
overlayBackground["focusedFieldData"] = mock<FocusedFieldData>({
tabId,
frameId: bottomFrameId,
});
subFrameOffsetsSpy[tabId] = new Map([
[topFrameId, { left: 1, top: 1, url: "https://top-frame.com" }],
[middleFrameId, { left: 2, top: 2, url: "https://middle-frame.com" }],
[bottomFrameId, { left: 3, top: 3, url: "https://bottom-frame.com" }],
]);
tabsSendMessageSpy.mockResolvedValue(
mock<SubFrameOffsetData>({
left: getFrameCounter,
top: getFrameCounter,
url: "url",
}),
);
});
it("skips rebuilding sub frame offsets if the sender contains the currently focused field", () => {
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: bottomFrameId });
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender);
expect(getFrameDetailsSpy).not.toHaveBeenCalled();
});
it("skips rebuilding sub frame offsets if the tab does not contain sub frames", () => {
const sender = mock<chrome.runtime.MessageSender>({
tab: createChromeTabMock({ id: 15 }),
frameId: 0,
});
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender);
expect(getFrameDetailsSpy).not.toHaveBeenCalled();
});
it("rebuilds the sub frame offsets for a given tab", async () => {
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: middleFrameId });
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender);
await flushPromises();
expect(getFrameDetailsSpy).toHaveBeenCalledWith({ tabId, frameId: topFrameId });
expect(getFrameDetailsSpy).toHaveBeenCalledWith({ tabId, frameId: bottomFrameId });
expect(getFrameDetailsSpy).not.toHaveBeenCalledWith({ tabId, frameId: middleFrameId });
});
it("triggers an update of the inline menu position after rebuilding sub frames", async () => {
jest.useFakeTimers();
overlayBackground["updateInlineMenuPositionTimeout"] = 1;
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: middleFrameId });
jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterSubFrameRebuild");
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender);
await flushPromises();
jest.advanceTimersByTime(650);
expect(
overlayBackground["updateInlineMenuPositionAfterSubFrameRebuild"],
).toHaveBeenCalled();
});
});
describe("updateInlineMenuPositionAfterSubFrameRebuild", () => {});
});
}); });

View File

@ -276,6 +276,14 @@ export class OverlayBackground implements OverlayBackgroundInterface {
} }
} }
/**
* Builds the offset data for a sub frame of a tab. The offset data is used
* to calculate the position of the inline menu list and button.
*
* @param tab - The tab that the sub frame is associated with
* @param frameId - The frame ID of the sub frame
* @param url - The URL of the sub frame
*/
private async buildSubFrameOffsets(tab: chrome.tabs.Tab, frameId: number, url: string) { private async buildSubFrameOffsets(tab: chrome.tabs.Tab, frameId: number, url: string) {
const tabId = tab.id; const tabId = tab.id;
let subFrameOffsetsForTab = this.subFrameOffsetsForTab[tabId]; let subFrameOffsetsForTab = this.subFrameOffsetsForTab[tabId];
@ -291,7 +299,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const subFrameData = { url, top: 0, left: 0 }; const subFrameData = { url, top: 0, left: 0 };
let frameDetails = await BrowserApi.getFrameDetails({ tabId, frameId }); let frameDetails = await BrowserApi.getFrameDetails({ tabId, frameId });
while (frameDetails && frameDetails.parentFrameId !== -1) { while (frameDetails && frameDetails.parentFrameId > -1) {
const subFrameOffset: SubFrameOffsetData = await BrowserApi.tabSendMessage( const subFrameOffset: SubFrameOffsetData = await BrowserApi.tabSendMessage(
tab, tab,
{ {
@ -324,6 +332,15 @@ export class OverlayBackground implements OverlayBackgroundInterface {
subFrameOffsetsForTab.set(frameId, subFrameData); subFrameOffsetsForTab.set(frameId, subFrameData);
} }
/**
* Handles rebuilding the sub frame offsets when the tab is repositioned or scrolled.
* Will trigger a re-positioning of the inline menu list and button. Note that we
* do not trigger an update to sub frame data if the sender is the frame that has
* the field currently focused. We trigger a re-calculation of the field's position
* and as a result, the sub frame offsets of that frame will be updated.
*
* @param sender - The sender of the message
*/
private async rebuildSubFrameOffsets(sender: chrome.runtime.MessageSender) { private async rebuildSubFrameOffsets(sender: chrome.runtime.MessageSender) {
if (sender.frameId === this.focusedFieldData?.frameId) { if (sender.frameId === this.focusedFieldData?.frameId) {
return; return;
@ -338,8 +355,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
clearTimeout(this.updateInlineMenuPositionTimeout); clearTimeout(this.updateInlineMenuPositionTimeout);
} }
const frameTabs = Array.from(subFrameOffsetsForTab.keys()); const tabFrameIds = Array.from(subFrameOffsetsForTab.keys());
for (const frameId of frameTabs) { for (const frameId of tabFrameIds) {
if (frameId === sender.frameId) { if (frameId === sender.frameId) {
continue; continue;
} }