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
7fd41c37f5
commit
541d0cecf8
@ -554,7 +554,6 @@ describe("BrowserApi", () => {
|
||||
describe("clearAlarm", () => {
|
||||
it("clears the alarm with the provided name", async () => {
|
||||
const alarmName = "alarm-name";
|
||||
chrome.alarms.clear = jest.fn().mockImplementation((name, callback) => callback(true));
|
||||
|
||||
const wasCleared = await BrowserApi.clearAlarm(alarmName);
|
||||
|
||||
@ -565,8 +564,6 @@ describe("BrowserApi", () => {
|
||||
|
||||
describe("clearAllAlarms", () => {
|
||||
it("clears all alarms", async () => {
|
||||
chrome.alarms.clearAll = jest.fn().mockImplementation((callback) => callback(true));
|
||||
|
||||
const wasCleared = await BrowserApi.clearAllAlarms();
|
||||
|
||||
expect(chrome.alarms.clearAll).toHaveBeenCalledWith(expect.any(Function));
|
||||
@ -578,9 +575,6 @@ describe("BrowserApi", () => {
|
||||
it("creates an alarm", async () => {
|
||||
const alarmName = "alarm-name";
|
||||
const alarmInfo = { when: 1000 };
|
||||
chrome.alarms.create = jest
|
||||
.fn()
|
||||
.mockImplementation((_name, _createInfo, callback) => callback());
|
||||
|
||||
await BrowserApi.createAlarm(alarmName, alarmInfo);
|
||||
|
||||
|
@ -0,0 +1,148 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ScheduledTaskNames } from "@bitwarden/common/platform/enums/scheduled-task-name.enum";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { GlobalState, StateProvider } from "@bitwarden/common/platform/state";
|
||||
|
||||
import { ActiveAlarm } from "./abstractions/browser-task-scheduler.service";
|
||||
import { BrowserTaskSchedulerService } from "./browser-task-scheduler.service";
|
||||
|
||||
let activeAlarms: ActiveAlarm[] = [];
|
||||
jest.mock("rxjs", () => ({
|
||||
firstValueFrom: jest.fn(() => Promise.resolve(activeAlarms)),
|
||||
map: jest.fn(),
|
||||
Observable: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("BrowserTaskSchedulerService", () => {
|
||||
let logService: MockProxy<ConsoleLogService>;
|
||||
let stateProvider: MockProxy<StateProvider>;
|
||||
let browserTaskSchedulerService: BrowserTaskSchedulerService;
|
||||
|
||||
beforeEach(() => {
|
||||
activeAlarms = [];
|
||||
logService = mock<ConsoleLogService>();
|
||||
stateProvider = mock<StateProvider>({
|
||||
getGlobal: jest.fn(() =>
|
||||
mock<GlobalState<any>>({
|
||||
state$: mock<Observable<any>>(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
browserTaskSchedulerService = new BrowserTaskSchedulerService(logService, stateProvider);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("setTimeout", () => {
|
||||
it("uses the global setTimeout API if the delay is less than 1000ms", async () => {
|
||||
const callback = jest.fn();
|
||||
const delayInMs = 999;
|
||||
jest.spyOn(globalThis, "setTimeout");
|
||||
|
||||
await browserTaskSchedulerService.setTimeout(
|
||||
callback,
|
||||
delayInMs,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
|
||||
expect(globalThis.setTimeout).toHaveBeenCalledWith(expect.any(Function), delayInMs);
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("triggers a recovered alarm immediately and skips creating the alarm", async () => {
|
||||
activeAlarms = [mock<ActiveAlarm>({ name: ScheduledTaskNames.loginStrategySessionTimeout })];
|
||||
browserTaskSchedulerService["recoveredAlarms"].add(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
const callback = jest.fn();
|
||||
|
||||
await browserTaskSchedulerService.setTimeout(
|
||||
callback,
|
||||
60 * 1000,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("creates a timeout alarm", async () => {
|
||||
const callback = jest.fn();
|
||||
const delayInMinutes = 2;
|
||||
|
||||
await browserTaskSchedulerService.setTimeout(
|
||||
callback,
|
||||
delayInMinutes * 60 * 1000,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
|
||||
expect(chrome.alarms.create).toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
{ delayInMinutes },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setInterval", () => {
|
||||
it("uses the global setInterval API if the interval is less than 1000ms", async () => {
|
||||
const callback = jest.fn();
|
||||
const intervalInMs = 999;
|
||||
jest.spyOn(globalThis, "setInterval");
|
||||
|
||||
await browserTaskSchedulerService.setInterval(
|
||||
callback,
|
||||
intervalInMs,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
|
||||
expect(globalThis.setInterval).toHaveBeenCalledWith(expect.any(Function), intervalInMs);
|
||||
expect(chrome.alarms.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("triggers a recovered alarm before creating the interval alarm", async () => {
|
||||
const periodInMinutes = 4;
|
||||
activeAlarms = [mock<ActiveAlarm>({ name: ScheduledTaskNames.loginStrategySessionTimeout })];
|
||||
browserTaskSchedulerService["recoveredAlarms"].add(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
const callback = jest.fn();
|
||||
|
||||
await browserTaskSchedulerService.setInterval(
|
||||
callback,
|
||||
periodInMinutes * 60 * 1000,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
);
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(chrome.alarms.create).toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
{ periodInMinutes, delayInMinutes: periodInMinutes },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("creates an interval alarm", async () => {
|
||||
const callback = jest.fn();
|
||||
const periodInMinutes = 2;
|
||||
const initialDelayInMs = 1000;
|
||||
|
||||
await browserTaskSchedulerService.setInterval(
|
||||
callback,
|
||||
periodInMinutes * 60 * 1000,
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
initialDelayInMs,
|
||||
);
|
||||
|
||||
expect(chrome.alarms.create).toHaveBeenCalledWith(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
{ periodInMinutes, delayInMinutes: initialDelayInMs / 1000 / 60 },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -46,10 +46,19 @@ export class BrowserTaskSchedulerService
|
||||
this.verifyAlarmsState().catch((e) => this.logService.error(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout to execute a callback after a delay. If the delay is less
|
||||
* than 1 minute, it will use the global setTimeout. Otherwise, it will
|
||||
* create a browser extension alarm to handle the delay.
|
||||
*
|
||||
* @param callback - The function to be called after the delay.
|
||||
* @param delayInMs - The delay in milliseconds.
|
||||
* @param taskName - The name of the task, used in defining the alarm.
|
||||
*/
|
||||
async setTimeout(
|
||||
callback: () => void,
|
||||
delayInMs: number,
|
||||
taskName?: ScheduledTaskName,
|
||||
taskName: ScheduledTaskName,
|
||||
): Promise<number | NodeJS.Timeout> {
|
||||
const delayInMinutes = delayInMs / 1000 / 60;
|
||||
if (delayInMinutes < 1) {
|
||||
@ -65,10 +74,20 @@ export class BrowserTaskSchedulerService
|
||||
await this.createAlarm(taskName, { delayInMinutes });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval to execute a callback at each interval. If the interval is
|
||||
* 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
|
||||
*/
|
||||
async setInterval(
|
||||
callback: () => void,
|
||||
intervalInMs: number,
|
||||
taskName?: ScheduledTaskName,
|
||||
taskName: ScheduledTaskName,
|
||||
initialDelayInMs?: number,
|
||||
): Promise<number | NodeJS.Timeout> {
|
||||
const intervalInMinutes = intervalInMs / 1000 / 60;
|
||||
@ -169,8 +188,8 @@ export class BrowserTaskSchedulerService
|
||||
private async deleteActiveAlarm(name: ScheduledTaskName): Promise<void> {
|
||||
delete this.onAlarmHandlers[name];
|
||||
const activeAlarms = await firstValueFrom(this.activeAlarms$);
|
||||
const filteredAlarms = activeAlarms.filter((alarm) => alarm.name !== name);
|
||||
await this.updateActiveAlarms(filteredAlarms);
|
||||
const filteredAlarms = activeAlarms?.filter((alarm) => alarm.name !== name);
|
||||
await this.updateActiveAlarms(filteredAlarms || []);
|
||||
}
|
||||
|
||||
private async updateActiveAlarms(alarms: ActiveAlarm[]): Promise<void> {
|
||||
|
@ -125,11 +125,11 @@ const offscreen = {
|
||||
};
|
||||
|
||||
const alarms = {
|
||||
clear: jest.fn(),
|
||||
clearAll: jest.fn(),
|
||||
create: jest.fn(),
|
||||
get: jest.fn(),
|
||||
getAll: jest.fn(),
|
||||
clear: jest.fn().mockImplementation((_name, callback) => callback(true)),
|
||||
clearAll: jest.fn().mockImplementation((callback) => callback(true)),
|
||||
create: jest.fn().mockImplementation((_name, _createInfo, callback) => callback()),
|
||||
get: jest.fn().mockImplementation((_name, callback) => callback(null)),
|
||||
getAll: jest.fn().mockImplementation((callback) => callback([])),
|
||||
onAlarm: {
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
|
@ -17,7 +17,7 @@ export class TaskSchedulerService implements TaskSchedulerServiceInterface {
|
||||
delayInMs: number,
|
||||
_taskName?: ScheduledTaskName,
|
||||
): Promise<number | NodeJS.Timeout> {
|
||||
return setTimeout(() => callback(), delayInMs);
|
||||
return globalThis.setTimeout(() => callback(), delayInMs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,7 +34,7 @@ export class TaskSchedulerService implements TaskSchedulerServiceInterface {
|
||||
_taskName?: ScheduledTaskName,
|
||||
_initialDelayInMs?: number,
|
||||
): Promise<number | NodeJS.Timeout> {
|
||||
return setInterval(() => callback(), intervalInMs);
|
||||
return globalThis.setInterval(() => callback(), intervalInMs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,11 +44,11 @@ export class TaskSchedulerService implements TaskSchedulerServiceInterface {
|
||||
*/
|
||||
async clearScheduledTask(taskIdentifier: TaskIdentifier): Promise<void> {
|
||||
if (taskIdentifier.timeoutId) {
|
||||
clearTimeout(taskIdentifier.timeoutId);
|
||||
globalThis.clearTimeout(taskIdentifier.timeoutId);
|
||||
}
|
||||
|
||||
if (taskIdentifier.intervalId) {
|
||||
clearInterval(taskIdentifier.intervalId);
|
||||
globalThis.clearInterval(taskIdentifier.intervalId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user