E2E: fix brittle Anvil wallet-writes ETH test (stale selectors + parallel workers) #87

Closed
opened 2026-04-28 06:26:54 +00:00 by PlasticDigits · 13 comments
PlasticDigits commented 2026-04-28 06:26:54 +00:00 (Migrated from gitlab.com)

Summary

bash scripts/e2e-anvil.sh can fail on frontend/e2e/anvil-wallet-writes.spec.ts — “TimeCurve ETH route: quote, swap, and buy” — even when unrelated frontend changes are fine. Two separate problems show up in practice.

Findings

1. Stale selector vs current Simple UI

The spec waits for:

buyPanel.locator('input[name="timecurve-pay-with"][value="eth"]')

TimeCurve Simple chooses pay mode (CL8Y / ETH / USDM) with toggle buttons (e.g. .timecurve-simple__rate-paywith-btn), aria-pressed, and aria-label like “Show price in ETH” — not <input type="radio" name="timecurve-pay-with">. So the locator can never resolve, and the test hits the 180s timeout waiting for a non-existent control.

Evidence: frontend/src/pages/TimeCurveSimplePage.tsx (rateBoardPayOptions — button elements per asset).

2. Parallel workers vs one Anvil

With ANVIL_E2E=1, frontend/playwright.config.ts uses multiple workers (workers: 5 when CI or ANVIL_E2E is set). scripts/e2e-anvil.sh starts one Anvil + one preview server, then runs e2e/anvil-*.spec.ts across workers in parallel.

Different files (anvil-referrals.spec.ts, anvil-wallet-writes.spec.ts, etc.) share the same chain and default mock account. Even where a file uses test.describe.configure({ mode: "serial" }), that only serializes within that file — not across files. That creates race risk (nonces, sale state, registration state) unrelated to the feature under test.

Acceptance criteria

  • ETH route test targets the actual TimeCurve Simple pay-mode UI (e.g. role/name within the buy panel, or a dedicated data-testid agreed in code + spec). The step that selects ETH must not depend on removed input[name="timecurve-pay-with"] unless that markup is deliberately reintroduced.
  • bash scripts/e2e-anvil.sh completes green reliably with the default worker policy (either by fixing isolation, or by documenting and implementing a supported mode: e.g. --workers=1 for Anvil in the script, fullyParallel: false for anvil-* only, or global setup that serializes Anvil mutating tests).
  • docs/testing/e2e-anvil.md (or docs/testing/strategy.md / Playwright config comment) briefly states why Anvil E2E concurrency is limited the chosen way, so future agents do not reintroduce parallel races.
  • Playwright config: frontend/playwright.config.ts (workers when ANVIL_E2E=1).
  • Anvil entrypoint: scripts/e2e-anvil.sh.
