mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-24 02:41:54 +01:00
[PM-6426] Implementing jest tests for the BrowserTaskSchedulerService
This commit is contained in:
parent
541d0cecf8
commit
5ce4bff8c6
@ -1137,6 +1137,7 @@ export default class MainBackground {
|
||||
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
|
||||
await this.eventUploadService.uploadEvents(userId as UserId);
|
||||
await this.taskSchedulerService.clearAllScheduledTasks();
|
||||
|
||||
await Promise.all([
|
||||
this.syncService.setLastSync(new Date(0), userId),
|
||||
|
@ -5,6 +5,8 @@ import { ScheduledTaskNames } from "@bitwarden/common/platform/enums/scheduled-t
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { GlobalState, StateProvider } from "@bitwarden/common/platform/state";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
|
||||
import { ActiveAlarm } from "./abstractions/browser-task-scheduler.service";
|
||||
import { BrowserTaskSchedulerService } from "./browser-task-scheduler.service";
|
||||
|
||||
@ -19,9 +21,31 @@ describe("BrowserTaskSchedulerService", () => {
|
||||
let logService: MockProxy<ConsoleLogService>;
|
||||
let stateProvider: MockProxy<StateProvider>;
|
||||
let browserTaskSchedulerService: BrowserTaskSchedulerService;
|
||||
const eventUploadsIntervalCreateInfo = { periodInMinutes: 5, delayInMinutes: 5 };
|
||||
const scheduleNextSyncIntervalCreateInfo = { periodInMinutes: 5, delayInMinutes: 5 };
|
||||
|
||||
beforeEach(() => {
|
||||
activeAlarms = [];
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(BrowserApi, "getAlarm").mockImplementation((alarmName) => {
|
||||
if (alarmName === ScheduledTaskNames.scheduleNextSyncInterval) {
|
||||
return Promise.resolve(mock<chrome.alarms.Alarm>({ name: alarmName }));
|
||||
}
|
||||
});
|
||||
activeAlarms = [
|
||||
mock<ActiveAlarm>({
|
||||
name: ScheduledTaskNames.eventUploadsInterval,
|
||||
createInfo: eventUploadsIntervalCreateInfo,
|
||||
}),
|
||||
mock<ActiveAlarm>({
|
||||
name: ScheduledTaskNames.scheduleNextSyncInterval,
|
||||
createInfo: scheduleNextSyncIntervalCreateInfo,
|
||||
}),
|
||||
mock<ActiveAlarm>({
|
||||
name: ScheduledTaskNames.fido2ClientAbortTimeout,
|
||||
startTime: Date.now() - 60001,
|
||||
createInfo: { delayInMinutes: 1, periodInMinutes: undefined },
|
||||
}),
|
||||
];
|
||||
logService = mock<ConsoleLogService>();
|
||||
stateProvider = mock<StateProvider>({
|
||||
getGlobal: jest.fn(() =>
|
||||
@ -35,6 +59,44 @@ describe("BrowserTaskSchedulerService", () => {
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe("verifyAlarmsState", () => {
|
||||
it("verifies the status of potentially existing alarms referenced from state on initialization", () => {
|
||||
expect(chrome.alarms.create).toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.eventUploadsInterval,
|
||||
eventUploadsIntervalCreateInfo,
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("skips creating an alarm if the alarm already exists", () => {
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.scheduleNextSyncInterval,
|
||||
scheduleNextSyncIntervalCreateInfo,
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("adds the alarm name to the set of recovered alarms if the alarm create info indicates it has expired", () => {
|
||||
expect(
|
||||
browserTaskSchedulerService["recoveredAlarms"].has(
|
||||
ScheduledTaskNames.fido2ClientAbortTimeout,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("clears the list of recovered alarms after 10 seconds", () => {
|
||||
jest.advanceTimersByTime(10 * 1000);
|
||||
|
||||
expect(
|
||||
browserTaskSchedulerService["recoveredAlarms"].has(
|
||||
ScheduledTaskNames.fido2ClientAbortTimeout,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setTimeout", () => {
|
||||
@ -50,7 +112,11 @@ describe("BrowserTaskSchedulerService", () => {
|
||||
);
|
||||
|
||||
expect(globalThis.setTimeout).toHaveBeenCalledWith(expect.any(Function), delayInMs);
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalled();
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
{ delayInMinutes: 1 },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("triggers a recovered alarm immediately and skips creating the alarm", async () => {
|
||||
@ -67,7 +133,11 @@ describe("BrowserTaskSchedulerService", () => {
|
||||
);
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalled();
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
{ delayInMinutes: 1 },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("creates a timeout alarm", async () => {
|
||||
@ -86,6 +156,37 @@ describe("BrowserTaskSchedulerService", () => {
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("skips creating a duplicate timeout alarm", async () => {
|
||||
const callback = jest.fn();
|
||||
const delayInMinutes = 2;
|
||||
jest.spyOn(BrowserApi, "getAlarm").mockResolvedValue(
|
||||
mock<chrome.alarms.Alarm>({
|
||||
name: ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
}),
|
||||
);
|
||||
jest.spyOn(BrowserApi, "createAlarm");
|
||||
|
||||
await browserTaskSchedulerService.setTimeout(
|
||||
callback,
|
||||
delayInMinutes * 60 * 1000,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
|
||||
expect(BrowserApi.createAlarm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("logs a warning if a duplicate handler is registered when creating an alarm", () => {
|
||||
const callback = jest.fn();
|
||||
const name = ScheduledTaskNames.loginStrategySessionTimeout;
|
||||
browserTaskSchedulerService["onAlarmHandlers"][name] = jest.fn();
|
||||
|
||||
browserTaskSchedulerService["registerAlarmHandler"](name, callback);
|
||||
|
||||
expect(logService.warning).toHaveBeenCalledWith(
|
||||
`Alarm handler for ${name} already exists. Overwriting.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setInterval", () => {
|
||||
@ -101,7 +202,11 @@ describe("BrowserTaskSchedulerService", () => {
|
||||
);
|
||||
|
||||
expect(globalThis.setInterval).toHaveBeenCalledWith(expect.any(Function), intervalInMs);
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalled();
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
{ periodInMinutes: 1, delayInMinutes: 1 },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("triggers a recovered alarm before creating the interval alarm", async () => {
|
||||
|
@ -79,10 +79,10 @@ export class BrowserTaskSchedulerService
|
||||
* less than 1 minute, it will use the global setInterval. Otherwise, it will
|
||||
* create a browser extension alarm to handle the interval.
|
||||
*
|
||||
* @param callback
|
||||
* @param intervalInMs
|
||||
* @param taskName
|
||||
* @param initialDelayInMs
|
||||
* @param callback - The function to be called at each interval.
|
||||
* @param intervalInMs - The interval in milliseconds.
|
||||
* @param taskName - The name of the task, used in defining the alarm.
|
||||
* @param initialDelayInMs - The initial delay in milliseconds.
|
||||
*/
|
||||
async setInterval(
|
||||
callback: () => void,
|
||||
@ -107,6 +107,13 @@ export class BrowserTaskSchedulerService
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a scheduled task by its task identifier. If the task identifier
|
||||
* contains a task name, it will clear the browser extension alarm with that
|
||||
* name.
|
||||
*
|
||||
* @param taskIdentifier - The task identifier containing the task name.
|
||||
*/
|
||||
async clearScheduledTask(taskIdentifier: TaskIdentifier): Promise<void> {
|
||||
void super.clearScheduledTask(taskIdentifier);
|
||||
|
||||
@ -176,7 +183,7 @@ export class BrowserTaskSchedulerService
|
||||
}
|
||||
|
||||
// 10 seconds after verifying the alarm state, we should treat any newly created alarms as non-recovered alarms.
|
||||
setTimeout(() => this.recoveredAlarms.clear(), 10 * 1000);
|
||||
globalThis.setTimeout(() => this.recoveredAlarms.clear(), 10 * 1000);
|
||||
}
|
||||
|
||||
private async setActiveAlarm(alarm: ActiveAlarm): Promise<void> {
|
||||
|
Loading…
Reference in New Issue
Block a user