Files
familysync/.planning/phases/04-shared-lists-live-sync/04-UAT.md
T

5.6 KiB

status, phase, source, mode, started, updated
status phase source mode started updated
complete 04-shared-lists-live-sync
04-01-SUMMARY.md
04-02-SUMMARY.md
04-03-SUMMARY.md
04-04-SUMMARY.md
04-05-SUMMARY.md
04-06-SUMMARY.md
04-07-SUMMARY.md
playwright-cli (automated, operator-elected) 2026-06-09T18:39:39Z 2026-06-09T18:48:06Z

Current Test

[testing complete]

Tests

1. Cold Start Smoke Test

expected: API /health returns 200, PWA loads, navigating to /lists renders the Lists surface with live data (not an error/blank). result: pass evidence: API /health → {"ok":true,"db":"up"}; PWA served on :5173; /lists rendered empty-state ("No lists yet") with GET /api/lists → 200.

2. Navigate to Lists (bottom tab bar)

expected: Bottom tab bar / desktop nav exposes a "Lists" link; clicking routes to /lists and shows the index. result: pass evidence: Desktop sidebar nav shows Calendar + Lists links; Calendar link routed to /calendar (full calendar rendered), Lists link routed to /lists. (Bottom tab bar is the mobile-width variant of the same nav.)

3. Create a named list (defaults to Shared)

expected: CreateListSheet opens; entering a name + confirming creates the list (default Shared), sheet closes, new card appears. result: pass evidence: "New list" sheet opened with Visibility toggle defaulting to Shared [pressed], Create disabled until named. Created "Groceries" → POST /api/lists → 201; card "Groceries · 0 items · Shared" appeared.

4. Delete a list with confirmation

expected: Delete (X) control opens ListDeleteDialog with clear heading/body; confirm removes card, cancel leaves it. result: pass evidence: Created throwaway "ToDelete"; hover revealed "Delete list: ToDelete"; dialog "Delete list?" with body "'ToDelete' and all its items will be permanently removed." Confirm → DELETE /api/lists/1279 → 200; ToDelete removed, Groceries remained.

5. Open a list and add items

expected: ListDetail shows add-item input; typing + Add inserts into "Active items" with checkbox + drag handle; optimistic. result: pass evidence: Opened /lists/1278 — "Live sync connected" indicator present. Added milk, eggs, bread → all in "Active items" with checkboxes and "Drag to reorder" handles.

6. Check an item — sinks to Completed

expected: Checking moves item into a "Completed (N)" section (D-05); unchecking returns to bottom of Active. result: pass evidence: Checked "milk" → moved into "Completed (1)" collapsible section (checkbox checked); Active showed eggs, bread.

7. Delete an item instantly (no confirm)

expected: Delete control removes item immediately, no confirmation (D-06/D-09 delete-wins). result: pass evidence: Deleted "eggs" → removed instantly, no dialog rendered (snapshot confirmed no dialog/confirm element).

8. Drag to reorder active items

expected: Dragging an item by its handle to a new position persists (survives reload); single-row rank write. result: pass evidence: Dragged "cheese" from bottom to top → order cheese/bread/apples; PATCH /api/list-items → 200; order persisted after full page reload.

9. Drag an item to the very top (collation regression)

expected: Dragging to position 0 persists; dragged item stays first after reload (utf8mb4_bin collation, Plan 04-07). result: pass evidence: Manual pointer drag of "cheese" above the a0-ranked top generated rank "Zz" (uppercase-prefixed). DB-ordered API AND UI both returned cheese FIRST (cheese=Zz, apples=a0, bread=a0V); persisted after reload. Without the collation fix, MariaDB's case-insensitive default would sort 'Zz' after 'a0' and bounce it to the bottom — confirmed fixed end-to-end.

10. Live sync between two members (within seconds)

expected: With two sessions on the same shared list, an edit in A appears in B within seconds, no manual refresh. result: pass evidence: Opened session B (separate browser context) on /lists/1278 — "Live sync connected". Added "butter" in session A → appeared in session B within ~3s with no reload.

11. Live sync survives a brief reconnect

expected: On SSE drop, LiveSyncIndicator reflects reconnecting/disconnected then returns to connected (bounded backoff); edits reconcile. result: pass evidence: (a) Took B offline + added "yogurt" in A → on reconnect, yogurt reconciled into B. (b) Blocked **/api/sse/lists (503) + reloaded B → indicator showed "Reconnecting…"; unblocked + reloaded → returned to "Live sync connected". Bounded-backoff hook also unit-tested (04-06, 8 passing hook tests).

12. Private-list isolation (no cross-leak)

expected: A member's private list and its events are never visible to a member without access (D-04). result: skipped reason: Not drivable via the live UI — the dev-auth bypass injects a single static DEV_USER with no user-switching, so two distinct authenticated members cannot be simulated through the PWA. D-04 isolation is comprehensively proven at the route layer by passing automated tests: T-04-02 (GET excludes another member's private list), the 4 D-04 scoped-SSE tests in 04-06 (no fan-out leak to non-members), and the 04-07 sharee-403 tests. Re-confirm during the live multi-user Pangolin/Authelia smoke (deployment gate).

Summary

total: 12 passed: 11 issues: 0 pending: 0 skipped: 1 blocked: 0

Gaps

[none — all functional tests passed; test 12 deferred to deployment-time multi-user smoke, already covered by automated route-layer D-04 tests]

Notes

  • Known non-blocking stub observed: ListDetail header renders "List" rather than the list name (carried from Plans 04-04/04-05; fetchListItems returns items only). Does not affect any LIST-01..04 behavior. Tracked in plan summaries.
  • Console: only a favicon.ico 404 (harmless); no application errors during any flow.