Files
relicario/extension/src/popup/components/__tests__/field-history.test.ts
adlee-was-taken 3b4788e5dc feat(ext/popup): field history view — masked values with reveal toggle
Shows current + historical values for tracked fields (password/concealed).
Click to reveal, copy button per entry (plaintext stored in a module-level
Map, never embedded in the DOM). Grouped by field name if multiple tracked
fields exist. Adds historyItemId to PopupState and 'field-history' to View.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-27 00:23:54 -04:00

70 lines
1.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { renderFieldHistory, teardown } from '../field-history';
// Mock popup module
vi.mock('../../popup', () => ({
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,
}));
import { sendMessage, navigate } from '../../popup';
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<typeof vi.fn>).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<typeof vi.fn>).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<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: { history: [] },
});
await renderFieldHistory(app);
app.querySelector<HTMLButtonElement>('#back-btn')?.click();
expect(navigate).toHaveBeenCalledWith('detail');
});
});