Skip to content

e2e: fix Detox suite drift, run against local Worker + local D1 - #111

Merged
tpaulshippy merged 3 commits into
mainfrom
fix/e2e-local-first
Nov 1, 2026
Merged

tpaulshippy merged 3 commits into
mainfrom
fix/e2e-local-first

Conversation

@tpaulshippy

@tpaulshippy tpaulshippy commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Consolidated local-first E2E: supersedes #102 (its seedHelpers.js is included byte-identical, plus a Nova Lite vision row in the seed SQL).

What changed (13 files):

  • front/e2e: 9 specs rewritten around shared REST+D1 helpers (selection injection instead of dead e2eInitialRoute, back-button nav + manual asserts, per-spec self-seeding, timestamped topics)
  • front/e2e/seedHelpers.js: three-way helpers — local wrangler dev via wrangler d1 execute --local, workers.dev via D1 REST, Django via seed-command hints
  • worker/scripts/seed-local-e2e.sql: local-D1 seed (users 60/61, PIN-less accounts, Nova Micro default + Nova Lite vision)
  • front/app/botChat.tsx: e2e image branch resolves a bundled asset to a simulator-local file URI (remote-URL uploads always fail over XHR)
  • front/app/onboarding/WizardStep.tsx: default export silences the dev-warning banner that covered the onboarding CTA

Local runbook (zero Cloudflare traffic):
Terminal 1: copy worker/ aside if needed, add gitignored .dev.vars (JWT_SECRET + AWS/Tavily), npx wrangler dev --port 8787, d1 migrations apply syft-db --local, d1 execute syft-db --local --file=./scripts/seed-local-e2e.sql
Terminal 2: API_BASE_URL=http://127.0.0.1:8787/api npx detox test -c ios.sim.debug

Verification: full suite 22/22 green in one run on local stack (127.0.0.1:8787 + iPhone 16 Pro sim). No deploys, no live-D1 traffic, no secrets committed.


Summary by cubic

Consolidates the Detox E2E suite to run entirely against a local Worker + local D1, so tests require no live Cloudflare traffic or Django backend. All nine specs now self-seed using shared REST/D1 helpers and verify against the real API.

