feat(ext/sw): add session inactivity timer with configurable timeout
Implements a service-worker-side session timer that locks the vault after a configurable period of inactivity (default 15 min). Supports two modes: 'inactivity' (timer-based) and 'every_time' (no timer). Config persists via chrome.storage.local and is exposed through get_session_config / update_session_config popup messages. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
99
extension/src/service-worker/__tests__/session-timer.test.ts
Normal file
99
extension/src/service-worker/__tests__/session-timer.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
||||
import * as timer from '../session-timer';
|
||||
|
||||
describe('session-timer', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
// Reset to default config for each test
|
||||
timer.setConfig({ mode: 'inactivity', minutes: 15 });
|
||||
timer.stopTimer();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
timer.stopTimer();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('fires callback after inactivity timeout', () => {
|
||||
const cb = vi.fn();
|
||||
timer.onExpired(cb);
|
||||
timer.resetTimer();
|
||||
|
||||
// Default is 15 minutes = 900_000 ms
|
||||
vi.advanceTimersByTime(899_999);
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
|
||||
vi.advanceTimersByTime(1);
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('resets timer on each resetTimer() call', () => {
|
||||
const cb = vi.fn();
|
||||
timer.onExpired(cb);
|
||||
timer.resetTimer();
|
||||
|
||||
// Advance 10 minutes
|
||||
vi.advanceTimersByTime(600_000);
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
|
||||
// Reset — clock should restart
|
||||
timer.resetTimer();
|
||||
|
||||
// Advance another 10 minutes (20 total, but only 10 since last reset)
|
||||
vi.advanceTimersByTime(600_000);
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
|
||||
// Advance remaining 5 minutes to hit the 15-minute mark from last reset
|
||||
vi.advanceTimersByTime(300_000);
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not fire when mode is every_time', () => {
|
||||
const cb = vi.fn();
|
||||
timer.onExpired(cb);
|
||||
timer.setConfig({ mode: 'every_time' });
|
||||
timer.resetTimer();
|
||||
|
||||
// Advance well past the default 15 minutes
|
||||
vi.advanceTimersByTime(60 * 60 * 1000);
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('respects updated minutes', () => {
|
||||
const cb = vi.fn();
|
||||
timer.onExpired(cb);
|
||||
timer.setConfig({ mode: 'inactivity', minutes: 5 });
|
||||
timer.resetTimer();
|
||||
|
||||
vi.advanceTimersByTime(4 * 60 * 1000);
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
|
||||
vi.advanceTimersByTime(60 * 1000);
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('getConfig returns current config', () => {
|
||||
// Default
|
||||
expect(timer.getConfig()).toEqual({ mode: 'inactivity', minutes: 15 });
|
||||
|
||||
// After set
|
||||
timer.setConfig({ mode: 'every_time' });
|
||||
expect(timer.getConfig()).toEqual({ mode: 'every_time' });
|
||||
|
||||
timer.setConfig({ mode: 'inactivity', minutes: 30 });
|
||||
expect(timer.getConfig()).toEqual({ mode: 'inactivity', minutes: 30 });
|
||||
});
|
||||
|
||||
it('stopTimer prevents firing', () => {
|
||||
const cb = vi.fn();
|
||||
timer.onExpired(cb);
|
||||
timer.resetTimer();
|
||||
|
||||
vi.advanceTimersByTime(600_000); // 10 minutes in
|
||||
timer.stopTimer();
|
||||
|
||||
// Advance past what would have been the 15-minute mark
|
||||
vi.advanceTimersByTime(600_000);
|
||||
expect(cb).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user