mirror of
https://github.com/bitwarden/browser
synced 2026-02-25 17:13:24 +00:00
[PM-2348] Close popup window before process reload (#16795)
* Close popup window before process reload * unit test coverage
This commit is contained in:
@@ -337,6 +337,68 @@ describe("BrowserPopupUtils", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("waitForAllPopupsClose", () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("should resolve immediately if no popups are open", async () => {
|
||||
jest.spyOn(BrowserApi, "isPopupOpen").mockResolvedValue(false);
|
||||
|
||||
const promise = BrowserPopupUtils.waitForAllPopupsClose();
|
||||
jest.advanceTimersByTime(100);
|
||||
|
||||
await expect(promise).resolves.toBeUndefined();
|
||||
expect(BrowserApi.isPopupOpen).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should resolve after timeout if popup never closes when using custom timeout", async () => {
|
||||
jest.spyOn(BrowserApi, "isPopupOpen").mockResolvedValue(true);
|
||||
|
||||
const promise = BrowserPopupUtils.waitForAllPopupsClose(500);
|
||||
|
||||
// Advance past the timeout
|
||||
jest.advanceTimersByTime(600);
|
||||
|
||||
await expect(promise).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should resolve after timeout if popup never closes when using default timeout", async () => {
|
||||
jest.spyOn(BrowserApi, "isPopupOpen").mockResolvedValue(true);
|
||||
|
||||
const promise = BrowserPopupUtils.waitForAllPopupsClose();
|
||||
|
||||
// Advance past the default timeout
|
||||
jest.advanceTimersByTime(1100);
|
||||
|
||||
await expect(promise).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should stop polling after popup closes before timeout", async () => {
|
||||
let callCount = 0;
|
||||
jest.spyOn(BrowserApi, "isPopupOpen").mockImplementation(async () => {
|
||||
callCount++;
|
||||
return callCount <= 2;
|
||||
});
|
||||
|
||||
const promise = BrowserPopupUtils.waitForAllPopupsClose(1000);
|
||||
|
||||
// Advance to when popup closes (300ms)
|
||||
jest.advanceTimersByTime(300);
|
||||
|
||||
await expect(promise).resolves.toBeUndefined();
|
||||
|
||||
// Advance further to ensure no more calls are made
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
expect(BrowserApi.isPopupOpen).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isSingleActionPopoutOpen", () => {
|
||||
const windowOptions = {
|
||||
id: 1,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { filter, firstValueFrom, interval, of, switchMap, takeWhile, timeout } from "rxjs";
|
||||
|
||||
import { ScrollOptions } from "./abstractions/browser-popup-utils.abstractions";
|
||||
import { BrowserApi } from "./browser-api";
|
||||
@@ -212,6 +213,27 @@ export default class BrowserPopupUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for all browser action popups to close, polling up to the specified timeout.
|
||||
* Used before extension reload to prevent zombie popups with invalidated contexts.
|
||||
*
|
||||
* @param timeoutMs - Maximum time to wait in milliseconds. Defaults to 1 second.
|
||||
* @returns Promise that resolves when all popups are closed or timeout is reached.
|
||||
*/
|
||||
static async waitForAllPopupsClose(timeoutMs = 1000): Promise<void> {
|
||||
await firstValueFrom(
|
||||
interval(100).pipe(
|
||||
switchMap(() => BrowserApi.isPopupOpen()),
|
||||
takeWhile((isOpen) => isOpen, true),
|
||||
filter((isOpen) => !isOpen),
|
||||
timeout({
|
||||
first: timeoutMs,
|
||||
with: () => of(true),
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if a single action window is open based on the passed popoutKey.
|
||||
* Will focus the existing window, and close any other windows that might exist
|
||||
|
||||
Reference in New Issue
Block a user