1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-14 02:08:50 +02:00

[PM-6426] Finalizing jest tests for BrowserTaskScheduler class

This commit is contained in:
Cesar Gonzalez 2024-04-02 09:36:17 -05:00
parent b9055cdc15
commit 2ee6d48585
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
2 changed files with 93 additions and 6 deletions

View File

@ -1,6 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { TaskIdentifier } from "@bitwarden/common/platform/abstractions/task-scheduler.service";
import { ScheduledTaskNames } from "@bitwarden/common/platform/enums/scheduled-task-name.enum"; import { ScheduledTaskNames } from "@bitwarden/common/platform/enums/scheduled-task-name.enum";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { GlobalState, StateProvider } from "@bitwarden/common/platform/state"; import { GlobalState, StateProvider } from "@bitwarden/common/platform/state";
@ -251,4 +252,70 @@ describe("BrowserTaskSchedulerService", () => {
); );
}); });
}); });
describe("clearScheduledTask", () => {
afterEach(() => {
chrome.alarms.clear = jest.fn().mockImplementation((_name, callback) => callback(true));
});
it("skips clearing the alarm if the alarm name is not provided", async () => {
await browserTaskSchedulerService.clearScheduledTask({
timeoutId: 1,
intervalId: 2,
});
expect(chrome.alarms.clear).not.toHaveBeenCalled();
});
it("skips deleting the active alarm if the alarm was not cleared", async () => {
const taskIdentifier: TaskIdentifier = { taskName: ScheduledTaskNames.eventUploadsInterval };
chrome.alarms.clear = jest.fn().mockImplementation((_name, callback) => callback(false));
jest.spyOn(browserTaskSchedulerService as any, "deleteActiveAlarm");
await browserTaskSchedulerService.clearScheduledTask(taskIdentifier);
expect(browserTaskSchedulerService["deleteActiveAlarm"]).not.toHaveBeenCalled();
});
it("clears a named alarm", async () => {
const taskIdentifier: TaskIdentifier = { taskName: ScheduledTaskNames.eventUploadsInterval };
jest.spyOn(browserTaskSchedulerService as any, "deleteActiveAlarm");
await browserTaskSchedulerService.clearScheduledTask(taskIdentifier);
expect(chrome.alarms.clear).toHaveBeenCalledWith(
ScheduledTaskNames.eventUploadsInterval,
expect.any(Function),
);
expect(browserTaskSchedulerService["deleteActiveAlarm"]).toHaveBeenCalledWith(
ScheduledTaskNames.eventUploadsInterval,
);
});
});
describe("clearAllScheduledTasks", () => {
it("clears all scheduled tasks and extension alarms", async () => {
jest.spyOn(BrowserApi, "clearAllAlarms");
jest.spyOn(browserTaskSchedulerService as any, "updateActiveAlarms");
await browserTaskSchedulerService.clearAllScheduledTasks();
expect(BrowserApi.clearAllAlarms).toHaveBeenCalled();
expect(browserTaskSchedulerService["updateActiveAlarms"]).toHaveBeenCalledWith([]);
expect(browserTaskSchedulerService["onAlarmHandlers"]).toEqual({});
expect(browserTaskSchedulerService["recoveredAlarms"].size).toBe(0);
});
});
describe("handleOnAlarm", () => {
it("triggers the alarm", async () => {
const alarm = mock<chrome.alarms.Alarm>({ name: ScheduledTaskNames.eventUploadsInterval });
const callback = jest.fn();
browserTaskSchedulerService["onAlarmHandlers"][alarm.name] = callback;
await browserTaskSchedulerService["handleOnAlarm"](alarm);
expect(callback).toHaveBeenCalled();
});
});
}); });

View File

@ -129,6 +129,10 @@ export class BrowserTaskSchedulerService
} }
} }
/**
* Clears all scheduled tasks by clearing all browser extension
* alarms and resetting the active alarms state.
*/
async clearAllScheduledTasks(): Promise<void> { async clearAllScheduledTasks(): Promise<void> {
await BrowserApi.clearAllAlarms(); await BrowserApi.clearAllAlarms();
await this.updateActiveAlarms([]); await this.updateActiveAlarms([]);
@ -136,6 +140,12 @@ export class BrowserTaskSchedulerService
this.recoveredAlarms.clear(); this.recoveredAlarms.clear();
} }
/**
* Creates a browser extension alarm with the given name and create info.
*
* @param name - The name of the alarm.
* @param createInfo - The alarm create info.
*/
private async createAlarm( private async createAlarm(
name: ScheduledTaskName, name: ScheduledTaskName,
createInfo: chrome.alarms.AlarmCreateInfo, createInfo: chrome.alarms.AlarmCreateInfo,
@ -150,6 +160,12 @@ export class BrowserTaskSchedulerService
await this.setActiveAlarm({ name, startTime: Date.now(), createInfo }); await this.setActiveAlarm({ name, startTime: Date.now(), createInfo });
} }
/**
* Registers an alarm handler for the given name.
*
* @param name - The name of the alarm.
* @param handler - The alarm handler.
*/
private registerAlarmHandler(name: ScheduledTaskName, handler: CallableFunction): void { private registerAlarmHandler(name: ScheduledTaskName, handler: CallableFunction): void {
if (this.onAlarmHandlers[name]) { if (this.onAlarmHandlers[name]) {
this.logService.warning(`Alarm handler for ${name} already exists. Overwriting.`); this.logService.warning(`Alarm handler for ${name} already exists. Overwriting.`);
@ -158,6 +174,10 @@ export class BrowserTaskSchedulerService
this.onAlarmHandlers[name] = () => handler(); this.onAlarmHandlers[name] = () => handler();
} }
/**
* Verifies the state of the active alarms by checking if
* any alarms have been missed or need to be created.
*/
private async verifyAlarmsState(): Promise<void> { private async verifyAlarmsState(): Promise<void> {
const currentTime = Date.now(); const currentTime = Date.now();
const activeAlarms = await firstValueFrom(this.activeAlarms$); const activeAlarms = await firstValueFrom(this.activeAlarms$);
@ -169,12 +189,12 @@ export class BrowserTaskSchedulerService
continue; continue;
} }
if ( const shouldAlarmHaveBeenTriggered = createInfo.when && createInfo.when < currentTime;
(createInfo.when && createInfo.when < currentTime) || const hasSetTimeoutAlarmExceededDelay =
(!createInfo.periodInMinutes && !createInfo.periodInMinutes &&
createInfo.delayInMinutes && createInfo.delayInMinutes &&
startTime + createInfo.delayInMinutes * 60 * 1000 < currentTime) startTime + createInfo.delayInMinutes * 60 * 1000 < currentTime;
) { if (shouldAlarmHaveBeenTriggered || hasSetTimeoutAlarmExceededDelay) {
this.recoveredAlarms.add(name); this.recoveredAlarms.add(name);
continue; continue;
} }