Key changes

  • Added front/e2e/seedHelpers.js with three-way seeding (local wrangler d1 execute --local, workers.dev REST, and Django seed hints) and ported all specs to it.
  • Added worker/scripts/seed-local-e2e.sql seeding the two E2E users and the Nova Lite vision model; ON CONFLICT upserts preserve FK state, and reseeds converge to Nova Micro as default with Nova Lite non-default.
  • In e2e mode, botChat.tsx uploads a bundled asset as a simulator-local file:// URI instead of a remote URL, which always failed; added expo-asset as a dependency.
  • Moved WizardStep.tsx from app/onboarding/ to components/onboarding/ to get it out of the route tree and stop the dev-warning banner from covering the onboarding CTA.
  • Extended the e2e eslint override to all e2e/*.js (fixes 17 no-undef errors) and disabled expo/no-dynamic-env-var for computed env reads.
  • Fixed spec drift: PIN lockout reset, 15s settle after D1 seeding, API-based user-echo polling, timestamped topics, and gating D1-only setup behind isWorkerBase for Django compatibility.
  • Reset the usage quota after 06's warm-up so a seeded account stays under the daily cap, and hardened 04's transcript proof (normalized unwrapped inbox arrays, message-count subtitle plus mounted list).

Written for commit 45ced0d. Summary will update on new commits.

View guided diff Turn on auto-fix

Superset of PR102 + spec fixes; verified green (9/9 files, 22/22 tests)
on a fully local stack: wrangler dev (PR97 worker code) + --local D1 +
iOS simulator, API_BASE_URL=http://127.0.0.1:8787/api. No --remote, no
*.workers.dev, no CLOUDFLARE_* tokens, no D1 REST.

- front/e2e/{01..07,_smoke,chatImageUpload}.e2e.js: consolidated specs,
  ported to PR102 seedHelpers API (authed token-first, tokensManifest
  {profile,bot}, ensureSubscriptionLevel(level), resetOnboardingFresh
  (access), ensureBot {params} with explicit default-model wiring).
- front/e2e/seedHelpers.js: PR102's local-first version verbatim (d1 via
  local wrangler; workers.dev REST path untouched/unused).
- worker/scripts/seed-local-e2e.sql: PR102's seed + Nova Lite vision row
  (Bedrock 400s image blocks on text-only Nova Micro; chatImageUpload
  needs it for the assistant-reply tail).
- front/app/botChat.tsx: e2e bundled-asset image branch (simulator-local
  file:// upload, no picsum remote).
- front/app/onboarding/WizardStep.tsx: default export (silences the dev
  banner covering the onboarding CTA in Detox runs).

Local-first spec fixes: 02 resets D1 lockout pre-PIN; 04 settles 15s
after its D1 storm (rapid local-exec subprocesses ECONNRESET the next
API fetch) and wipes non-review chats (row-0 inbox fixture must beat
leftover suite chats); 06 polls the user echo via API (cold first-send
can drop the optimistic bubble); 07 taps drawer-item-flashcards when
already visible (open drawer covers the menu button).

Worker runtime comes from PR #97 until merged; this supersedes #102
(do not merge that PR). Local runbook: cp PR97 worker to /tmp,
ALLOW_E2E_FAKE_STREAM=true + .dev.vars there, wrangler dev :8787,
migrations + seed --local, Metro with EXPO_PUBLIC_API_BASE_URL, detox
-c ios.sim.debug. NOTE: front/e2e d1() resolves wrangler via PATH and
uses worker/.wrangler state, so point both at the live /tmp runtime
(symlink, gitignored-but-untracked by design).

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 13 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread front/e2e/07-spaced-repetition.e2e.js Resolved
Comment thread front/e2e/02-pin-reauth.e2e.js Resolved
Comment thread front/e2e/seedHelpers.js Resolved
Comment thread front/e2e/07-spaced-repetition.e2e.js Resolved
Comment thread front/e2e/07-spaced-repetition.e2e.js Resolved
Comment thread front/e2e/seedHelpers.js Resolved
Comment thread front/e2e/01-teen-login.e2e.js Resolved
Comment thread worker/scripts/seed-local-e2e.sql Resolved
Comment thread front/app/botChat.tsx Resolved
Comment thread front/e2e/01-teen-login.e2e.js Resolved
Lint: extend the e2e eslint override to all e2e/*.js (seedHelpers had
17 no-undef errors); turn off expo/no-dynamic-env-var there (computed
process.env key dodges babel-preset-expo's expo/virtual/env rewrite
under jest); drop unused imports in 01/02/05. Lint: 17 errors -> 0.

Review fixes (all triaged VALID except 06:271, proven invalid below):
- Django compat: gate D1-only setup (resetPinLockoutState,
  resetOnboardingFresh/deleteAllChatsD1, ensureSubscriptionLevel) behind
  isWorkerBase in 01/02/03/04/05/06/07/chatImageUpload.
- seedHelpers d1Cloudflare honors CLOUDFLARE_WORKER_API_TOKEN alias.
- ensurePin with currentPin now always clears+re-sets (guarantees PIN);
  02 passes current PIN + D1-clears unknown leftovers on Worker.
- ensureBot PATCHes existing bots with opts.params (rerun drift fix).
- tokensManifest fails fast on API_BASE_URL vs EXPO_PUBLIC_API_BASE_URL
  mismatch (app keys tokens by the Expo base).
- 01: delegated-login URL via normalized apiBase() (no //login).
- 03: refusal-test timeout 120s -> 180s (poll budget is 150s).
- 04: transcript test asserts the actually opened chat, not the
  Worker-only fractions fixture.
- 06: real-path bot gets enable_web_search:true; warm-up turn settles
  cold-start remounts; quota resets (new resetDailyUsageD1 helper
  mirroring worker resetDailyUsage, placed post-launch because app boot
  rewrites the account timezone and any tz flip voids the baseline);
  same post-launch reset in 03/05/chatImageUpload.
- 07: wait for chat screen before backToDrawer in beforeAll.
- WizardStep moved app/onboarding -> components/onboarding (out of the
  route tree), 4 named imports + test updated, default export dropped.
- botChat: expo-asset declared in package.json, download error logged.
- seed-local-e2e.sql: ON CONFLICT upserts (REPLACE wiped FK state);
  Nova Lite upserted by model_id (capabilities converge to text+image).

06:271 (require non-empty persisted assistant text after cancel) is
proven invalid on Worker: turn.ts persists the assistant row ONLY on
full completion; a client abort cancels the handler (3 cancel runs, 0
assistant rows). Replaced with persisted-user-echo poll (strongest
deterministic server-side assertion for a cancelled turn).

Verified on local stack only (127.0.0.1:8787 wrangler dev + --local D1
+ simulator; no live-Cloudflare traffic): _smoke, 01 (4/4), 02 (5/5),
03 (2/2), 04 (2/2), 05 (3/3), 06 (2/2), 07 (2/2), chatImageUpload (1/1)
all green; lint 0 errors; tsc clean.
@tpaulshippy

Copy link
Copy Markdown
Owner Author

@cubic-dev-ai incremental review

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 1, 2026

Copy link
Copy Markdown

@cubic-dev-ai incremental review

@tpaulshippy I’ve started reviewing the changes since the last completed review. I’ll post the results here when it’s done.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 22 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread front/e2e/06-streaming-chat.e2e.js Resolved
Comment thread front/e2e/04-parent-review.e2e.js Resolved
Comment thread front/e2e/04-parent-review.e2e.js Resolved
Comment thread worker/scripts/seed-local-e2e.sql Resolved
…ns, seed default invariant)

- 06 (P1): reset the usage baseline again after the warm-up echo poll
  and before the measured prompt — the warm-up alone can push a
  level-0-seeded account over the daily cap. No relaunch in between, so
  the timezone (and baseline) is stable.
- 04 (P2): transcript preflight normalizes inbox.results || inbox || [],
  exactly like beforeAll, so a Django response without a results wrapper
  reads as the array instead of an empty inbox.
- 04 (P2): post-navigation proof the transcript rendered assistant
  history via stable native surface only — exact API message count in
  the subtitle ('Read only · N messages') + mounted transcript list —
  alongside the existing unique user strings and API assistant checks
  (assistant bubbles are WebView-rendered, never matchable).
- seed-local-e2e.sql (P2): explicit default-model invariant — clear any
  old default, set Nova Micro default, force Nova Lite non-default,
  all idempotent on reseed. Verified: pre-set Lite default, reseeded
  twice, flags converge to Micro=1/Lite=0 and stay.

Verified local-only (127.0.0.1:8787 wrangler dev + --local D1 +
simulator): 06 2/2, 04 2/2 green; node --check + eslint clean on
touched files; full front lint 0 errors.
@tpaulshippy

Copy link
Copy Markdown
Owner Author

@cubic-dev-ai incremental review

@cubic-dev-ai

cubic-dev-ai Bot commented Nov 1, 2026

Copy link
Copy Markdown

@cubic-dev-ai incremental review

@tpaulshippy I’ve started reviewing the changes since the last completed review. I’ll post the results here when it’s done.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 3 files (changes from recent commits).

View guided diff | Turn on auto-fix | Re-trigger cubic

@tpaulshippy
tpaulshippy merged commit 994ff7a into main Nov 1, 2026
5 checks passed
@tpaulshippy
tpaulshippy deleted the fix/e2e-local-first branch November 1, 2026 17:38
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.

1 participant