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("content-script GET_SETTINGS does not reschedule the lock alarm", async () => {72 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();7374 await messages.handleMessage({ type: "GET_SETTINGS" }, pageSender);7576 expect(alarmCreations()).toBe(0);77 });7879 it("repeated AUTOFILL_QUERY does not reschedule the lock alarm", async () => {80 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();8182 for (let i = 0; i < 50; i += 1) {83 await messages.handleMessage(84 { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },85 pageSender,86 );87 }8889 expect(alarmCreations()).toBe(0);90 });9192 it("repeated ML_PREDICT_BATCH does not reschedule the lock alarm", async () => {93 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();9495 for (let i = 0; i < 50; i += 1) {96 await messages.handleMessage(97 { type: "ML_PREDICT_BATCH", payload: { fields: [] } },98 pageSender,99 );100 }101102 expect(alarmCreations()).toBe(0);103 });104105 it("page TOUCH_AUTO_LOCK is rejected without rescheduling the lock alarm", async () => {106 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();107108 await expect(109 messages.handleMessage({ type: "TOUCH_AUTO_LOCK", payload: {} }, pageSender),110 ).resolves.toMatchObject({ ok: false, error: "invalid_sender" });111 expect(alarmCreations()).toBe(0);112 });113114 it("an extension-page TOUCH_AUTO_LOCK reschedules the lock alarm", async () => {115 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();116117 await messages.handleMessage(118 { type: "TOUCH_AUTO_LOCK", payload: {} },119 { url: "chrome-extension://test-extension-id/popup.html" },120 );121122 expect(alarmCreations()).toBe(1);123 });124125 126 127 it("a focus loop between real actions adds nothing to the deadline", async () => {128 (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();129130 for (let i = 0; i < 20; i += 1) {131 await messages.handleMessage(132 { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },133 pageSender,134 );135 }136 await messages.handleMessage(137 { type: "TOUCH_AUTO_LOCK", payload: {} },138 { url: "chrome-extension://test-extension-id/popup.html" },139 );140 for (let i = 0; i < 20; i += 1) {141 await messages.handleMessage(142 { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },143 pageSender,144 );145 }146147 expect(alarmCreations()).toBe(1);148 });149});