From 53c49143ecbc23da0dc42e44f0887dd10112643f Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 28 Oct 2026 10:29:26 +0100 Subject: [PATCH] fix: drop the scheduled badge from a demo message once its send is cancelled --- lib/__tests__/demo-client-eml.test.ts | 18 ++++++++++++++++++ lib/demo/demo-client.ts | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/__tests__/demo-client-eml.test.ts b/lib/__tests__/demo-client-eml.test.ts index d6413e10b..11a656a4d 100644 --- a/lib/__tests__/demo-client-eml.test.ts +++ b/lib/__tests__/demo-client-eml.test.ts @@ -30,3 +30,21 @@ describe('DemoJMAPClient raw messages', () => { expect(await (await client.fetchBlob(draft!.blobId!)).text()).toContain('Subject: Draft for export'); }); }); + +// A server sends a new object on every fetch. The app stamps the scheduled +// send onto the message it opens; with the demo's own object, a cancelled +// send kept its Scheduled badge in Drafts. +describe('DemoJMAPClient getEmail', () => { + it('returns a copy that the caller may change', async () => { + const client = new DemoJMAPClient(); + const { emails } = await client.getEmails(undefined, undefined, 1); + const opened = (await client.getEmail(emails[0].id))!; + opened.isScheduled = true; + opened.scheduledSendAt = '2030-01-01T08:00:00Z'; + const again = (await client.getEmail(emails[0].id))!; + expect(again.isScheduled).toBeUndefined(); + expect(again.scheduledSendAt).toBeUndefined(); + const { emails: listed } = await client.getEmails(undefined, undefined, 500); + expect(listed.find((e) => e.id === emails[0].id)!.isScheduled).toBeUndefined(); + }); +}); diff --git a/lib/demo/demo-client.ts b/lib/demo/demo-client.ts index ec3374cc6..50b095d2e 100644 --- a/lib/demo/demo-client.ts +++ b/lib/demo/demo-client.ts @@ -383,7 +383,10 @@ export class DemoJMAPClient implements IJMAPClient { } async getEmail(emailId: string): Promise { - return this.data.emails.find(e => e.id === emailId) ?? null; + // A copy, as a server sends: the app stamps what it opens (a scheduled + // send's fields), which must not stick to the stored message. + const email = this.data.emails.find(e => e.id === emailId); + return email ? structuredClone(email) : null; } async getTagCounts(tagIds: string[], accountId?: string, excludeMailboxIds?: string[]): Promise> {