Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ What changed:

Tests:
- 11 new unit tests across `pseudonyms.test.ts` (8: FNV determinism, pool integrity per language, in-pool selection, stability, seed-based variation) and `usePseudonym.test.ts` (3: stable across rerenders, regenerate caps at 1, respects language pool).
- 5 new E2E specs in `circulation.spec.ts` covering: home tile visible + labelled bilingually, navigation tile→feed, non-opted-in shows Join CTA (and hides the Share button), feed→settings nav, TTL chooser exposes all three durations.
- 5 new E2E specs in `circulation.spec.ts` covering: home tile visible + labelled bilingually, navigation tile→feed, non-opted-in shows Join CTA (and hides the Share button), feed→settings nav, TTL chooser exposes all three durations. 🟡 Fixed E2E auth mock setup (was timing out before ProtectedRoute); all 5 specs now pass.
- `tsc --noEmit` clean. All new vitest tests pass.

**Bug fixes discovered during test validation:**
- 🟡 **E2E test auth setup:** `circulation.spec.ts` was missing `injectMockSession()` and `mockAuthRoutes()`, causing tests to timeout at the home tile (behind ProtectedRoute). Fixed by adding both to beforeEach.
- 🟡 **Language filter missing:** `useLoveLetters.refresh()` had no client-side language filter despite RLS policy comment claiming it enforces language isolation. Added `.eq('language', primaryLang)` for defense-in-depth. Users in different primary languages now correctly see only letters in their language.

**ADHD-Friendly:**
- **One thing at a time.** Each screen is single-focus (compose; or browse; or toggle settings). No multi-step share form.
- **Skip is always available.** The whole feature is opt-in twice over — once via `receive_letters`, separately via `share_letters`. Defaults are both OFF.
Expand Down
4 changes: 3 additions & 1 deletion e2e/circulation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test, expect, type Page } from '@playwright/test';
import { setFrenchLanguage } from './helpers/mocks';
import { injectMockSession, mockAuthRoutes, setFrenchLanguage } from './helpers/mocks';

// Inline mocks so the circulation hooks don't crash against an unmocked
// Supabase URL on mount. Returning empty arrays / null is enough for the
Expand All @@ -23,6 +23,8 @@ async function mockCirculationData(page: Page) {

test.describe('Circulation of Love — navigation', () => {
test.beforeEach(async ({ page }) => {
await injectMockSession(page);
await mockAuthRoutes(page);
await setFrenchLanguage(page);
await mockCirculationData(page);
await page.goto('/');
Expand Down
7 changes: 4 additions & 3 deletions src/hooks/useLoveLetters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ export function useLoveLetters(): UseLoveLetters {
const refresh = useCallback(async () => {
setLoading(true);
setError(null);
// RLS already enforces language + opt-in + passed + live. We add the
// archived filter as a belt-and-suspenders client safeguard.
// RLS enforces opt-in + passed + live. We filter by language and archived
// here as client-side guards (defense-in-depth).
const { data, error: selectError } = await supabase
.from('love_letters')
.select('*')
.eq('language', primaryLang)
.eq('archived', false)
.order('posted_at', { ascending: false })
.limit(50);
Expand All @@ -68,7 +69,7 @@ export function useLoveLetters(): UseLoveLetters {
}
setCurrent((data ?? []) as LoveLetter[]);
setLoading(false);
}, []);
}, [primaryLang]);

useEffect(() => {
refresh();
Expand Down
Loading