import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('../../popup', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); const getState = vi.fn(() => ({ view: 'settings-vault', entries: [], selectedId: null, selectedItem: null, selectedIndex: 0, searchQuery: '', activeGroup: null, error: null, loading: false, capturedTabId: null, capturedUrl: '', newType: null, vaultSettings: { trash_retention: { kind: 'days', value: 30 }, field_history_retention: { kind: 'forever' }, generator_defaults: { kind: 'random', length: 20, classes: { lower: true, upper: true, digits: true, symbols: true }, symbol_charset: { kind: 'safe_only' }, }, attachment_caps: {}, autofill_origin_acks: { 'github.com': 1000000000, 'example.com': 999000000 }, }, generatorDefaults: null, })); const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); return { navigate, setState, sendMessage, getState, escapeHtml }; }); vi.mock('../generator-panel', () => ({ openGeneratorPanel: vi.fn(), closeGeneratorPanel: vi.fn(), })); import { renderVaultSettings } from '../settings-vault'; import { sendMessage } from '../../popup'; describe('settings-vault', () => { beforeEach(() => { document.body.innerHTML = '
'; vi.mocked(sendMessage).mockReset(); vi.mocked(sendMessage).mockResolvedValue({ ok: true }); }); it('renders with seeded vault-settings values', () => { const app = document.getElementById('app')!; renderVaultSettings(app); expect(app.textContent).toContain('vault settings'); expect(app.textContent).toContain('github.com'); expect(app.textContent).toContain('example.com'); const trashSel = document.getElementById('trash-retention') as HTMLSelectElement; expect(trashSel.value).toBe('days:30'); const histSel = document.getElementById('history-retention') as HTMLSelectElement; expect(histSel.value).toBe('forever'); }); it('renders origin acks sorted by recency (descending)', () => { const app = document.getElementById('app')!; renderVaultSettings(app); const rows = Array.from(document.querySelectorAll('.ack-row__host')).map((e) => e.textContent); expect(rows).toEqual(['github.com', 'example.com']); }); it('save button disabled until a change is made', () => { const app = document.getElementById('app')!; renderVaultSettings(app); const saveBtn = document.getElementById('save-btn') as HTMLButtonElement; expect(saveBtn.disabled).toBe(true); const trashSel = document.getElementById('trash-retention') as HTMLSelectElement; trashSel.value = 'forever'; trashSel.dispatchEvent(new Event('change', { bubbles: true })); expect(saveBtn.disabled).toBe(false); }); it('revoke button removes origin from pending and enables save', () => { const app = document.getElementById('app')!; renderVaultSettings(app); (document.querySelector('[data-revoke="github.com"]') as HTMLButtonElement).click(); expect(document.querySelector('[data-revoke="github.com"]')).toBeNull(); expect((document.getElementById('save-btn') as HTMLButtonElement).disabled).toBe(false); }); it('save button triggers update_vault_settings with pending', async () => { const app = document.getElementById('app')!; renderVaultSettings(app); (document.querySelector('[data-revoke="github.com"]') as HTMLButtonElement).click(); (document.getElementById('save-btn') as HTMLButtonElement).click(); await new Promise((r) => setTimeout(r, 10)); const call = vi.mocked(sendMessage).mock.calls.find( ([m]) => (m as any).type === 'update_vault_settings', ); expect(call).toBeDefined(); const payload = call![0] as { settings: any }; expect(payload.settings.autofill_origin_acks).not.toHaveProperty('github.com'); expect(payload.settings.autofill_origin_acks).toHaveProperty('example.com'); }); });