1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-06 09:20:43 +01:00

[PM-6426] Working through jest tests

This commit is contained in:
Cesar Gonzalez 2024-05-01 13:00:31 -05:00
parent e82a52961b
commit 8117068379
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
2 changed files with 120 additions and 21 deletions

View File

@ -6,6 +6,8 @@ import { ConsoleLogService } from "@bitwarden/common/platform/services/console-l
import { GlobalState, StateProvider } from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/common/types/guid";
import { flushPromises } from "../../autofill/spec/testing-utils";
import {
ActiveAlarm,
BrowserTaskSchedulerService,
@ -20,10 +22,29 @@ jest.mock("rxjs", () => {
};
});
function setupGlobalBrowserMock(overrides: Partial<chrome.alarms.Alarm> = {}) {
globalThis.browser.alarms = {
create: jest.fn(),
clear: jest.fn(),
get: jest.fn(),
getAll: jest.fn(),
clearAll: jest.fn(),
onAlarm: {
addListener: jest.fn(),
removeListener: jest.fn(),
hasListener: jest.fn(),
},
...overrides,
};
}
const userUuid = "user-uuid" as UserId;
function getAlarmNameMock(taskName: string) {
return `${userUuid}__${taskName}`;
}
describe("BrowserTaskSchedulerService", () => {
const callback = jest.fn();
const delayInMinutes = 2;
const userUuid = "user-uuid" as UserId;
let activeUserIdMock$: BehaviorSubject<UserId>;
let activeAlarmsMock$: BehaviorSubject<ActiveAlarm[]>;
let logService: MockProxy<ConsoleLogService>;
@ -71,12 +92,17 @@ describe("BrowserTaskSchedulerService", () => {
ScheduledTaskNames.loginStrategySessionTimeout,
callback,
);
// @ts-expect-error mocking global browser object
// eslint-disable-next-line no-global-assign
globalThis.browser = {};
});
afterEach(() => {
jest.clearAllMocks();
jest.clearAllTimers();
jest.useRealTimers();
// eslint-disable-next-line no-global-assign
globalThis.browser = undefined;
});
describe("setTimeout", () => {
@ -87,7 +113,7 @@ describe("BrowserTaskSchedulerService", () => {
);
expect(chrome.alarms.create).toHaveBeenCalledWith(
`${userUuid}__${ScheduledTaskNames.loginStrategySessionTimeout}`,
getAlarmNameMock(ScheduledTaskNames.loginStrategySessionTimeout),
{ delayInMinutes },
expect.any(Function),
);
@ -126,8 +152,26 @@ describe("BrowserTaskSchedulerService", () => {
);
});
it("uses the global setTimeout API if the delay is less than 1000ms", async () => {
const delayInMs = 15000;
it("creates an alarm that is not associated with a user", async () => {
activeUserIdMock$.next(undefined);
chrome.alarms.get = jest.fn().mockImplementation((_name, callback) => callback(undefined));
await browserTaskSchedulerService.setTimeout(
ScheduledTaskNames.loginStrategySessionTimeout,
delayInMinutes * 60 * 1000,
);
expect(chrome.alarms.create).toHaveBeenCalledWith(
ScheduledTaskNames.loginStrategySessionTimeout,
{ delayInMinutes },
expect.any(Function),
);
});
describe("when the task is scheduled to be triggered in less than 1 minute", () => {
const delayInMs = 45000;
it("sets a timeout using the global setTimeout API", async () => {
jest.spyOn(globalThis, "setTimeout");
await browserTaskSchedulerService.setTimeout(
@ -136,11 +180,74 @@ describe("BrowserTaskSchedulerService", () => {
);
expect(globalThis.setTimeout).toHaveBeenCalledWith(expect.any(Function), delayInMs);
});
it("sets a fallback alarm", async () => {
const delayInMs = 15000;
await browserTaskSchedulerService.setTimeout(
ScheduledTaskNames.loginStrategySessionTimeout,
delayInMs,
);
expect(chrome.alarms.create).toHaveBeenCalledWith(
`${userUuid}__${ScheduledTaskNames.loginStrategySessionTimeout}`,
getAlarmNameMock(ScheduledTaskNames.loginStrategySessionTimeout),
{ delayInMinutes: 0.5 },
expect.any(Function),
);
});
it("sets the fallback for a minimum of 1 minute if the environment not for Chrome", async () => {
setupGlobalBrowserMock();
await browserTaskSchedulerService.setTimeout(
ScheduledTaskNames.loginStrategySessionTimeout,
delayInMs,
);
expect(browser.alarms.create).toHaveBeenCalledWith(
getAlarmNameMock(ScheduledTaskNames.loginStrategySessionTimeout),
{ delayInMinutes: 1 },
);
});
it("clears the fallback alarm when the setTimeout is triggered", async () => {
jest.useFakeTimers();
await browserTaskSchedulerService.setTimeout(
ScheduledTaskNames.loginStrategySessionTimeout,
delayInMs,
);
jest.advanceTimersByTime(delayInMs);
await flushPromises();
expect(chrome.alarms.clear).toHaveBeenCalledWith(
getAlarmNameMock(ScheduledTaskNames.loginStrategySessionTimeout),
expect.any(Function),
);
});
});
});
describe("triggering a task", () => {
it("clears an non user-based alarm if a separate user-based alarm has been set up", async () => {
jest.useFakeTimers();
activeUserIdMock$.next(undefined);
const delayInMs = 10000;
chrome.alarms.get = jest
.fn()
.mockImplementation((_name, callback) => callback(mock<chrome.alarms.Alarm>()));
await browserTaskSchedulerService.setTimeout(
ScheduledTaskNames.loginStrategySessionTimeout,
delayInMs,
);
jest.advanceTimersByTime(delayInMs);
await flushPromises();
expect(chrome.alarms.clear).toHaveBeenCalledWith(
ScheduledTaskNames.loginStrategySessionTimeout,
expect.any(Function),
);
});
});
});

View File

@ -152,7 +152,7 @@ export class BrowserTaskSchedulerServiceImplementation
createInfo.delayInMinutes &&
startTime + createInfo.delayInMinutes * 60 * 1000 < currentTime;
if (shouldAlarmHaveBeenTriggered || hasSetTimeoutAlarmExceededDelay) {
await this.triggerRecoveredAlarm(alarmName);
await this.triggerTask(alarmName);
continue;
}
@ -247,16 +247,6 @@ export class BrowserTaskSchedulerServiceImplementation
await this.activeAlarmsState.update(() => alarms);
}
/**
* Triggers a recovered alarm by deleting it from the recovered alarms set
*
* @param alarmName - The name of the recovered alarm to trigger.
* @param periodInMinutes - The period in minutes of the recovered alarm.
*/
private async triggerRecoveredAlarm(alarmName: string, periodInMinutes?: number): Promise<void> {
await this.triggerTask(alarmName, periodInMinutes);
}
/**
* Sets up the on alarm listener to handle alarms.
*/
@ -398,7 +388,9 @@ export class BrowserTaskSchedulerServiceImplementation
/**
* Checks if the environment is a non-Chrome environment. This is used to determine
* if the browser alarms API should be used in place of the chrome alarms API.
* if the browser alarms API should be used in place of the chrome alarms API. This
* is necessary because the `chrome` polyfill that Mozilla implements does not allow
* passing the callback parameter in the same way most `chrome.alarm` api calls allow.
*/
private isNonChromeEnvironment(): boolean {
return typeof browser !== "undefined" && !!browser.alarms;