feat(messaging-ui): attachments in inbox mail — reply + compose upload
InboxAdapter gains uploadAttachment + attachment params on mailReply/ composeInternal/composeExternal. MailReader reply and ComposeModal grow an attach affordance (paperclip + pending chips); mail history renders sent attachments. MockInboxAdapter implements upload. 67 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,24 @@ describe('mock inbox adapter', () => {
|
||||
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('<Inbox /> (rendered)', () => {
|
||||
@@ -55,6 +73,17 @@ describe('<Inbox /> (rendered)', () => {
|
||||
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'));
|
||||
|
||||
Reference in New Issue
Block a user