1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-30 16:23:53 +00:00

[WIP] updates tests.

This commit is contained in:
Miles Blackwood
2025-12-18 12:32:53 -05:00
parent 79143cc563
commit cec52c19e2
2 changed files with 247 additions and 11 deletions

View File

@@ -2240,6 +2240,121 @@ describe("CollectAutofillContentService", () => {
expect(collectAutofillContentService["setupOverlayListenersOnMutatedElements"]).toBeCalled();
});
it("triggers debounced page details update when mutations occur in shadow roots", () => {
jest.useFakeTimers();
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: document.querySelectorAll("div"),
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: null,
target: document.body,
};
collectAutofillContentService["currentLocationHref"] = window.location.href;
jest.spyOn(domQueryService, "checkMutationsInShadowRoots").mockReturnValue(true);
jest.spyOn(collectAutofillContentService as any, "debouncedRequirePageDetailsUpdate");
collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]);
expect(domQueryService.checkMutationsInShadowRoots).toHaveBeenCalledWith([mutationRecord]);
expect(collectAutofillContentService["debouncedRequirePageDetailsUpdate"]).toHaveBeenCalled();
jest.useRealTimers();
});
it("does not trigger debounced update when mutations are not in shadow roots", () => {
jest.useFakeTimers();
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: document.querySelectorAll("div"),
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: null,
target: document.body,
};
collectAutofillContentService["currentLocationHref"] = window.location.href;
jest.spyOn(domQueryService, "checkMutationsInShadowRoots").mockReturnValue(false);
jest.spyOn(collectAutofillContentService as any, "debouncedRequirePageDetailsUpdate");
collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]);
expect(domQueryService.checkMutationsInShadowRoots).toHaveBeenCalledWith([mutationRecord]);
expect(
collectAutofillContentService["debouncedRequirePageDetailsUpdate"],
).not.toHaveBeenCalled();
jest.useRealTimers();
});
it("schedules a debounced check for new shadow roots", () => {
jest.useFakeTimers();
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: document.querySelectorAll("div"),
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: null,
target: document.body,
};
collectAutofillContentService["currentLocationHref"] = window.location.href;
collectAutofillContentService["pendingShadowDomCheck"] = false;
jest.spyOn(domQueryService, "checkMutationsInShadowRoots").mockReturnValue(false);
jest.spyOn(collectAutofillContentService as any, "checkForNewShadowRoots");
collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]);
expect(collectAutofillContentService["pendingShadowDomCheck"]).toBe(true);
expect(collectAutofillContentService["shadowDomCheckTimeout"]).not.toBeNull();
// Fast-forward time to trigger the debounced check
jest.advanceTimersByTime(500);
expect(collectAutofillContentService["checkForNewShadowRoots"]).toHaveBeenCalled();
expect(collectAutofillContentService["pendingShadowDomCheck"]).toBe(false);
jest.useRealTimers();
});
it("does not schedule duplicate shadow root checks when already pending", () => {
jest.useFakeTimers();
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: document.querySelectorAll("div"),
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: null,
target: document.body,
};
collectAutofillContentService["currentLocationHref"] = window.location.href;
collectAutofillContentService["pendingShadowDomCheck"] = true;
const initialTimeout = setTimeout(() => {}, 500);
collectAutofillContentService["shadowDomCheckTimeout"] = initialTimeout;
collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]);
// Should not change the timeout since check is already pending
expect(collectAutofillContentService["shadowDomCheckTimeout"]).toBe(initialTimeout);
clearTimeout(initialTimeout);
jest.useRealTimers();
});
});
describe("setupOverlayListenersOnMutatedElements", () => {
@@ -2660,22 +2775,25 @@ describe("CollectAutofillContentService", () => {
jest.useRealTimers();
});
it("will require an update to page details if shadow DOM is present", () => {
jest
.spyOn(domQueryService as any, "checkPageContainsShadowDom")
.mockImplementationOnce(() => true);
it("processes queued mutations and clears the queue", () => {
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: document.querySelectorAll("div"),
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: document.querySelectorAll("li"),
target: document.body,
};
collectAutofillContentService["requirePageDetailsUpdate"] = jest.fn();
collectAutofillContentService["mutationsQueue"] = [[], []];
collectAutofillContentService["mutationsQueue"] = [[mutationRecord], [mutationRecord]];
jest.spyOn(collectAutofillContentService as any, "processMutationRecords");
collectAutofillContentService["processMutations"]();
jest.runOnlyPendingTimers();
expect(domQueryService.checkPageContainsShadowDom).toHaveBeenCalled();
expect(collectAutofillContentService["mutationsQueue"]).toHaveLength(0);
expect(collectAutofillContentService["requirePageDetailsUpdate"]).toHaveBeenCalled();
});
});
});

View File

@@ -147,4 +147,122 @@ describe("DomQueryService", () => {
expect(formFieldElements).toStrictEqual([input]);
});
});
describe("checkMutationsInShadowRoots", () => {
it("returns true when a mutation occurred within a shadow root", () => {
const customElement = document.createElement("custom-element");
const shadowRoot = customElement.attachShadow({ mode: "open" });
const input = document.createElement("input");
shadowRoot.appendChild(input);
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: NodeList.prototype,
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: NodeList.prototype,
target: input,
};
const result = domQueryService.checkMutationsInShadowRoots([mutationRecord]);
expect(result).toBe(true);
});
it("returns false when mutations occurred in the light DOM", () => {
const div = document.createElement("div");
document.body.appendChild(div);
const mutationRecord: MutationRecord = {
type: "childList",
addedNodes: NodeList.prototype,
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: NodeList.prototype,
target: div,
};
const result = domQueryService.checkMutationsInShadowRoots([mutationRecord]);
expect(result).toBe(false);
});
it("returns true if any mutation in the array is in a shadow root", () => {
const customElement = document.createElement("custom-element");
const shadowRoot = customElement.attachShadow({ mode: "open" });
const shadowInput = document.createElement("input");
shadowRoot.appendChild(shadowInput);
const lightDiv = document.createElement("div");
document.body.appendChild(lightDiv);
const shadowMutation: MutationRecord = {
type: "childList",
addedNodes: NodeList.prototype,
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: NodeList.prototype,
target: shadowInput,
};
const lightMutation: MutationRecord = {
type: "childList",
addedNodes: NodeList.prototype,
attributeName: null,
attributeNamespace: null,
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: NodeList.prototype,
target: lightDiv,
};
const result = domQueryService.checkMutationsInShadowRoots([lightMutation, shadowMutation]);
expect(result).toBe(true);
});
});
describe("checkForNewShadowRoots", () => {
it("returns true when a shadow root is not in the observed set", () => {
const customElement = document.createElement("custom-element");
customElement.attachShadow({ mode: "open" });
document.body.appendChild(customElement);
const result = domQueryService.checkForNewShadowRoots();
expect(result).toBe(true);
});
it("returns false when all shadow roots are already observed", () => {
const customElement = document.createElement("custom-element");
const shadowRoot = customElement.attachShadow({ mode: "open" });
document.body.appendChild(customElement);
// Simulate the shadow root being observed by adding it to the tracked set
domQueryService["observedShadowRoots"].add(shadowRoot);
const result = domQueryService.checkForNewShadowRoots();
expect(result).toBe(false);
});
it("returns false when there are no shadow roots on the page", () => {
const div = document.createElement("div");
document.body.appendChild(div);
const result = domQueryService.checkForNewShadowRoots();
expect(result).toBe(false);
});
});
});