Skip to content

test(e2e): full user-journey spec against production (landing, scan, auth, browse)#76

Merged
bejranonda merged 6 commits into
mainfrom
claude/code-review-refactor-212i2
Jun 9, 2026
Merged

test(e2e): full user-journey spec against production (landing, scan, auth, browse)#76
bejranonda merged 6 commits into
mainfrom
claude/code-review-refactor-212i2

Conversation

@bejranonda

Copy link
Copy Markdown
Owner

Summary

Adds frontend/tests/e2e/user-journey.spec.ts — a browser-driven full user journey against production (https://shinnyguide.autobahn.bot), exercising the app the way a real user would in four independent phases:

  1. Landing + locale switch/th renders, switching to /en re-hydrates under the new lang, hreflang graph covers all 4 locales + x-default, zero console errors.
  2. Scan flow — uploads a 256×256 noisy PNG (generated in-memory, no binary fixture; sized to clear the 500-byte client floor in useScanUpload.ts), clicks the "Analyze Now" CTA, and waits for the analyze cascade (Workers AI → Gemini) to land on a terminal UI state — meal result, not-food card, or handled-error card. Own 180s timeout absorbs cold-start cascades.
  3. Auth round-trip — submits a fresh unique registration with no voucher; asserts the live /api/auth/register contract (voucher_required on 400) AND that the form settles on an inline error/success — explicitly failing if the Next.js client error boundary engages (a crash observed during test development).
  4. Recipes / chat / dashboard — each reaches a stable rendered state (own UI or auth redirect) without fatal console errors; known-benign noise (RSC prefetch fallback, auth/me probe) filtered with rationale.

Also brings the branch up to date with main (merge commit d428ff8); both scan commits unique to this branch had already landed on main via #8-era PRs, so conflicted source files were resolved by taking main's newer versions.

Verification

  • npm run check:all — type-check + i18n key check + 171/171 unit tests pass
  • npx playwright test tests/e2e/user-journey.spec.ts4/4 phases pass against production, ~17s warm (repeated runs stable, including one run that exercised the cold-path analyze timeout and confirmed the UI's handled-error terminal state)

Side-effect budget per run

  • 1 vision-model call (the noise PNG routes to the not_food branch)
  • 1 registration attempt that is rejected by voucher gating, leaving no DB row

https://claude.ai/code/session_01BWLU1nKZpxToH5hMeBDusG


Generated by Claude Code

claude added 6 commits April 21, 2026 22:08
…o collage

User report: "I uploaded 2 photos, but got results from only 1."

Root cause: when the client uploads N>1 photos, the scan hook stitches
them into a single grid image via stitchImagesToCanvas(), then POSTs
that image to /api/analyze. The server forwards it to Gemini with the
stock meal/menu/drink_snack prompt, which has no language describing
the collage layout. The model frequently interprets the stitched image
as a single scene and returns one dish in `dishes[]` (meal mode) or
ignores all but one tile (menu mode), so the UI can only display one
result.

Fix:
- `useScanAnalysis.ts`: include `photoCount: uploadedImages.length` in
  the POST body.
- `analyze/route.ts`: parse the photoCount, clamp to [1, 12] (matches
  the stitcher's 4x3 max grid), log it in REQUEST_RECEIVED, and thread
  it through both inference paths (`buildLocalizedPrompt(..., photoCount)`).
- `ai-prompt.ts`: `buildLocalizedPrompt` now takes an optional photoCount.
  When > 1, a `buildCollageInstruction()` preamble is prepended that:
  - Tells the model the image is a grid of N SEPARATE photos on a white
    background with white padding between tiles.
  - For `meal`: requires one `dishes[]` entry per tile, sets dishCount=N,
    in reading order (left-to-right, top-to-bottom).
  - For `menu`: collects items from ALL N tiles into a single menuItems[]
    array (the user often photographs multi-page menus).
  - For `drink_snack`: schema is single-item, so the model is told to
    pick the most prominent tile and deterministically analyze it.

Single-photo scans are unaffected — the builder signature defaults
photoCount=1 and skips the preamble.

Verified: `npm run build` compiles cleanly.

https://claude.ai/code/session_01BWLU1nKZpxToH5hMeBDusG
Previously the history entry for a meal-mode scan took only dishes[0]'s
name, nutrition, and spikeReduction — so a 2-dish meal appeared in
history as "Chicken Curry" with the nutrition of just the first dish,
and the second dish was lost.

This commit:
- Extends `ScanHistoryEntry` with an optional `dishes: ScanHistoryDish[]`
  field. Each dish carries name + spikeReduction + nutrition (the fields
  a list view would render). Heavier per-dish data (score breakdowns,
  eating-sequence steps) is intentionally omitted to keep localStorage
  usage small — the entry cap is 10, so adding the array keeps total
  size bounded.
- Adds `summarizeMealForHistory()` in useScanAnalysis that:
  * Sums nutrition across all dishes so the top-level `nutrition` field
    reflects the whole plate, not just dish #1.
  * Averages spikeReduction across dishes (matches MealOverview's header).
  * Labels the summary "Pad Thai + 2 more" for multi-dish meals so the
    user can distinguish a multi-photo scan from a single-dish scan.
- Leaves the top-level fields (`foodName`, `nutrition`, `spikeReduction`)
  populated so any existing or future read-only UI still works without
  traversing the `dishes` array.
- `dishes` is optional, so entries written before this change remain
  valid (backward-compatible schema).

Menu and drink_snack modes are unchanged — their server-side schemas
are already single-record-per-scan.

Verified: tsc --noEmit and `npm run build` both pass.

https://claude.ai/code/session_01BWLU1nKZpxToH5hMeBDusG
Drives the production app (https://shinnyguide.autobahn.bot) end-to-end
the way a real user would, in four independent phases:

  1. Landing + locale switch — /th renders, switching to /en works,
     hreflang graph covers all 4 locales + x-default.
  2. Scan flow — uploads a 256x256 noisy PNG (generated in-memory so
     the spec stays self-contained — no binary fixture), clicks the
     "Analyze Now" CTA, waits for the analyze cascade (Workers AI ->
     Gemini) to land on a terminal UI state. The PNG is incompressible
     enough to clear the 500-byte client-side floor in useScanUpload.ts
     and noisy enough that Gemini routes into the not_food branch —
     valid terminal state for the "did the journey complete" question.
  3. Auth round-trip — POSTs /api/auth/register with a fresh unique
     email and no voucher. Verifies the form settles on either an
     inline error banner (voucher_required, the expected path with
     VOUCHER_REQUIRED_FOR_REGISTRATION on) OR a success card, and
     explicitly fails if the Next.js client-side error boundary
     engages — that was caught in the May/Jun 2026 bug-hunt round.
  4. Recipes / chat / dashboard — visits each and verifies a stable
     rendered state (own UI or auth-redirect) without fatal console
     errors. Filters out known-benign noise: /api/auth/me 401
     (fixed in PR #56), Next.js RSC prefetch fallback.

Runtime against warm production: ~10s for all 4 phases. The scan
phase has its own 180s timeout to absorb cold-start Workers AI +
Gemini fallback cascade.

Run:
  cd frontend && npx playwright test tests/e2e/user-journey.spec.ts
Test-results/ + playwright-report/ are local-run artifacts (HTML
reports, trace files, screenshots on failure). Adding them now so the
new tests/e2e/user-journey.spec.ts run doesn't leave dirty
working-tree noise behind.
…ranch

The user-journey spec landed in 5d9d51d, but this branch predates the
Playwright layer (config + devDep + scripts lived only on later
branches). Port playwright.config.ts as-is and add @playwright/test +
test:e2e scripts so 'npm run test:e2e' works here.

Verified: all 4 journey phases pass against production from this
branch (40s, scan phase exercised the cold-path timeout terminal
state and the UI handled it).
…factor-212i2

# Conflicts:
#	frontend/package.json
#	frontend/src/app/api/analyze/route.ts
#	frontend/src/hooks/scan/useScanAnalysis.ts
#	frontend/src/lib/ai-prompt.ts
@bejranonda bejranonda merged commit f0a22bc into main Jun 9, 2026
1 of 5 checks passed
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@bejranonda, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 21 minutes and 13 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 92e024a0-f054-4e26-9df6-821d95a9ea87

📥 Commits

Reviewing files that changed from the base of the PR and between db69caa and d428ff8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • .gitignore
  • frontend/tests/e2e/user-journey.spec.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/code-review-refactor-212i2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

bejranonda pushed a commit that referenced this pull request Jun 9, 2026
Round 12 added tests/e2e/user-journey.spec.ts (PRs #76-#78) — the fifth
testing lens: one spec walking production end-to-end through the
rendered UI. This commit records it everywhere the project documents
its testing posture, approach, and known issues:

- README / README-TH: e2e count 93 → 97 (6 spec files), four-lens →
  five-lens posture, Round 12 summary line, quality-gate table.
- CHANGELOG: full Round 12 entry — phase table, what building the spec
  itself surfaced (intermittent register error-boundary crash, silent
  sub-500-byte file rejection, duplicate-label button trap, benign
  console-noise classes), side-effect budget, test posture.
- RELEASE_NOTES: Round 12 section at top (unreleased).
- CONTRIBUTING: journey-spec section in Testing with run command,
  per-run cost, and the four extension rules.
- docs/GUIDELINE: "The full user-journey lens" — the approach (terminal-
  state assertions, runtime fixtures above client floors, type=submit
  over text selectors, documented-pattern console filtering) and method
  (phase independence, crash sentinels, side-effect budgets, when to
  run). Coverage table updated to 171 unit / 97 e2e.
- docs/ITERATION_PROCESS: §3 notes the spec automates manual checks
  1/3/4/5; §9 four-lens → five-lens table with the Round 12 row and
  the integration-seam lesson.
- docs/KNOWLEDGE_BASE: user-journey row in the Playwright table + a
  Round 12 section with the seven design decisions for extending it.
- docs/KNOWN_ISSUES: new §2a (intermittent client error boundary on
  register response — sentinel-pinned, medium priority) and §5
  (known-benign console-error noise, documented so new specs reuse
  the shared filter instead of rediscovering "flakes").
- docs/claude.md + docs/gemini.md: AI-guide sections pointing at the
  spec, its traps, and the GUIDELINE recipe.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants