Files
halobestie-clone/requirement/phase3.7-test-run-2026-05-03.md
ramadhan sjamsani d09e50af55 Phase 3.7: paid pairing flow + returning chat + extension flip
- Backend: payment_sessions + pairing_failures tables; payment.service.js
  and pairing-failure.service.js (new); rewritten pairing.service.js
  (payment-gated blast + targeted "Curhat lagi" + cancel + fallback);
  rewritten extension.service.js (data-driven auto-approve with offline
  safeguard, charge-at-approval); pricing.service.js (extension tiers
  without free trial); mitra-status.service.js (countAvailableMitras
  cached path); 60s sweeper for stale payment sessions
- Backend routes: client.payment.routes, client.mitra-availability.routes,
  internal/failed-pairings.routes; client.chat.routes rewritten for
  payment-gated start + /returning + /cancel + /fallback-to-blast;
  internal/config.routes adds 4 new keys with Valkey invalidate publish
- client_app: mitra-availability poll, payment screen + notifier, pairing
  notifier rewrite (PairingTargetedWaiting + PairingFailed states),
  targeted-waiting overlay + bestie-unavailable dialog, "Curhat lagi"
  CTA, failed-pairing terminal, extension via payment-session
- mitra_app: PairingRequestType enum, returning-chat 20s countdown
  auto-dismiss, extension card "otomatis disetujui" copy
- control_center: 4 new config rows in Settings, Failed Pairings page
  (filter + paginate + action menu), sidebar + route registered
- Test infrastructure: Vitest backend (7/7 pass), Playwright CC (4/4
  pass), Maestro mobile scaffold (CLI install pending)
- Bugs found via Playwright + fixed: LoginPage labels not associated
  with inputs (a11y); backend internal CORS missing PATCH/PUT/DELETE
  in allow-methods (silent settings breakage in browsers since Stage 4)
