Skip to content

test(playwright): stabilize flaky full-flow E2E — root-cause 3 races (ClickUp 86caj0zzq)#130

Merged
AquiGorka merged 2 commits into
mainfrom
fix/stabilize-playwright-full-flow
Jul 3, 2026
Merged

test(playwright): stabilize flaky full-flow E2E — root-cause 3 races (ClickUp 86caj0zzq)#130
AquiGorka merged 2 commits into
mainfrom
fix/stabilize-playwright-full-flow

Conversation

@AquiGorka

Copy link
Copy Markdown
Contributor

What

Stabilize the flaky full-flow Playwright browser E2E (playwright/tests/full-flow.spec.ts) so it's a reliable gate. The flakes moved between steps per run (observed 13, 5, 12); each is root-caused to a specific race/setup bug and replaced with a condition-based wait on the real signal. No app/product code changes — the diff is the spec only, plus a harness version bump.

ClickUp 86caj0zzq.

Race inventory + fixes

Step 13 — async settlement read race. The POS #pay-instant-btn calls /pay/instant/execute, which writes the merchant's IN/COMPLETED transaction row only after the bundle settles on chain (provider-platform executor 5s + verifier 10s ticks; the endpoint polls up to 120s). Step 12 returns in ~5s because it reads #pos-status mid-flight ("Processing payment…", non-error) — it never waits for settlement. The old Step 13 did a fixed waitForTimeout(5000) + a single-shot fetchCompletedInboundStroops read, which raced the projection and saw 0 stroops.
→ Replaced with a bounded expect.poll(fetchCompletedInboundStroops › 0) (150s timeout / 2s interval — just above the endpoint's own 120s cap). UI smoke made condition-based (expect(#balance-display,#tx-list).toBeVisible).

Step 5 — post-Step-4 navigation handoff race. Step 4's "Next" runs registerPp (network) then navigate("/setup/join"). Step 4 ended on a flaky waitForLoadState("networkidle"), which can resolve mid-navigation; under load the nav outran Step 5's 5s isVisible check, so Step 5 fell back to the fragile /home path (listPps()→badge→modal #jc-url async chain) and timed out. Trace confirmed the provider page went /setup/fund → /setup/join → /home; DB confirmed registration succeeded (pure nav-timing).
→ Step 4 now waits for the join view's #council-url to render (the real post-condition), so Step 5 deterministically starts on the join view.

Step 4 — under-funded bundle fee-payer (deterministic, was masked). The PP root account is the fee-payer for every bundle (executor.process.ts: feePayerPubkey = ppPublicKey). provider-platform's pre-flight requires the fee-payer to hold ≥ NETWORK_FEE (1_000_000_000 stroops = 100 XLM, canonical across all docker suites) + resource fee after reserves. Step 4 funded it with only 10 XLM, so every bundle terminal-FAILed with Insufficient fees on fee-payer account and the payment never settled — previously hidden by Step 13's old loose UI-text assertion. The green e2e/pos-instant suites fund the PP via friendbot (~10k XLM).
→ Step 4 funds the PP with 500 XLM (well above the 100 XLM floor). NETWORK_FEE left canonical; no product/config change.

No masking

No test.retries, test.skip/quarantine, .only, or blanket timeout: inflation used as the fix — grep-clean. Each fix waits on an actual condition/signal.

Stability evidence

./test.sh playwright run 10/10 consecutive green, 0 flakes (each ~6.3–6.9m, all 16/16 including Steps 13b + 14 which were previously blocked by the Step 13 failure).

Notes

  • Diff: playwright/tests/full-flow.spec.ts only + playwright/package.json version bump (0.1.0 → 0.1.1). No app-code files touched.
  • deno fmt/deno lint don't apply to playwright/ (Node/Playwright TS project, extensionless imports, excluded from the fmt-lint CI matrix); the suite is compiled by Playwright's own tooling.

AquiGorka added 2 commits July 3, 2026 16:18
…Up 86caj0zzq)

Make the full-flow browser suite a reliable gate by replacing timing-fragile
waits/setup with condition-based waits on the real signals. No app-code changes.

- Step 13 (async settlement): replace fixed waitForTimeout(5000) + single-shot
  fetchCompletedInboundStroops read with a bounded expect.poll on the real
  projection (>0, 150s/2s). /pay/instant/execute writes the merchant's
  IN/COMPLETED row only after on-chain settlement, well after Step 12's
  mid-flight POS status — the old read raced it.
- Step 5 (nav handoff): Step 4's Next registers the PP then navigates to
  /setup/join; wait for the join view's #council-url to render instead of a
  flaky networkidle, so Step 5 starts deterministically on the join view
  rather than racing the in-flight nav onto the fragile /home fallback.
- Step 4 (fee-payer funding): fund the PP root account (bundle fee-payer) with
  500 XLM instead of 10. provider-platform's pre-flight requires the fee-payer
  hold >= NETWORK_FEE (100 XLM); 10 XLM deterministically failed every bundle
  with InsufficientFees (masked before by the old loose Step 13 assertion).
  The green e2e/pos-instant suites fund the PP via friendbot (~10k XLM).

Verified: 10/10 consecutive green runs of ./test.sh playwright, 0 flakes.
@AquiGorka AquiGorka merged commit ef911b3 into main Jul 3, 2026
10 checks passed
@AquiGorka AquiGorka deleted the fix/stabilize-playwright-full-flow branch July 3, 2026 19:35
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