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 {
FocusedFieldData,
PageDetailsForTab,
SubFrameOffsetData,
SubFrameOffsetsForTab,
@ -66,6 +67,7 @@ describe("OverlayBackground", () => {
let subFrameOffsetsSpy: SubFrameOffsetsForTab;
let getFrameDetailsSpy: jest.SpyInstance;
let tabsSendMessageSpy: jest.SpyInstance;
let getFrameCounter: number = 2;
beforeEach(() => {
accountService = mockAccountServiceWith(mockUserId);
@ -112,13 +114,21 @@ describe("OverlayBackground", () => {
pageDetailsForTabSpy = overlayBackground["pageDetailsForTab"];
subFrameOffsetsSpy = overlayBackground["subFrameOffsetsForTab"];
getFrameDetailsSpy = jest.spyOn(BrowserApi, "getFrameDetails");
getFrameDetailsSpy.mockImplementation((_details: chrome.webNavigation.GetFrameDetails) => {
getFrameCounter--;
return mock<chrome.webNavigation.GetFrameResultDetails>({
parentFrameId: getFrameCounter,
});
});
tabsSendMessageSpy = jest.spyOn(BrowserApi, "tabSendMessage");
void overlayBackground.init();
});
afterEach(() => {
getFrameCounter = 2;
jest.clearAllMocks();
jest.useRealTimers();
mockReset(cipherService);
});
@ -141,23 +151,17 @@ describe("OverlayBackground", () => {
});
describe("building sub frame offsets", () => {
let getFrameCounter: number = 2;
beforeEach(() => {
getFrameDetailsSpy.mockImplementation((_details: chrome.webNavigation.GetFrameDetails) => {
getFrameCounter--;
return mock<chrome.webNavigation.GetFrameResultDetails>({
parentFrameId: getFrameCounter,
});
});
tabsSendMessageSpy.mockResolvedValue(mock<SubFrameOffsetData>());
tabsSendMessageSpy.mockResolvedValue(
mock<SubFrameOffsetData>({
left: getFrameCounter,
top: getFrameCounter,
url: "url",
}),
);
});
afterEach(() => {
getFrameCounter = 2;
});
it("builds the offset values for a sub frame within the tab", () => {
it("builds the offset values for a sub frame within the tab", async () => {
sendMockExtensionMessage(
{ command: "collectPageDetailsResponse", details: createAutofillPageDetailsMock() },
mock<chrome.runtime.MessageSender>({
@ -165,8 +169,11 @@ describe("OverlayBackground", () => {
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);
});
@ -202,10 +209,7 @@ describe("OverlayBackground", () => {
tabsSendMessageSpy.mockResolvedValueOnce(null);
sendMockExtensionMessage(
{ command: "collectPageDetailsResponse", details: createAutofillPageDetailsMock() },
mock<chrome.runtime.MessageSender>({
tab,
frameId,
}),
mock<chrome.runtime.MessageSender>({ tab, frameId }),
);
await flushPromises();
@ -219,6 +223,22 @@ describe("OverlayBackground", () => {
);
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();
});
});
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) {
const tabId = tab.id;
let subFrameOffsetsForTab = this.subFrameOffsetsForTab[tabId];
@ -291,7 +299,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const subFrameData = { url, top: 0, left: 0 };
let frameDetails = await BrowserApi.getFrameDetails({ tabId, frameId });
while (frameDetails && frameDetails.parentFrameId !== -1) {
while (frameDetails && frameDetails.parentFrameId > -1) {
const subFrameOffset: SubFrameOffsetData = await BrowserApi.tabSendMessage(
tab,
{
@ -324,6 +332,15 @@ export class OverlayBackground implements OverlayBackgroundInterface {
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) {
if (sender.frameId === this.focusedFieldData?.frameId) {
return;
@ -338,8 +355,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
clearTimeout(this.updateInlineMenuPositionTimeout);
}
const frameTabs = Array.from(subFrameOffsetsForTab.keys());
for (const frameId of frameTabs) {
const tabFrameIds = Array.from(subFrameOffsetsForTab.keys());
for (const frameId of tabFrameIds) {
if (frameId === sender.frameId) {
continue;
}