feat(ext/popup): trash view — list trashed items with restore/purge

Shows trashed items sorted newest-first with restore buttons.
Empty trash button purges all items + orphan blobs. Header shows
count and days until oldest auto-purges.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-26 19:28:56 -04:00
parent 39a8e12438
commit 9fbf9bb3ee
3 changed files with 246 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { renderTrash } from '../trash';
// Mock popup module
vi.mock('../../popup', () => ({
getState: vi.fn(() => ({
vaultSettings: { trash_retention: { kind: 'days', value: 30 } },
})),
setState: vi.fn(),
sendMessage: vi.fn(),
navigate: vi.fn(),
escapeHtml: (s: string) => s,
}));
import { sendMessage, navigate } from '../../popup';
describe('trash view', () => {
let app: HTMLElement;
beforeEach(() => {
document.body.innerHTML = '<div id="app"></div>';
app = document.getElementById('app')!;
vi.clearAllMocks();
});
it('renders empty state when no trashed items', async () => {
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: { items: [] },
});
await renderTrash(app);
expect(app.innerHTML).toContain('Trash is empty');
expect(app.querySelector('#empty-trash-btn')).toBeNull();
});
it('renders trashed items with restore buttons', async () => {
const now = Math.floor(Date.now() / 1000);
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: {
items: [
['id1', { id: 'id1', type: 'login', title: 'Test Login', trashed_at: now - 3600, tags: [], favorite: false, modified: now, attachment_summaries: [] }],
],
},
});
await renderTrash(app);
expect(app.innerHTML).toContain('Test Login');
expect(app.innerHTML).toContain('restore');
expect(app.querySelector('#empty-trash-btn')).not.toBeNull();
});
it('back button navigates to list', async () => {
(sendMessage as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
ok: true,
data: { items: [] },
});
await renderTrash(app);
app.querySelector<HTMLButtonElement>('#back-btn')?.click();
expect(navigate).toHaveBeenCalledWith('list');
});
});