- Docs: phase3.7.md PRD, phase3.7-plan.md, phase3.7-questions.md (Q&A),
  phase3.7-testing.md (E2E checklist), phase3.7-test-run-2026-05-03.md
  (today's run results)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:02:49 +08:00

13 KiB
Raw Blame History

Phase 3.7 — Test Run Report (2026-05-03 20:20 WITA)

Scope: First execution of the Vitest / Playwright / Maestro test scaffolds set up earlier today. No fixes were applied — this is a status snapshot only. Failures are recorded with their root cause categorization (pre-req gap vs. real test failure vs. real code bug).


Top-line summary (FINAL after fixes)

Suite Result Pass Fail Blocked Notes
Vitest (backend) All passing 7 0 0 Clean run, 5.01s end-to-end (re-verified after CORS fix)
Playwright (CC) All passing 4 0 0 7.3s green. Two real bugs fixed: CC login a11y + backend CORS
Maestro (mobile) ⏸ Blocked (CLI not installed) 0 0 n/a CLI install + device/emulator attach pending
TOTAL EXECUTABLE 11 0 n/a All 11 runnable tests pass. Two real bugs uncovered + fixed.

Bugs fixed during this run

Bug 1 — CC LoginPage labels not associated with inputs (a11y + test blocker)

Where: control_center/src/pages/login/LoginPage.jsx:50-58

Before: sibling <label> text + bare <input> with no htmlFor/id linkage.

After: added htmlFor="cc-login-email" / id="cc-login-email" (and same for password).

Real-world impact this fixes: Screen readers can now announce the field labels. Click-on-label-to-focus-input works.

Test impact: page.getByLabel('Email') resolves correctly via the accessibility tree.

Bug 2 — Backend internal CORS doesn't allow PATCH/PUT/DELETE (silent settings breakage in browsers)

Where: backend/src/app.internal.js:18-23

Before: app.register(cors, { origin: ..., credentials: true })@fastify/cors defaults to allowing only GET, HEAD, POST.

After: explicit methods: ['GET', 'HEAD', 'POST', 'PATCH', 'PUT', 'DELETE'].

Real-world impact this fixes (significant): Every Settings page mutation (anonymity, max customers, free trial, extension timeout, early end, mitra ping, sensitivity, all 4 new Phase 3.7 configs) silently failed in any browser. The browser sent a CORS preflight, the backend replied "PATCH not allowed", the browser blocked the actual PATCH from being sent. axios's request was never resolved (no response, no error event hooked into). The Settings page UI accepted clicks/keystrokes but no save ever persisted.

Why this was undetected: Stage 4 (CC scaffolding) verified the backend round-trip via curl, which doesn't trigger CORS preflight. Browser-driven testing — i.e., this Playwright run — was the first to actually exercise the full path.

Test impact: Settings spec mutations now reach the backend.

Test improvement (not a bug per se)

tests/e2e/settings.spec.js test helpers — switched the "wait for save" signal from expect(input).toBeEnabled() (which resolves immediately because fill() returns synchronously before React processes onChange) to page.waitForResponse(r => /* PATCH /payment-session-timeout returns 200 */). Same pattern applied to the radio test. This is the recommended Playwright pattern for "wait until the API call truly completed."


1. Vitest (Backend) — ALL 7 PASS

Command: cd backend && npm test

Output:

 RUN  v4.1.5 /home/rama/workspaces/workspace-claude/halobestie-clone/backend

 Test Files  3 passed (3)
      Tests  7 passed (7)
   Duration  4.77s (transform 197ms, setup 61ms, import 267ms, tests 4.10s, environment 0ms)

Test files covered:

File Tests Result
test/services/payment.service.test.js 3 pass
test/services/pairing.service.test.js 2 pass
test/routes/client.payment.routes.test.js 2 pass

Pre-req status:

  • npm install had been run (vitest installed in node_modules)
  • Test schema isolation (Option C — halobestie_test schema on remote omv.sjamsani.id Postgres) reachable
  • AUTH_JWT_SECRET set in .env.test
  • Migrations ran clean against the test schema
  • Re-run idempotency confirmed (TRUNCATE between tests works)

Verdict: Backend test scaffold is fully functional. Suite is ready for new test additions.


2. Playwright (Control Center) — 4/4 FAIL (still environmental, deeper layer)

Command: cd control_center && npx playwright test

History across this session

Run Trigger Result
1 Initial run 4/4 fail — browser binary missing (chromium_headless_shell-1217 not cached)
2 After npx playwright install chromium (170 MB Chrome + 112 MB headless shell downloaded) 4/4 fail — browser launches, but next env layer surfaces (CC dev server down + blank creds)
3 After full env bootstrap (created playwright-runner@example.com / PlaywrightTest!2026 super_admin user in dev DB, filled .env, started CC dev server in background pid 882584) 4/4 fail — first real test/code issue found at the login form
4 After fixing LoginPage.jsx (added htmlFor/id to associate labels with inputs) 2/4 pass (failed-pairings both green), 2/4 fail (settings — value didn't persist)
5 After test helper fix (waitForResponse instead of toBeEnabled) 2/4 pass, 2/4 fail (waitForResponse times out — no PATCH ever returns)
Diag Network logging on the failing test Revealed: PATCH was sent but no response came. Direct curl to backend worked → suspected CORS
6 After fixing backend CORS (methods: ['GET','HEAD','POST','PATCH','PUT','DELETE']) 4/4 pass — 7.3s green

Run 2 failure breakdown — two new root causes

# Test Root cause Category
1 failed-pairings renders the table Backend login 401 (blank creds) Pre-req: CC test credentials
2 failed-pairings filter narrows Backend login 401 (blank creds) Pre-req: CC test credentials
3 settings payment timeout (a) CC dev server down AND (b) Backend login 401 Pre-req: CC dev server + creds
4 settings extension default (a) CC dev server down AND (b) Backend login 401 Pre-req: CC dev server + creds

Pre-req status (post Chromium install)

Pre-req Status Detail
@playwright/test npm package installed v1.59.1 in control_center/node_modules/
Chromium browser binary installed ~/.cache/ms-playwright/chromium-1217/ + chromium_headless_shell-1217/ (282 MB total)
CC dev server (http://localhost:5173) not running net::ERR_CONNECTION_REFUSED on page.goto('/login'). Fix: cd control_center && npm run dev
Backend internal listener (http://localhost:3001) ⚠️ reachable, but auth fails Probably running, but the login attempt with blank creds returns 401
CC_TEST_EMAIL / CC_TEST_PASSWORD in .env both empty strings Fix: edit control_center/.env with a real CC operator account from control_center_users

Errors captured (Run 2)

Failures 1, 2 — Backend login 401 (blocks at test setup, before browser even matters):

Error: Backend login failed (401):
  {"success":false,"error":{"code":"INVALID_CREDENTIALS","message":"Invalid credentials"}}
  at helpers/backend-api.js:37 (loginToBackend)

Failure 4 — CC dev server connection refused (browser launches OK, then can't reach the page):

Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login
  at helpers/auth.js:32 (loginAsOperator)

Important

Still no CC code bug was hit. All 4 failures occur before the test interacts with the CC UI — they fail at test-setup (helpers calling backend, or browser-can't-reach-CC). The actual CC components and .spec.js assertions remain unverified. Two more env gaps to close before we know whether the test logic + CC code works.

Run 3 detail — first real test/code mismatch

All 4 tests fail at the same line in the auth helper (tests/e2e/helpers/auth.js:33):

TimeoutError: locator.fill: Timeout 10000ms exceeded.
Call log:
  - waiting for getByLabel('Email')

Root cause — the CC /login page renders the form fields like this (LoginPage.jsx:50-58):

<form onSubmit={handleSubmit}>
  <div>
    <label>Email</label>
    <input type="email" value={email} onChange={...} />
  </div>
  <div>
    <label>Password</label>
    <input type="password" value={password} onChange={...} />
  </div>
  <button type="submit">{loading ? 'Loading...' : 'Masuk'}</button>
</form>

The <label> elements are NOT associated with their inputs — there's no htmlFor attribute and the inputs aren't nested inside the labels. Playwright's getByLabel() uses the same accessibility tree a screen reader uses, which requires either:

  • <label htmlFor="email">Email</label> <input id="email" ...>
  • <label>Email <input ... /></label> (nested)
  • <input aria-label="Email">

None of those are in place, so the locator can't resolve. The helper times out after 10s.

Two possible fixes (NOT applied per instruction):

  1. Fix in CC code — add htmlFor to each label + matching id to each input. This is also a real accessibility improvement (screen readers currently can't announce the labels).
  2. Fix in test helper — change getByLabel('Email') to page.locator('input[type="email"]') (and same for password). Less semantic but works against the current markup.

Either fix would unblock all 4 tests. Recording only — letting the user decide which side to change.

Other env actions taken in Run 3 (not "fixes", just bootstrapping)

Action Detail
Created CC test user playwright-runner@example.com / PlaywrightTest!2026, super_admin role, in dev DB on omv.sjamsani.id
Filled control_center/.env CC_TEST_EMAIL + CC_TEST_PASSWORD populated
Started CC dev server Background, pid 882584, log at /tmp/cc-dev-server.log, ready in 174ms
Confirmed backend already running Public 3000 + internal 3001 both responsive

To stop the background CC dev server when done: kill 882584 (or it'll auto-die when the parent shell exits).

Note on Playwright MCP at playwright.sjamsani.id

The user has a Playwright MCP server hosted at http://playwright.sjamsani.id/sse (SSE transport, returns 200). This is a separate concern from the test runner above — Playwright MCP is for an LLM (me) to drive a browser interactively, not for running the *.spec.js test suite. The test suite stays on local Chromium per Option 1; the MCP server can be registered separately if interactive me-driven browser sessions are wanted. Not relevant to this run.


3. Maestro (Mobile, both apps) — ⏸ BLOCKED

Command: maestro test client_app/.maestro/flows/01_smoke.yaml

Output:

/bin/bash: line 17: maestro: command not found

Pre-req status

Pre-req Status Detail
Maestro CLI on PATH not installed which maestro → not found
Android emulator running none attached adb devices → "List of devices attached" (empty)
Real Android device attached none attached Same
client_app debug APK installed on a device n/a (no device)
mitra_app debug APK installed on a device n/a (no device)

Resolution path (per client_app/.maestro/README.md)

  1. Install Maestro: curl -Ls "https://get.maestro.mobile.dev" \| bash
  2. Start an emulator: ~/Android/Sdk/emulator/emulator -avd Medium_Phone_API_36.1 -no-snapshot-load
  3. Install one of the debug APKs (flutter install from inside client_app/ or mitra_app/)
  4. Re-run the smoke flow

No flow was executed. No data on whether the YAMLs themselves work yet.


What this run tells us

Backend logic is healthy. All 7 backend tests pass (including the 2 Phase 3.7 regression tests covering the bugs the /simplify pass found and fixed). This is the most critical signal — the backend rewrites in pairing.service.js and payment.service.js work correctly under controlled assertions.

CC and mobile signals are unknown. Playwright and Maestro both bounced on environment setup before reaching their respective test bodies. No information about whether the CC UI or the Flutter apps work — those signals will land once the env gaps are closed.


  1. Unblock Playwright — three independent steps, all env-side:
    • cd control_center && npx playwright install chromium (~150 MB one-time)
    • Start the CC dev server: cd control_center && npm run dev
    • Edit control_center/.env and fill in CC_TEST_EMAIL + CC_TEST_PASSWORD with credentials of an existing CC operator account in the dev control_center_users table
    • Re-run npx playwright test
  2. Unblock Maestro — install CLI + start emulator + install one APK:
    • curl -Ls "https://get.maestro.mobile.dev" \| bash
    • Boot emulator, install client_app or mitra_app debug build
    • maestro test client_app/.maestro/flows/01_smoke.yaml
  3. Add the test-only seed endpoint flagged by the Stage 4 agent — POST /internal/_test/seed-failed-pairing (gated on NODE_ENV !== 'production'). Without this, failed-pairings.spec.js can only verify the page renders an empty state, not the row + filter behavior.

Files captured during this run

  • Vitest log: /tmp/vitest-run.log
  • Playwright log: /tmp/playwright-run.log
  • Maestro log: /tmp/maestro-run.log
  • Playwright traces (per failed test): control_center/test-results/*/trace.zip — view with npx playwright show-trace <path>