Files
relicario/extension/src/popup/components/__tests__/attachments-disclosure.test.ts
adlee-was-taken ce59223fc0 feat(ext): shared state host — decouple components from popup.ts
Introduce shared/state.ts as a service-locator so popup components
(item-detail, item-form, trash, devices, settings, etc.) work in both
the popup and vault tab bundles. Both entry points register themselves
as the host; components import from shared/state instead of popup.ts.
Vault.ts now delegates to the real popup components, removing ~300 lines
of placeholder renderers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:38:06 -04:00

74 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('../../../shared/state', async () => {
const sendMessage = vi.fn();
const escapeHtml = (s: string): string => s.replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]!));
return { sendMessage, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() };
});
import { renderAttachmentsDisclosure, wireAttachmentsDisclosure } from '../attachments-disclosure';
import { sendMessage } from '../../../shared/state';
import type { AttachmentRef } from '../../../shared/types';
const REF1: AttachmentRef = { id: 'a1', filename: 'doc.pdf', mime_type: 'application/pdf', size: 12345, created: 1700000000 };
const REF2: AttachmentRef = { id: 'a2', filename: 'photo.png', mime_type: 'image/png', size: 240000, created: 1700000001 };
describe('attachments-disclosure render', () => {
it('renders empty state with no rows in edit mode', () => {
const html = renderAttachmentsDisclosure({ itemId: 'i1', attachments: [], mode: 'edit', onChange: vi.fn() });
expect(html).toContain('attachments');
expect(html).toContain('+ attach file');
expect(html).not.toContain('attachment-row');
});
it('renders rows + remove buttons in edit mode', () => {
const html = renderAttachmentsDisclosure({ itemId: 'i1', attachments: [REF1, REF2], mode: 'edit', onChange: vi.fn() });
expect(html).toContain('doc.pdf');
expect(html).toContain('photo.png');
expect(html).toContain('×');
expect(html).toContain('attachment-row__thumb'); // image-mime row gets thumb hook
});
it('renders rows + download buttons in view mode (no add btn)', () => {
const html = renderAttachmentsDisclosure({ itemId: 'i1', attachments: [REF1], mode: 'view' });
expect(html).toContain('↓');
expect(html).not.toContain('+ attach file');
});
});
describe('attachments-disclosure wiring', () => {
beforeEach(() => {
vi.mocked(sendMessage).mockReset();
});
it('clicking + attach triggers file input click', () => {
document.body.innerHTML = renderAttachmentsDisclosure({ itemId: 'i1', attachments: [], mode: 'edit', onChange: vi.fn() });
const fileInput = document.querySelector('.attachments-disclosure__file-input') as HTMLInputElement;
const clickSpy = vi.spyOn(fileInput, 'click');
wireAttachmentsDisclosure(document.body, { itemId: 'i1', attachments: [], mode: 'edit', onChange: vi.fn() });
(document.querySelector('.attachment-add-btn') as HTMLButtonElement).click();
expect(clickSpy).toHaveBeenCalled();
});
it('clicking × calls onChange with the attachment removed', () => {
const onChange = vi.fn();
document.body.innerHTML = renderAttachmentsDisclosure({ itemId: 'i1', attachments: [REF1, REF2], mode: 'edit', onChange });
wireAttachmentsDisclosure(document.body, { itemId: 'i1', attachments: [REF1, REF2], mode: 'edit', onChange });
(document.querySelectorAll('.attachment-row__remove')[0] as HTMLElement).click();
expect(onChange).toHaveBeenCalledWith([REF2]);
});
it('clicking ↓ in view mode sends download_attachment', async () => {
vi.mocked(sendMessage).mockResolvedValueOnce({ ok: true, data: { bytes: new ArrayBuffer(10), filename: 'doc.pdf', mimeType: 'application/pdf' } });
document.body.innerHTML = renderAttachmentsDisclosure({ itemId: 'i1', attachments: [REF1], mode: 'view' });
wireAttachmentsDisclosure(document.body, { itemId: 'i1', attachments: [REF1], mode: 'view' });
(document.querySelector('.attachment-row__download') as HTMLElement).click();
await new Promise((r) => setTimeout(r, 50));
expect(vi.mocked(sendMessage)).toHaveBeenCalledWith(expect.objectContaining({
type: 'download_attachment',
itemId: 'i1',
attachmentId: 'a1',
}));
});
});