Skip to content

test(ci): run both frontend suites, and repair what that turned up - #201

Merged
0xdevcollins merged 1 commit into
mainfrom
claude/vibrant-sanderson-43731c
Aug 9, 2026
Merged

0xdevcollins merged 1 commit into
mainfrom
claude/vibrant-sanderson-43731c

Conversation

@0xdevcollins

Copy link
Copy Markdown
Owner

Neither apps/dashboard nor apps/checkout ran tests in CI. The dashboard's suite had been broken on main for some time and the checkout's had never executed at all — which is how nineteen call sites kept a doubled /v1 prefix until someone opened a network panel. The assertions that would have caught it were sitting in files no runner touched.

Both now run in a test-frontend job. Getting there took three infrastructure fixes and four stale-assertion sites.

Dashboard — infrastructure

@useroutr/ui failed to resolve (2 suites never collected). The alias existed but pointed one level short: ../packages/ui/src from apps/dashboard resolves to apps/packages. Moved to array form with regex find entries so subpath imports (@useroutr/ui/globals.css) aren't rewritten onto the index file.

Target container is not a DOM element (5 tests) was not an environment problem — jsdom was configured correctly and the sibling suite passed. The test spied on document.createElement with a blanket mockReturnValue, so the createElement('div') that testing-library's render() makes for its own container got the fake anchor instead. Same for document.body.appendChild. Both spies now pass through anything that isn't the download anchor, plus an afterEach to restore them — they wrap the live document, so without it each test layered another spy on the previous one's.

Dashboard — assertions that encoded real behaviour changes

Test Was Now
PayoutStatusBadge ×5 toHaveClass("pending"|"processing"|…) The tone name is no longer emitted as a class — BrandStatusBadge maps it to Tailwind tokens. Each status asserts the pair it actually resolves to: PENDING → bg-warning/10 text-warning, CANCELLED → bg-secondary text-muted-foreground (shared with neutral, deliberately).
usePayouts ×4 /v1/payouts, /v1/payouts/:id/retry|cancel /payouts, /payouts/:id/retry|cancel. api.ts sets BASE_URL = ${origin}/v1, so callers pass bare paths — these were the pre-fix paths.
BatchGroupHeader ×1 getByText('(1)') Threw on multiple matches: three payouts with three distinct statuses render three chips. Now asserts all three plus each status label. Surfaced only because the suite finally loaded.

Nothing was loosened or skipped.

Checkout

No test script, no runner, no config — vitest, jsdom and testing-library added to match the dashboard's versions.

The test file had drifted much further than the missing runner suggested. It was written against a CryptoPayment with a "Select network" / "Select token" two-step picker over ETH/BASE/BNB and USDC/USDT/ETH. The current component has no token picker at all, and offers Stellar plus the five CCTP chains under "Send USDC from". Assertions rewritten against the shipped component; QuoteCountdown's three were still accurate and pass unchanged.

The mocks were stale to match — wagmi's useConnect/useBalance and a useQuote hook the component doesn't use were mocked, while useChainId, useSendTransaction, @rainbow-me/rainbowkit and next/navigation weren't. It also wrapped renders in WagmiProvider while mocking wagmi without exporting it, which would have rendered undefined. Provider dropped (every wagmi hook is mocked); QueryClientProvider stays and the two crypto hooks are left real so they exercise their own wiring against a mocked api client.

Not done

packages/types has no test script and no test files on main, so there was nothing to wire up — a job for it would fail immediately. Its tests and a test-packages job live on fix/single-v1-prefix (237b615), unmerged. I test-merged that branch against this one: no conflicts, and the two jobs compose.

Verification

From a clean npm ci:

  • apps/dashboard — 6 files, 38 tests, all passing
  • apps/checkout — 1 file, 8 tests, all passing
  • npm run lint clean in both apps (0 errors; pre-existing warnings only)
  • next build still passes for checkout, since vitest.config.ts lands inside its tsconfig include glob
  • Workflow YAML parses; jobs are lint, test-api, test-frontend, test-contracts-soroban, build

One thing left alone: CancelConfirmationModal nests a <div> and <p> inside Radix's description <p>, which React warns is a hydration risk. The suite passes — it's invalid markup, not a test failure — so it belongs in its own change.

🤖 Generated with Claude Code

Neither apps/dashboard nor apps/checkout ran tests in CI. The dashboard's
suite had been broken on main for some time and the checkout's had never
executed at all, which is how nineteen call sites kept a doubled /v1 prefix
until someone opened a network panel — the assertions that would have caught
it were sitting in files no runner touched.

Both now run in a `test-frontend` job. Three infrastructure faults and four
stale-assertion sites had to be fixed to get there.

Dashboard, infrastructure:

