test(jmap): cover the Email/set keyword removal patch shape

Extend the existing keyword write coverage to pin the three call sites
that clear a keyword. markAsRead, batchMarkAsRead and toggleStar must
patch with null and never with false, and must still send true when
setting the keyword.

The three removal cases fail against the previous boolean form, one per
call site. The three set cases pass either way, since that path was
already correct.
This commit is contained in:
zisisvakras
2026-08-17 15:26:41 +03:00
parent be97c8bf08
commit 816cd1bf56
@@ -38,6 +38,11 @@ function mockEmailSet() {
return { captured, fetchSpy };
}
function patchFor(call: JMAPMethodCall, emailId: string): Record<string, unknown> {
const update = call[1].update as Record<string, Record<string, unknown>>;
return update[emailId];
}
describe('JMAP keyword writes route to the email account (#281)', () => {
beforeEach(() => vi.restoreAllMocks());
afterEach(() => vi.restoreAllMocks());
@@ -72,3 +77,60 @@ describe('JMAP keyword writes route to the email account (#281)', () => {
expect(captured[0][1].accountId).toBe('primary-account');
});
});
describe('Email/set clears keywords with null, not false', () => {
beforeEach(() => vi.restoreAllMocks());
afterEach(() => vi.restoreAllMocks());
it('markAsRead(false) removes $seen with null', async () => {
const client = createClient();
const { captured } = mockEmailSet();
await client.markAsRead('email-x', false);
const patch = patchFor(captured[0], 'email-x');
expect(patch['keywords/$seen']).toBeNull();
expect(patch['keywords/$seen']).not.toBe(false);
});
it('markAsRead(true) sets $seen to true', async () => {
const client = createClient();
const { captured } = mockEmailSet();
await client.markAsRead('email-x', true);
expect(patchFor(captured[0], 'email-x')['keywords/$seen']).toBe(true);
});
it('batchMarkAsRead(false) removes $seen with null for every id', async () => {
const client = createClient();
const { captured } = mockEmailSet();
await client.batchMarkAsRead(['email-a', 'email-b'], false);
for (const id of ['email-a', 'email-b']) {
const patch = patchFor(captured[0], id);
expect(patch['keywords/$seen']).toBeNull();
expect(patch['keywords/$seen']).not.toBe(false);
}
});
it('batchMarkAsRead(true) sets $seen to true for every id', async () => {
const client = createClient();
const { captured } = mockEmailSet();
await client.batchMarkAsRead(['email-a', 'email-b'], true);
for (const id of ['email-a', 'email-b']) {
expect(patchFor(captured[0], id)['keywords/$seen']).toBe(true);
}
});
it('toggleStar(false) removes $flagged with null', async () => {
const client = createClient();
const { captured } = mockEmailSet();
await client.toggleStar('email-x', false);
const patch = patchFor(captured[0], 'email-x');
expect(patch['keywords/$flagged']).toBeNull();
expect(patch['keywords/$flagged']).not.toBe(false);
});
it('toggleStar(true) sets $flagged to true', async () => {
const client = createClient();
const { captured } = mockEmailSet();
await client.toggleStar('email-x', true);
expect(patchFor(captured[0], 'email-x')['keywords/$flagged']).toBe(true);
});
});