## Summary `bash scripts/e2e-anvil.sh` can fail on **`frontend/e2e/anvil-wallet-writes.spec.ts`** — **“TimeCurve ETH route: quote, swap, and buy”** — even when unrelated frontend changes are fine. Two separate problems show up in practice. ## Findings ### 1. Stale selector vs current Simple UI The spec waits for: ```ts buyPanel.locator('input[name="timecurve-pay-with"][value="eth"]') ``` **TimeCurve Simple** chooses pay mode (CL8Y / ETH / USDM) with **toggle buttons** (e.g. `.timecurve-simple__rate-paywith-btn`), `aria-pressed`, and `aria-label` like “Show price in ETH” — not `<input type="radio" name="timecurve-pay-with">`. So the locator can **never resolve**, and the test hits the **180s timeout** waiting for a non-existent control. **Evidence:** `frontend/src/pages/TimeCurveSimplePage.tsx` (`rateBoardPayOptions` — `button` elements per asset). ### 2. Parallel workers vs one Anvil With **`ANVIL_E2E=1`**, `frontend/playwright.config.ts` uses **multiple workers** (`workers: 5` when `CI` or `ANVIL_E2E` is set). `scripts/e2e-anvil.sh` starts **one** Anvil + one preview server, then runs **`e2e/anvil-*.spec.ts`** across workers **in parallel**. Different files (`anvil-referrals.spec.ts`, `anvil-wallet-writes.spec.ts`, etc.) share the **same chain and default mock account**. Even where a file uses `test.describe.configure({ mode: "serial" })`, that only serializes **within** that file — not **across** files. That creates **race risk** (nonces, sale state, registration state) unrelated to the feature under test. ## Acceptance criteria - [ ] **ETH route test** targets the **actual** TimeCurve Simple pay-mode UI (e.g. role/name within the buy panel, or a dedicated `data-testid` agreed in code + spec). The step that selects **ETH** must not depend on removed `input[name="timecurve-pay-with"]` unless that markup is deliberately reintroduced. - [ ] **`bash scripts/e2e-anvil.sh`** completes green **reliably** with the default worker policy (either by fixing isolation, or by documenting and implementing a supported mode: e.g. `--workers=1` for Anvil in the script, `fullyParallel: false` for `anvil-*` only, or global setup that serializes Anvil mutating tests). - [ ] **`docs/testing/e2e-anvil.md`** (or `docs/testing/strategy.md` / Playwright config comment) briefly states **why** Anvil E2E concurrency is limited the chosen way, so future agents do not reintroduce parallel races. ## Related - Playwright config: `frontend/playwright.config.ts` (`workers` when `ANVIL_E2E=1`). - Anvil entrypoint: `scripts/e2e-anvil.sh`.
PlasticDigits commented 2026-04-28 06:43:44 +00:00 (Migrated from gitlab.com)

mentioned in commit 7b0f37da60

mentioned in commit 7b0f37da6087b13a573269676116e237bce7cc69
PlasticDigits commented 2026-04-28 06:44:00 +00:00 (Migrated from gitlab.com)

Implemented in 7b0f37d on main.

What changed

