Files
Oier Bravo Urtasun 768b3cda58 docs: split CLAUDE.md into docs/{development,deployment,history/fase-*}
CLAUDE.md shrinks to rules + status + index (175 lines, down from 476).
Dev setup, prod deploy, and per-phase build records move to docs/.
Gotchas regrouped by domain (auth, frontend, PWA, testing, backend, deploy,
platform) and unnumbered so they don't drift as new ones land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:21:20 +02:00

3.5 KiB

Fase 7 — collective-flow E2E coverage (2026-04-14)

12 new Playwright tests + 1 DB fixtures helper + 1 migration. Writing the tests uncovered three product-code bugs which are fixed as part of this phase.

Tests (all green, included in just test-e2e)

  • apps/web/tests/e2e/onboarding.test.ts — O-01..O-03: Eva auto-redirect to /onboarding, happy-path create, empty-name submit-disabled.
  • apps/web/tests/e2e/invitation.test.ts — I-01..I-05: admin generates token, logged-out-then-accept, logged-in one-click accept, expired + already-used error states.
  • apps/web/tests/e2e/manage-collective.test.ts — MC-02..MC-05: promote member → admin, demote back, remove member, generate pending invitation.
  • apps/web/tests/fixtures/db.tsresetEva() / seedExpiredInvitation() / seedUsedInvitation() / restoreSeedMembership() / countMembership(). All via raw SQL (bypasses RLS) so they don't depend on SUPABASE_SERVICE_ROLE_KEY being in the Playwright process env.

Scope dropped from the plan

  • O-04 (multi-collective via a "+ new collective" button in the sidebar): the affordance does not exist in the UI; adding it would be feature work, out of a test-only phase.
  • MC-01 (rename collective): /collective/manage has no rename input; same reasoning.

Both are flagged in plan/fase-7-collective-flow-tests.md as future UI follow-ups.

Product-code fixes discovered while writing these tests (all committed with this phase)

  1. supabase/migrations/012_create_collective_rpc.sql — atomic create_collective(name, emoji) RPC. Pre-existing onboarding flow did two separate REST calls (INSERT collectives chained with .select().single(), then INSERT collective_members). Both failed: the RETURNING on the first triggered the SELECT policy (is_member(id)) which the creator can't pass yet, and the second was gated by is_admin(collective_id) against a collective with zero admins. The RPC is SECURITY DEFINER and does both inserts atomically. onboarding/+page.svelte now calls supabase.rpc('create_collective', {...}).

  2. routes/+layout.svelte — resume pending invitation after login. invitation/[token]/+page.svelte stashes the token in sessionStorage.pendingInvitationToken before triggering OAuth, but nothing read it back. Post-login redirect logic now checks sessionStorage and routes the user back to /invitation/<token> instead of defaulting to /onboarding or /lists.

  3. routes/(app)/collective/manage/+page.svelte — reactively reload members. onMount called loadMembers() once; if $currentCollective was still null at that point (race with the auth listener on a cold navigation), the query short-circuited and the list stayed empty. Now subscribed to currentCollective so loadMembers re-runs whenever the store resolves.

  4. routes/invitation/[token]/+page.svelte — wait for authLoading before deciding. Racing with the auth listener's first emit caused the page to see !$isAuthenticated for a freshly-authenticated user and trigger a redundant Keycloak round-trip. Now awaits authLoading → false before branching.

Test IDs added to UI (stable selectors for Playwright)

  • Onboarding: onboarding-create-tab, onboarding-join-tab, collective-name-input, collective-submit.
  • Manage: member-row-<uid>, role-select-<uid>, remove-member-<uid>, generate-invite, invite-link.
  • Invitation: invitation-accept, invitation-error (with data-error-key attribute carrying the error key for assertion).