1234567891011121314import { beforeEach, describe, expect, it, vi } from "vitest";1516type StorageShape = Record<string, unknown>;1718function makeChromeStorageMock() {19 const storage: StorageShape = {};20 const chrome = {21 storage: {22 local: {23 get: vi.fn(async (key?: string | string[]) => {24 if (!key) return { ...storage };25 const keys = Array.isArray(key) ? key : [key];26 return keys.reduce<StorageShape>((acc, k) => { acc[k] = storage[k]; return acc; }, {});27 }),28 set: vi.fn(async (next: StorageShape) => { Object.assign(storage, next); }),29 remove: vi.fn(async () => {}),30 },31 session: {32 get: vi.fn(async () => ({})),33 set: vi.fn(async () => {}),34 remove: vi.fn(async () => {}),35 },36 },37 alarms: { clear: vi.fn(), create: vi.fn(), onAlarm: { addListener: vi.fn() } },38 idle: { setDetectionInterval: vi.fn(), onStateChanged: { addListener: vi.fn() } },39 runtime: {40 id: "test-extension-id",41 getURL: vi.fn((path: string) => `chrome-extension://test-extension-id/${path}`),42 },43 } as unknown as typeof globalThis.chrome;44 return { chrome };45}4647const pageSender = {48 tab: { url: "https://example.com/login" },49 url: "https://example.com/login",50} as unknown as chrome.runtime.MessageSender;5152describe("PB-26-107: passive page traffic does not refresh auto-lock", () => {53 let vault: typeof import("./vault");54 let messages: typeof import("./messages");5556 beforeEach(async () => {57 vi.resetModules();58 (globalThis as Record<string, unknown>).chrome = makeChromeStorageMock().chrome;59 vi.stubGlobal("fetch", vi.fn(async () => { throw new Error("no network"); }));60 vi.stubGlobal("__PEACH_E2E_PASSKEYS__", false);6162 vault = await import("./vault");63 messages = await import("./messages");64 await vault.createVault("test-autolock-pass");65 });6667 function alarmCreations(): number {68 return (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mock.calls.length;69 }7071 it("repeated AUTOFILL_QUERY does not reschedule the lock alarm", async () => {72 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();7374 for (let i = 0; i < 50; i += 1) {75 await messages.handleMessage(76 { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },77 pageSender,78 );79 }8081 expect(alarmCreations()).toBe(0);82 });8384 it("repeated ML_PREDICT_BATCH does not reschedule the lock alarm", async () => {85 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();8687 for (let i = 0; i < 50; i += 1) {88 await messages.handleMessage(89 { type: "ML_PREDICT_BATCH", payload: { fields: [] } },90 pageSender,91 );92 }9394 expect(alarmCreations()).toBe(0);95 });9697 98 99 it("a user-initiated message still reschedules the lock alarm", async () => {100 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();101102 await messages.handleMessage({ type: "TOUCH_AUTO_LOCK", payload: {} }, pageSender);103104 expect(alarmCreations()).toBe(1);105 });106107 108 109 it("a focus loop between real actions adds nothing to the deadline", async () => {110 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();111112 for (let i = 0; i < 20; i += 1) {113 await messages.handleMessage(114 { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },115 pageSender,116 );117 }118 await messages.handleMessage({ type: "TOUCH_AUTO_LOCK", payload: {} }, pageSender);119 for (let i = 0; i < 20; i += 1) {120 await messages.handleMessage(121 { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },122 pageSender,123 );124 }125126 expect(alarmCreations()).toBe(1);127 });128});