import { beforeEach, describe, expect, it, vi } from 'vitest'; import { renderFieldHistory, teardown } from '../field-history'; // Mock popup module vi.mock('../../../shared/state', () => ({ getState: vi.fn(() => ({ historyItemId: 'item123', selectedItem: { id: 'item123', title: 'Test Item', modified: 1000 }, })), setState: vi.fn(), sendMessage: vi.fn(), navigate: vi.fn(), escapeHtml: (s: string) => s, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn(), })); import { sendMessage, navigate } from '../../../shared/state'; describe('field-history view', () => { let app: HTMLElement; beforeEach(() => { app = document.createElement('div'); teardown(); vi.clearAllMocks(); }); it('renders empty state when no history', async () => { (sendMessage as ReturnType).mockResolvedValueOnce({ ok: true, data: { history: [] }, }); await renderFieldHistory(app); expect(app.innerHTML).toContain('No history available'); }); it('renders history entries masked by default', async () => { (sendMessage as ReturnType).mockResolvedValueOnce({ ok: true, data: { history: [{ field_id: 'f1', field_name: 'password', current_value: 'secret123', entries: [{ value: 'oldpass', changed_at: 500 }], }], }, }); await renderFieldHistory(app); expect(app.innerHTML).toContain('••••••••••••'); expect(app.innerHTML).not.toContain('secret123'); expect(app.innerHTML).toContain('current'); }); it('back button navigates to detail', async () => { (sendMessage as ReturnType).mockResolvedValueOnce({ ok: true, data: { history: [] }, }); await renderFieldHistory(app); app.querySelector('#back-btn')?.click(); expect(navigate).toHaveBeenCalledWith('detail'); }); });