Extend 04-shared-identity's UI test to not just assert team@example.org is
offered but to actually select it as the sender and confirm it becomes the
active From identity, then hold on the composer so the selected group address is
visible in the recorded video. Adds selectComposerFrom / selectedComposerFrom
helpers.
Feat/unified mailbox account scope
Rework the sidebar "All accounts" into an account-bounded "Unified Mailbox"
by default, with cross-account merging as an opt-in (admin-gated) sub-option.
The standalone per-account "All Mail" virtual folder is folded into the unified
All mail / Unread / Starred entries.
Conflict resolution notes:
- stores/settings-store.ts: both main and this branch independently added a
per-account default-identity (#507) migration at different versions (main v6,
branch v7). Merged migration is version 7 using the refactored migrateSettings
function; the unified-mailbox rework is guarded at `version < 7` so users who
stopped at main's interim v6 identity bump still receive it, while the #507
identity-map coercion stays at `version < 6` so their populated map is kept.
- stores/auth-store.ts: kept main's applyPreferredIdentity (superset with the
pre-#507 legacy migration).
- stores/email-store.ts: removed the ALL_MAIL_MAILBOX_ID paths (folded into the
unified views) while preserving main's plugin hooks (onSearchResults /
onEmailsFetched); adopted advancedSearchCrossViewEmails for advanced cross-view
search.
- components/settings/layout-settings.tsx: kept main's faviconUnreadBadge setting
alongside the new unifiedCrossAccount toggle.
- integration/: union-merged the two independently-authored suites - branch suite
is authoritative (matches new behavior) with main's shared-identity (#569) group
infrastructure preserved.
- components/email/email-composer.tsx: dropped a duplicate data-testid attribute
introduced by the auto-merge.
Provision a Stalwart group (team@example.org) with carol as a member before her
first login, and assert the composer's From selector offers the group address.
This confirms the group-membership scenario of #569 already works out of the
box: Stalwart returns the group's send-as identity on the member's own account,
so the app's normal single-account identity load surfaces it (identities.length
> 1 -> the From <select> renders with team@).
- stalwart: create the `team` Group in plan-accounts and add carol via
User.memberGroupIds in the entrypoint (id resolved after apply, like
DOMAIN_ID). carol, not alice/bob, so the sync specs stay unshared.
- helpers: GROUP config, openComposer/composerFromOptions, and JmapClient
accounts + sharedAccountNames (Identity/get needs the submission capability).
- composer: add data-testid="composer-from" to the From <select> and its
single-identity <span> fallback.
Ref: https://github.com/bulwarkmail/webmail/issues/569
Add an end-to-end integration harness that runs the webmail against a real
Stalwart mail server in Docker and drives it with Playwright, focused on the
mail/folder synchronisation behaviour (unread/total counters, folder-list
sync, account-scoped Unified Mailbox) that the unified-mailbox work touches.
- integration/ stack: Stalwart (declarative bootstrap: alice/bob/carol,
submission + IMAP listeners, permissive CORS) + webmail (dev mode, so the
browser's plaintext cross-origin JMAP calls aren't blocked by the prod CSP).
- Helpers: dependency-free SMTP submit client, JMAP client for seeding /
inspecting server state, and page helpers (login, add/switch account,
locale-independent folder-counter reads).
- Specs: login, single-account sync (receive/read/move/delete/folder-create/
burst) and multi-account (per-account isolation + cross-account Unified
Inbox aggregation + background-account delivery). 12 tests, all green.
- Add focused data-testid hooks to the mail UI (folder rows + counters, email
list items, account switcher, composer) for stable selectors.
- Exclude examples/ and integration/ from tsconfig/eslint/.dockerignore.
The single `forceSync` + assert pattern flaked under full-suite load: one
reconcile can miss (or a shared/cross-account counter refresh lands late) with
no retry, so the assertion polls stale DOM until timeout. The flake moved
between reconcile-dependent tests (server-side move, spam source drain, unified
/ shared counters) run to run.
- Add expectFolderCountsSynced(): nudges a reconcile (visibilitychange ->
checkForStateChanges) before *every* poll, so a missed reconcile is retried
for the full window. Compares only the provided unread/total fields.
- Use it for the reconcile-dependent counter checks in 02 (move/delete),
03 (multi-account isolation + unified aggregation), 04 (All Mail), 05 (spam
source), 06 (shared folders); drop the now-redundant standalone forceSync.
Pure live-push assertions (incoming/burst, background-login-live) keep the
plain helpers so they still prove push works.
- The spam->not-spam round-trip could stall the Junk badge reconcile even with
retries under load; assert the optimistic list removal + authoritative server
round-trip (out of Junk, back in Inbox) instead of the badge.
Validated with two back-to-back full-suite runs: 35 passed each.
Add draft and shared-folder-move coverage (suite now 31 tests). Findings are
asserted server-side or pinned with test.fail where the UI is incomplete.
Drafts (07):
- multiple recipients (committed and typed-but-uncommitted) persist, and the
draft reopens via the continue-draft button;
- a server-created draft (with $draft) shows the continue-draft button;
- a changed sender identity is saved to the draft on the server;
- KNOWN BUG (test.fail): reopening a draft resets the From selector to the
default identity instead of the one the draft was saved with.
Shared-folder moves (08):
- shared -> shared (same owner) moves work in both directions (server-verified);
- KNOWN LIMITATION (test.fail): cross-account moves (own account <-> shared
folder) don't relocate the message — the Move-to submenu offers the target
but clicking it is a no-op.
Hooks added: composer From select + save-status, viewer edit-draft button,
context-menu "Move to" submenu + per-target testids (testId on
ContextMenuSubMenu). Helpers: JMAP identities/createDraft/sharing, composer
drive + move-via-submenu. README documents the findings.
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.
Add an end-to-end integration harness that runs the webmail against a real
Stalwart mail server in Docker and drives it with Playwright, focused on the
mail/folder synchronisation behaviour (unread/total counters, folder-list
sync, account-scoped Unified Mailbox) that the unified-mailbox work touches.
- integration/ stack: Stalwart (declarative bootstrap: alice/bob/carol,
submission + IMAP listeners, permissive CORS) + webmail (dev mode, so the
browser's plaintext cross-origin JMAP calls aren't blocked by the prod CSP).
- Helpers: dependency-free SMTP submit client, JMAP client for seeding /
inspecting server state, and page helpers (login, add/switch account,
locale-independent folder-counter reads).
- Specs: login, single-account sync (receive/read/move/delete/folder-create/
burst) and multi-account (per-account isolation + cross-account Unified
Inbox aggregation + background-account delivery). 12 tests, all green.
- Add focused data-testid hooks to the mail UI (folder rows + counters, email
list items, account switcher, composer) for stable selectors.
- Exclude examples/ and integration/ from tsconfig/eslint/.dockerignore.