Files
SRCmail/integration/tests/04-all-mail.spec.ts
T
Stefan Hildebrandt e05fbb2fe9 test(integration): All Mail, message actions, and shared-folder sync
Extend the integration suite (now 22 tests) to cover:

- All Mail view (04): single-account merge of Inbox + custom folders with
  Junk excluded, and cross-account aggregation across every logged-in account.
- Message actions from the list context menu (05): mark read/unread, delete
  (→ Trash), mark-as-spam (→ Junk) and not-spam round-trip, verified on both
  the UI counters/row state and the server mailbox the message ends up in.
- Shared/delegated folders (06): a delegated folder (+ Trash/Junk) shared
  alice→carol; the shared folder renders with its counter, and read/unread/
  delete/spam performed there land correctly (server-verified).

Hooks added: data-testid on context-menu delete/spam/read-unread items
(via a testId prop on ContextMenuItem), data-shared on folder rows, and
testId/data-expanded on sidebar section headers to drive the Shared section.

Observations surfaced by the suite (asserted server-side / with a reconcile):
- mark-as-spam doesn't optimistically decrement the *source* counter the way
  delete does; a visibility reconcile settles it.
- shared *destination* counters (shared Trash/Junk) don't refresh live —
  forceSync reconciles the active account only, not shared accounts.
2026-07-11 21:15:20 +02:00

98 lines
3.3 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { ACCOUNTS } from './helpers/config';
import { sendMail } from './helpers/smtp';
import { JmapClient } from './helpers/jmap';
import {
login,
addAccount,
seedAllMailSettings,
folderRow,
openFolder,
expectFolderUnread,
expectEmailVisible,
emailItem,
forceSync,
} from './helpers/app';
/**
* The "All Mail" view — a virtual folder that merges messages across an
* account's folders (Inbox + custom, excluding junk/sent/trash/drafts/archive),
* and across every logged-in account when the cross-account sub-option is on.
*/
const { alice, bob } = ACCOUNTS;
const ALL_MAIL = '__cross_all__';
let seq = 0;
const subj = (l: string) => `IT ${l} ${Date.now()}-${seq++}`;
const send = (to: typeof alice, subject: string) =>
sendMail({ from: to.email, authPass: to.password, to: to.email, subject, body: 'x' });
test.describe('All Mail — single account', () => {
let jmap: JmapClient;
test.beforeEach(async () => {
jmap = await JmapClient.connect(alice.email, alice.password);
await jmap.reset();
});
test('merges Inbox + custom folders and excludes Junk', async ({ page }) => {
const inboxSubj = subj('am-inbox');
const folderSubj = subj('am-folder');
const junkSubj = subj('am-junk');
await send(alice, inboxSubj);
await send(alice, folderSubj);
await send(alice, junkSubj);
// File one into a custom folder and one into Junk (excluded from All Mail).
const folderMail = await jmap.waitForEmail(folderSubj);
await jmap.moveEmailToFolder(folderMail.id, 'Projects');
const junkMail = await jmap.waitForEmail(junkSubj);
const junk = await jmap.mailboxByRole('junk');
await jmap.moveEmail(junkMail.id, junk!.id);
await seedAllMailSettings(page, { crossAccount: false });
await login(page, alice);
// The All Mail entry is present and shows the two included-folder unreads.
await expect(folderRow(page, { name: ALL_MAIL }).first()).toBeVisible();
await expectFolderUnread(page, { name: ALL_MAIL }, 2);
// Its list merges the Inbox and custom-folder messages, but not Junk.
await openFolder(page, { name: ALL_MAIL });
await forceSync(page);
await expectEmailVisible(page, inboxSubj);
await expectEmailVisible(page, folderSubj);
await expect(emailItem(page, junkSubj)).toHaveCount(0);
});
});
test.describe('All Mail — cross account', () => {
test.beforeEach(async () => {
for (const a of [alice, bob]) {
const j = await JmapClient.connect(a.email, a.password);
await j.reset();
}
});
test('merges mail from every logged-in account', async ({ page }) => {
const aSubj = subj('am-a');
const bSubj = subj('am-b');
await send(alice, aSubj);
await send(bob, bSubj);
await seedAllMailSettings(page, { crossAccount: true });
await login(page, alice);
await addAccount(page, bob);
await forceSync(page);
// All Mail aggregates unread across both accounts (alice 1 + bob 1).
await expect(folderRow(page, { name: ALL_MAIL }).first()).toBeVisible();
await expectFolderUnread(page, { name: ALL_MAIL }, 2);
await openFolder(page, { name: ALL_MAIL });
await forceSync(page);
await expectEmailVisible(page, aSubj);
await expectEmailVisible(page, bSubj);
});
});