`vitest.config.ts` aliased @useroutr/ui at `../packages/ui/src`, which from
apps/dashboard resolves to apps/packages — one level short. Two suites failed
to collect. The aliases move to array form with regex `find` entries so
subpath imports (@useroutr/ui/globals.css) are not rewritten onto the index
file.

PayoutExportButton's five failures were not an `environment` problem: jsdom
was configured correctly and the sibling suite passed. The test spied on
`document.createElement` with a blanket `mockReturnValue`, so the
`createElement('div')` that testing-library's `render()` makes for its own
container got the fake anchor instead — "Target container is not a DOM
element" was reporting exactly that. Same for `document.body.appendChild`.
Both spies now pass through anything that is not the download anchor, and an
`afterEach` restores them: they wrap the live `document`, so without it each
test layered another spy on the previous test's.

Dashboard, assertions that encoded real behaviour changes:

- PayoutStatusBadge (5): the tone name is no longer emitted as a class.
  BrandStatusBadge maps it to Tailwind tokens, so each status now asserts the
  pair it actually resolves to — PENDING to `bg-warning/10 text-warning`, and
  CANCELLED to `bg-secondary text-muted-foreground`, which it shares with
  neutral deliberately.
- usePayouts (4): `/v1/payouts` to `/payouts`. api.ts sets
  BASE_URL = ${origin}/v1, so callers pass bare paths. These were the
  pre-fix paths.
- BatchGroupHeader (1): `getByText('(1)')` threw on multiple matches — three
  payouts with three distinct statuses render three chips. Now asserts all
  three, plus each status label. Only surfaced because the suite finally
  loaded.

Checkout:

No test script, no runner, no config — vitest, jsdom and testing-library are
added to match the dashboard's versions.

The test file had drifted much further than the missing runner suggested. It
was written against a CryptoPayment with a "Select network" / "Select token"
two-step picker over ETH/BASE/BNB and USDC/USDT/ETH. The current component
has no token picker at all and offers Stellar plus the five CCTP chains under
"Send USDC from". Assertions are rewritten against the shipped component.
QuoteCountdown's three were still accurate and pass unchanged.

The mocks were stale to match: wagmi's useConnect and useBalance and a
`useQuote` hook the component does not use were mocked, while useChainId,
useSendTransaction, @rainbow-me/rainbowkit and next/navigation were not. It
also wrapped renders in WagmiProvider while mocking wagmi without exporting
it, which would have rendered undefined. The provider is dropped since every
wagmi hook is mocked; QueryClientProvider stays and the two crypto hooks are
left real so they exercise their own wiring against a mocked api client.

Not done: packages/types has no test script and no test files on main, so
nothing to wire up. Its tests and a `test-packages` job live on
fix/single-v1-prefix; test-merged against this, the two compose without
conflict.

Verified from a clean `npm ci`: dashboard 38 tests, checkout 8, both green.
Lint clean in both apps (0 errors, pre-existing warnings only) and the
checkout `next build` still passes, since vitest.config.ts lands inside its
tsconfig include glob.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
useroutr-www Ready Ready Preview Aug 9, 2026 2:51pm

@0xdevcollins
0xdevcollins merged commit 2062824 into main Aug 9, 2026
7 checks passed
0xdevcollins added a commit that referenced this pull request Aug 9, 2026
… dialog

Radix renders `AlertDialogDescription` as a `<p>`, and this one held a `<div>`
(the payout summary) and another `<p>` (the closing warning). That is invalid
HTML: React flags it at runtime, and the browser's parser recovers by closing
the paragraph early, which splits the element `aria-describedby` points at.

`asChild` renders the description as the `<div>` it always needed to be, with
the intro text moved into its own `<p>`. The whole block stays inside the
description rather than moving out as a sibling, so the accessible description
still covers all of it.

Salvaged from #202, whose test and config work was superseded by #201 — this
was the one change in it that nothing else carried. Dashboard suite stays at
38/38.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0xdevcollins added a commit that referenced this pull request Aug 9, 2026
… dialog (#203)

Radix renders `AlertDialogDescription` as a `<p>`, and this one held a `<div>`
(the payout summary) and another `<p>` (the closing warning). That is invalid
HTML: React flags it at runtime, and the browser's parser recovers by closing
the paragraph early, which splits the element `aria-describedby` points at.

`asChild` renders the description as the `<div>` it always needed to be, with
the intro text moved into its own `<p>`. The whole block stays inside the
description rather than moving out as a sibling, so the accessible description
still covers all of it.

Salvaged from #202, whose test and config work was superseded by #201 — this
was the one change in it that nothing else carried. Dashboard suite stays at
38/38.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview – useroutr-www — 2b89fee4 Deployed Aug 9, 2026 by vercel[bot]
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