import { describe, it, expect } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { InboxProvider } from './provider'; import { Inbox } from './inbox'; import { MockInboxAdapter } from '../adapters/mock-inbox'; function mount() { return render( , ); } describe('mock inbox adapter', () => { it('unifies work items + mail in the Open view; other states drop mail', async () => { const a = new MockInboxAdapter(); const open = await a.listInbox('OPEN'); expect(open.some((i) => i.kind === 'MAIL')).toBe(true); expect(open.some((i) => i.kind === 'MENTION')).toBe(true); const done = await a.listInbox('DONE'); expect(done.some((i) => i.kind === 'MAIL')).toBe(false); }); it('transition moves a work item out of Open', async () => { const a = new MockInboxAdapter(); await a.transition('in_3', 'DONE'); expect((await a.listInbox('OPEN')).some((i) => i.id === 'in_3')).toBe(false); expect((await a.listInbox('DONE')).some((i) => i.id === 'in_3')).toBe(true); }); it('mail reply appends to the thread', async () => { const a = new MockInboxAdapter(); await a.mailReply('mt_welcome', 'thanks!'); expect((await a.mailHistory('mt_welcome')).some((m) => m.text === 'thanks!')).toBe(true); }); it('reply carries an uploaded attachment', async () => { const a = new MockInboxAdapter(); const ref = await a.uploadAttachment(new File(['x'], 'plan.pdf', { type: 'application/pdf' })); expect(ref).toMatchObject({ filename: 'plan.pdf', mimeType: 'application/pdf' }); await a.mailReply('mt_welcome', '', ref); const last = (await a.mailHistory('mt_welcome')).at(-1)!; expect(last.attachment?.filename).toBe('plan.pdf'); }); it('composeInternal carries a first attachment onto the new thread', async () => { const a = new MockInboxAdapter(); const ref = await a.uploadAttachment(new File(['x'], 'quote.png', { type: 'image/png' })); await a.composeInternal('pp_sofia', 'Quote', 'see attached', [ref]); const open = await a.listInbox('OPEN'); const row = open.find((i) => i.title === 'Quote')!; expect((await a.mailHistory(row.threadId!)).at(-1)?.attachment?.filename).toBe('quote.png'); }); }); describe(' (rendered)', () => { it('lists items and opens a mail thread on click', async () => { mount(); // A folded mail row is present. const welcome = await screen.findByText('Welcome to the Founders Club'); fireEvent.click(welcome); // The reader opens with a reply box. expect(await screen.findByLabelText('Reply')).toBeTruthy(); }); it('replies into a mail thread', async () => { mount(); fireEvent.click(await screen.findByText('Welcome to the Founders Club')); const input = (await screen.findByLabelText('Reply')) as HTMLInputElement; fireEvent.change(input, { target: { value: 'got it' } }); fireEvent.click(screen.getByText('Reply')); await waitFor(() => expect(screen.getByText('got it')).toBeTruthy()); }); it('attaches a file into a mail reply', async () => { mount(); fireEvent.click(await screen.findByText('Welcome to the Founders Club')); const file = new File(['data'], 'roof.pdf', { type: 'application/pdf' }); fireEvent.change(await screen.findByLabelText('Attach file'), { target: { files: [file] } }); // The pending chip shows the file, then Reply sends it. await screen.findByText(/roof\.pdf/); fireEvent.click(screen.getByText('Reply')); await waitFor(() => expect(screen.getAllByText(/roof\.pdf/).length).toBeGreaterThan(0)); }); it('composes an in-app message and it shows in the inbox', async () => { mount(); fireEvent.click(await screen.findByText('New message')); // pick a recipient fireEvent.click(await screen.findByText('Sofia Ramirez')); fireEvent.change(screen.getByLabelText('Subject'), { target: { value: 'Quick q' } }); fireEvent.change(screen.getByLabelText('Message body'), { target: { value: 'ping' } }); fireEvent.click(screen.getByText('Send')); await waitFor(() => expect(screen.getByText('Quick q')).toBeTruthy()); }); });