Verification checklist for @brouie

  • bash scripts/e2e-anvil.sh green (Foundry + frontend deps); Playwright output shows Running N tests using 1 worker when ANVIL_E2E=1.
  • In DevTools, Simple Buy CHARM panel: ETH toggle has data-testid="timecurve-simple-paywith-eth" and aria-pressed when selected.
  • ETH E2E path: after toggling ETH, quoted field is labeled Quoted ETH spend… and leaves … once the quoter returns; Buy CHARM enables (or shows Refreshing quote… then enables per #56).
  • npm run test:e2e in CI path (no Anvil) unchanged intent: can still use multiple workers — only ANVIL_E2E=1 forces serial.

Issue left open for your sign-off.

Implemented in `7b0f37d` on `main`. **What changed** - **ETH route E2E** now drives the real Simple pay-mode **toggles**: stable hooks `data-testid="timecurve-simple-paywith-{cl8y,eth,usdm}"` on **TimeCurve Simple** and **Arena** (same CSS, shared test ids). The spec uses `getByTestId("timecurve-simple-paywith-eth")` and waits on the **quoted ETH** control (`aria-label` *Quoted ETH spend for the selected CL8Y target*) and the quote field clearing the `…` loading state — not removed `input[name="timecurve-pay-with"]` or the old “Quote: spend” copy. - **Playwright / one Anvil:** when `ANVIL_E2E=1`, config uses **`workers: 1`** and **`fullyParallel: false`** so `e2e/anvil-*.spec.ts` files do not race a single chain + mock account. Default CI `test:e2e` (no Anvil) still uses **5** workers. - **Docs:** [docs/testing/e2e-anvil.md#anvil-e2e-concurrency-gitlab-87](docs/testing/e2e-anvil.md#anvil-e2e-concurrency-gitlab-87), [docs/testing/invariants-and-business-logic.md#anvil-e2e-playwright-concurrency-and-pay-mode-selectors-issue-87](docs/testing/invariants-and-business-logic.md#anvil-e2e-playwright-concurrency-and-pay-mode-selectors-issue-87), [docs/testing/strategy.md](docs/testing/strategy.md) Stage 1; contributor guardrail in `.cursor/skills/yieldomega-guardrails/SKILL.md` item 25; play skill [skills/verify-yo-e2e-anvil/SKILL.md](skills/verify-yo-e2e-anvil/SKILL.md) + index in [skills/README.md](skills/README.md). **Verification checklist for @brouie** - [ ] `bash scripts/e2e-anvil.sh` green (Foundry + `frontend` deps); Playwright output shows **Running N tests using 1 worker** when `ANVIL_E2E=1`. - [ ] In DevTools, Simple **Buy CHARM** panel: ETH toggle has `data-testid="timecurve-simple-paywith-eth"` and `aria-pressed` when selected. - [ ] ETH E2E path: after toggling ETH, quoted field is labeled *Quoted ETH spend…* and leaves `…` once the quoter returns; **Buy CHARM** enables (or shows *Refreshing quote…* then enables per #56). - [ ] `npm run test:e2e` in CI path (no Anvil) unchanged intent: can still use multiple workers — only `ANVIL_E2E=1` forces serial. Issue left **open** for your sign-off.
Brouie commented 2026-04-29 02:12:08 +00:00 (Migrated from gitlab.com)

Verified 7b0f37d against all 4 acceptance items. Closing.

Item 1 - bash scripts/e2e-anvil.sh green with workers:1

Ran bash scripts/e2e-anvil.sh on the QA server (anvil + frontend build + Playwright). Output snippet:

Running 5 tests using 1 worker

PASS 1 [chromium] anvil-collection.spec.ts:15:3 collection page is under construction (700ms)
PASS 2 [chromium] anvil-referrals.spec.ts:26:3 connected wallet: register code, share links, copy to clipboard (1.3s)
PASS 3 [chromium] anvil-timecurve.spec.ts:21:3 Arena TimeCurve page shows onchain panel without read errors (1.2s)
PASS 4 [chromium] anvil-wallet-writes.spec.ts:21:3 TimeCurve approve and buy (CL8Y) (815ms)
PASS 5 [chromium] anvil-wallet-writes.spec.ts:47:3 TimeCurve ETH route: quote, swap, and buy (1.1s)

5 passed (6.4s)
Done.

The previously brittle TimeCurve ETH route case at line 47 (the one that was hitting 180s timeout on the stale input[name=timecurve-pay-with] selector) now passes in 1.1s. All 5 anvil-* spec files run serially under 1 worker as designed.

Item 2 - data-testid hooks present + aria-pressed driven

Source confirmed in two places sharing the same testid pattern (per dev's note that Simple and Arena use the same hook):

src/pages/timeCurveArena/TimeCurveArenaView.tsx:217: data-testid={timecurve-simple-paywith-${key}}
src/pages/TimeCurveSimplePage.tsx:483: data-testid={timecurve-simple-paywith-${key}}

The E2E test that just passed (anvil-wallet-writes.spec.ts:47:3) exercises this hook live by calling getByTestId(timecurve-simple-paywith-eth) and asserts on the aria-pressed state, so the live behavior is implicitly verified by the green test.

Item 3 - ETH E2E path: quoted field labeled correctly + leaves loading state

Same case anvil-wallet-writes.spec.ts:47:3 (1.1s) covers this end-to-end. Test passes, which means the quoted field's aria-label and loading-state-clearing are both behaving per spec. The case literally drives the timecurve-simple-paywith-eth toggle, waits for the quoter, and asserts the buy flow - all green.

Item 4 - Default test:e2e (no Anvil) preserves multi-worker behavior

Source confirmed in frontend/playwright.config.ts:

const isAnvilE2E = process.env.ANVIL_E2E === 1;
fullyParallel: !isAnvilE2E,
workers: isAnvilE2E ? 1 : process.env.CI ? 5 : undefined,

So default CI path with no ANVIL_E2E flag: workers 5 (CI) or undefined (local). Only ANVIL_E2E=1 forces serial. Matches dev's intent in the issue note.

Sign-off

All 4 acceptance items verified. Closing.

cc @PlasticDigits

Verified 7b0f37d against all 4 acceptance items. Closing. ## Item 1 - bash scripts/e2e-anvil.sh green with workers:1 Ran bash scripts/e2e-anvil.sh on the QA server (anvil + frontend build + Playwright). Output snippet: Running 5 tests using 1 worker PASS 1 [chromium] anvil-collection.spec.ts:15:3 collection page is under construction (700ms) PASS 2 [chromium] anvil-referrals.spec.ts:26:3 connected wallet: register code, share links, copy to clipboard (1.3s) PASS 3 [chromium] anvil-timecurve.spec.ts:21:3 Arena TimeCurve page shows onchain panel without read errors (1.2s) PASS 4 [chromium] anvil-wallet-writes.spec.ts:21:3 TimeCurve approve and buy (CL8Y) (815ms) PASS 5 [chromium] anvil-wallet-writes.spec.ts:47:3 TimeCurve ETH route: quote, swap, and buy (1.1s) 5 passed (6.4s) Done. The previously brittle TimeCurve ETH route case at line 47 (the one that was hitting 180s timeout on the stale input[name=timecurve-pay-with] selector) now passes in 1.1s. All 5 anvil-* spec files run serially under 1 worker as designed. ## Item 2 - data-testid hooks present + aria-pressed driven Source confirmed in two places sharing the same testid pattern (per dev's note that Simple and Arena use the same hook): src/pages/timeCurveArena/TimeCurveArenaView.tsx:217: data-testid={timecurve-simple-paywith-${key}} src/pages/TimeCurveSimplePage.tsx:483: data-testid={timecurve-simple-paywith-${key}} The E2E test that just passed (anvil-wallet-writes.spec.ts:47:3) exercises this hook live by calling getByTestId(timecurve-simple-paywith-eth) and asserts on the aria-pressed state, so the live behavior is implicitly verified by the green test. ## Item 3 - ETH E2E path: quoted field labeled correctly + leaves loading state Same case anvil-wallet-writes.spec.ts:47:3 (1.1s) covers this end-to-end. Test passes, which means the quoted field's aria-label and loading-state-clearing are both behaving per spec. The case literally drives the timecurve-simple-paywith-eth toggle, waits for the quoter, and asserts the buy flow - all green. ## Item 4 - Default test:e2e (no Anvil) preserves multi-worker behavior Source confirmed in frontend/playwright.config.ts: const isAnvilE2E = process.env.ANVIL_E2E === 1; fullyParallel: !isAnvilE2E, workers: isAnvilE2E ? 1 : process.env.CI ? 5 : undefined, So default CI path with no ANVIL_E2E flag: workers 5 (CI) or undefined (local). Only ANVIL_E2E=1 forces serial. Matches dev's intent in the issue note. ## Sign-off All 4 acceptance items verified. Closing. cc @PlasticDigits
Brouie (Migrated from gitlab.com) closed this issue 2026-04-29 02:12:10 +00:00
Brouie commented 2026-04-29 04:58:45 +00:00 (Migrated from gitlab.com)

mentioned in issue #58

mentioned in issue #58
PlasticDigits commented 2026-04-30 05:25:09 +00:00 (Migrated from gitlab.com)

mentioned in issue #100

mentioned in issue #100
PlasticDigits commented 2026-05-01 06:11:33 +00:00 (Migrated from gitlab.com)

mentioned in issue #104

mentioned in issue #104
PlasticDigits commented 2026-05-03 07:10:36 +00:00 (Migrated from gitlab.com)

mentioned in issue #113

mentioned in issue #113
PlasticDigits commented 2026-05-04 08:36:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #135

mentioned in issue #135
PlasticDigits commented 2026-05-29 03:24:01 +00:00 (Migrated from gitlab.com)

mentioned in issue #263

mentioned in issue #263
PlasticDigits commented 2026-06-02 13:36:01 +00:00 (Migrated from gitlab.com)

mentioned in merge request !401

mentioned in merge request !401
PlasticDigits commented 2026-06-19 11:55:55 +00:00 (Migrated from gitlab.com)

mentioned in merge request !515

mentioned in merge request !515
PlasticDigits commented 2026-06-19 13:31:44 +00:00 (Migrated from gitlab.com)

mentioned in issue #340

mentioned in issue #340
PlasticDigits commented 2026-06-26 09:11:08 +00:00 (Migrated from gitlab.com)

mentioned in merge request !526

mentioned in merge request !526
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#87
No description provided.