import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { finishSetup, STEPS } from '../setup'; import { state, clearWizardState } from '../setup-steps'; describe('finishSetup', () => { beforeEach(() => { (global as any).chrome = { tabs: { create: vi.fn(() => Promise.resolve({ id: 999 })), getCurrent: vi.fn(() => Promise.resolve({ id: 42 })), remove: vi.fn(() => Promise.resolve()), }, runtime: { getURL: vi.fn((p: string) => `chrome-extension://abc/${p}`), }, }; }); it('opens vault.html in a new tab', async () => { await finishSetup(); expect(chrome.runtime.getURL).toHaveBeenCalledWith('vault.html'); expect(chrome.tabs.create).toHaveBeenCalledWith({ url: 'chrome-extension://abc/vault.html', }); }); it('closes the current setup tab after opening the vault tab', async () => { await finishSetup(); expect(chrome.tabs.getCurrent).toHaveBeenCalled(); expect(chrome.tabs.remove).toHaveBeenCalledWith(42); }); it('still opens the vault tab even if closing the setup tab fails', async () => { (chrome.tabs.remove as any).mockRejectedValueOnce(new Error('no permission')); await expect(finishSetup()).resolves.not.toThrow(); expect(chrome.tabs.create).toHaveBeenCalled(); }); }); describe('setup step registry', () => { it('has the six steps in canonical order', () => { expect(STEPS.map((s) => s.id)).toEqual(['mode', 'host', 'connection', 'vault', 'device', 'done']); }); it('each step renders non-empty HTML and attach returns a teardown', () => { const ctx = { state: {} as never, rerender: vi.fn(), goto: vi.fn() }; for (const step of STEPS) { const html = step.render(ctx as never); expect(typeof html).toBe('string'); expect(html.length).toBeGreaterThan(0); // render output must be in the DOM before attach (attach wires getElementById listeners) document.body.innerHTML = `
${html}
`; const teardown = step.attach(document.body, ctx as never); expect(typeof teardown).toBe('function'); teardown(); // must not throw } }); }); describe('clearWizardState', () => { afterEach(() => { clearWizardState(); }); it('zero-fills the reachable Uint8Array fields and resets state', () => { const carrier = new Uint8Array([1, 2, 3, 4]); const ref = new Uint8Array([5, 6, 7, 8]); state.carrierImageBytes = carrier; state.referenceImageBytes = ref; state.passphrase = 'secret'; state.mode = 'new'; clearWizardState(); expect(Array.from(carrier)).toEqual([0, 0, 0, 0]); // fill(0) ran on the captured ref expect(Array.from(ref)).toEqual([0, 0, 0, 0]); expect(state.carrierImageBytes).toBeNull(); // field reset expect(state.referenceImageBytes).toBeNull(); expect(state.passphrase).toBe(''); expect(state.mode).toBeNull(); }); });