Skip to content

Feat - Implement E2E test with Playwright and Chainwright#38

Open
akbarsaputrait wants to merge 11 commits into
masterfrom
feat/e2e-test
Open

Feat - Implement E2E test with Playwright and Chainwright#38
akbarsaputrait wants to merge 11 commits into
masterfrom
feat/e2e-test

Conversation

@akbarsaputrait

Copy link
Copy Markdown
Member

Summary

This PR expands the E2E test suite to cover real-money cross-chain USDC payment flows across EVM (Base), Solana, and Stellar. Previously, only mocked flows existed; this adds live mainnet tests for all major bridge, checkout, and deposit routing combinations. Each flow uses the appropriate wallet automation strategy — MetaMask and Phantom via the chainwright extension driver for EVM/Solana, and a novel in-page headless signer for Stellar (no browser extension required).


Changes Made

  • Added a centralized e2e/env.ts module as the single source of truth for all E2E env vars (wallet credentials, addresses, amounts), loaded consistently by the config, wallet setup, and spec files
  • Replaced the single flat payment-flow.spec.ts with per-flow spec files under e2e/payment-flows/bridge/, checkout/, and deposit/ — one file per source→destination pair
  • Added 7 new real-funds bridge/checkout/deposit specs: EVM↔Stellar, Solana↔Stellar, Solana↔EVM, Checkout EVM→Stellar, Deposit Stellar→EVM
  • Refactored helpers.ts to add high-level startBridgePayment, startCheckoutPayment, startDepositPayment, enterDepositAmount, payInWithMetaMask, payInWithPhantom, payInWithStellarHeadless helpers shared across all specs
  • Added wallet-setup/metamask.setup.ts and wallet-setup/phantom.setup.ts for cached chainwright wallet profiles
  • Added lib/e2e-stellar-kit.ts — an in-page HeadlessSecretKeyModule that implements ModuleInterface using @stellar/stellar-sdk so Playwright can drive Stellar pay-ins without Freighter or any extension
  • Split Stellar constants into lib/e2e-stellar-constants.ts to avoid transitive @stellar/freighter-api import failures under the Playwright ESM loader
  • Overhauled playwright.config.ts: replaced the single chromium project with a sequenced set of named projects (mockedevm-to-stellarstellar-to-evm → … → deposit-stellar-to-evm), each with appropriate headless/headed mode, 10-minute timeout, and dependencies chaining
  • Added .env.e2e.example documenting all required secrets, and E2E_INTEGRATION.md with full setup and run instructions

How to Test

⚠️ These tests move real mainnet USDC. Use throwaway wallets with small balances only.

  1. Copy the env template and fill in your credentials:
    cp examples/nextjs-app/.env.e2e.example examples/nextjs-app/.env.e2e
    # Fill in E2E_EVM_SEED_PHRASE, E2E_STELLAR_SECRET, E2E_SOLANA_SEED_PHRASE, and the corresponding addresses
  2. Build the cached wallet profiles (one-time, or after changing credentials):
    cd examples/nextjs-app && pnpm setup-wallets
  3. Start the dev server:
    pnpm dev
  4. Run the mocked suite (fast, no real funds, no credentials needed):
    pnpm test:e2e --project=mocked
  5. Run a specific real-funds flow (example — EVM → Stellar):
    pnpm test:e2e --project=evm-to-stellar
  6. Run the full sequenced suite (requires all three wallet credentials):
    pnpm test:e2e
  7. Verify: each spec should reach waitForPayoutCompleted and pass. Failed runs produce a trace, screenshot, and video in playwright-report/.

Testing Checklist

  • Unit tests added/updated — N/A (E2E only)
  • Integration tests added/updated — these are the integration tests
  • Edge cases covered — optional UI branches handled via clickIfVisible; tests skip gracefully when credentials are absent
  • Manual testing completed — flows verified on mainnet with small amounts
  • No regressions in existing tests — mocked suite preserved; new specs are isolated under payment-flows/

Reviewer Notes

  • Real-funds tests are opt-in by design. Every spec checks for the relevant env var and calls test.skip() if absent — CI runs only the mocked project unless credentials are explicitly provided.
  • Stellar headless signer security tradeoff: The HeadlessSecretKeyModule injects a secret key at runtime via window.__E2E_STELLAR_SECRET__ (set by Playwright's page.addInitScript). The secret never exists as an env var in the app — it can't be constructed outside the test context. Reviewers should confirm this is acceptable for the threat model.
  • Project sequencing is intentional. The dependencies chain (mocked → evm-to-stellar → … → deposit-stellar-to-evm) ensures the fast mocked suite always runs first and that real-fund flows run in a predictable order. Running a mid-chain project individually still works — Playwright resolves transitive deps.
  • wallet-mock.ts was removed. It was superseded by the chainwright fixture approach. Confirm nothing outside this branch still imports it.

Related

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
intent-example Ready Ready Preview, Comment Jun 12, 2026 2:43am

Request Review

@akbarsaputrait akbarsaputrait marked this pull request as ready for review June 11, 2026 15:33

@shawnmuggle shawnmuggle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for this — real-funds E2E coverage across all three chains is genuinely valuable, and the in-page headless Stellar signer is a clever way to avoid extension automation. That said, there are several funds-safety issues and one undisclosed production behavior change that need to be addressed before this can merge. Inline comments below for the blocking items; the rest are summarized here.

Blocking (see inline comments)

  1. Hardcoded fallback destination addresses in e2e/env.ts can silently misdirect real USDC.
  2. trace: "on" records the injected Stellar secret key into trace artifacts on every run.
  3. The mocked project has no mocking — it hits the live production API and creates real unpaid payments on every CI run.
  4. Undisclosed production change to fee-quote appId resolution in three connectkit components, shipped in v0.1.28 without a CHANGELOG entry.
  5. setup-wallets never builds the MetaMask profile, so every documented EVM-source flow fails out of the box.
  6. 9 of 27 Playwright projects (and their package.json scripts) reference spec files that don't exist.
  7. The mocked project sets headless: false, which breaks the documented secrets-free CI path on runners without a display server.
  8. The E2E signer ships in the production bundle and moves @creit.tech/stellar-wallets-kit from a lazy chunk onto the critical path.
  9. All 6 deposit specs hardcode "0.1", ignoring E2E_AMOUNT despite docs saying it applies to every flow.

Non-blocking, worth fixing

  • Skip guards only check the source wallet. Every spec should also skip when the destination address is unset: test.skip(!E2E.stellar.secret || !E2E.evm.address, ...).
  • No balance precheck / reconciliation. A pay-in that lands on-chain followed by a payout-assertion timeout reads as a generic test failure, and the linear dependencies chain then skips every downstream flow — funds end up stranded on one side and the wallet drains across runs. Consider a per-wallet balance gate before each flow and pairing A→B with B→A legs.
  • The single linear dependency chain is fragile. One flaky mainnet payment skips everything after it, and running --project=X without --no-deps re-runs the whole upstream chain with real money. workers: 1 already serializes; consider depending only on mocked.
  • test:e2e runs all real-funds flows by default once .env.e2e exists. Consider making it an alias for the mocked project and requiring an explicit opt-in (e.g. E2E_REAL_FUNDS=1) for the full chain.
  • chainwright is pre-1.0 on a caret range (^0.9.12) and is handed real seed phrases. Please pin an exact version.
  • Node engines undeclared: chainwright requires Node >=22 and process.loadEnvFile needs >=20.12, but there's no engines field and @types/node is ^20.
  • Root .gitignore has no .env.e2e entry — it's only ignored inside examples/nextjs-app/. Since e2e/env.ts loads env files cwd-relative, a .env.e2e created elsewhere in the monorepo is committable.
  • pnpm-workspace.yaml pins playwright-core to exactly 1.60.0 while @playwright/test floats on ^1.60.0 — the next lockfile refresh can split the pair and break the driver protocol. Pin the family together or drop the override.
  • Comment/doc drift: the mocked comment says "Headless" but code says headless: false; stellar-to-evm/stellar-to-solana comments say headless is fine yet force headed; useStellarSigner's docblock references a "build-time NEXT_PUBLIC_ fallback" that doesn't exist (good that it doesn't — please fix the comment, not the code); docs/e2e/03-playwright-config.md shows 19 projects and a different dependency chain than the shipped config; TEST_IDS.md links to E2E_INTEGRATION.md, which doesn't exist (the docs live in docs/e2e/); the PR description says 7 specs but the branch adds 18.
  • DRY: the 27 near-identical project blocks, 27 test:e2e:* scripts, and 18 ~90%-identical spec files could all be generated from a single flow list — adding a flow currently means editing 3 files in 4 places.
  • Selector robustness: waitForPayoutCompleted asserts exact UI copy ("Payment Completed") at the end of an 8-minute real-money flow — a data-testid for the terminal state would survive copy changes. The Stellar pay-in picks the source token via first /USDC/i text match instead of the exact rozopay-option-{id} testid used on the EVM/Solana paths.
  • clickIfVisible swallows all errors, not just not-visible timeouts — a failed click on an existing chain-picker button silently proceeds on the wrong chain. Catch only TimeoutError.
  • bundle-analysis.html is a generated artifact (~248KB) that gets re-committed every build — consider gitignoring it.
  • Default vault password fallback (TempE2ePassword123!) means the cached .wallet-cache/ profiles holding real seed phrases are encrypted with a publicly known string when the env var is unset — fail hard instead.
  • Test appId: the playground (and therefore these tests) creates payments under rozoDemo against the production API. A dedicated test appId (rozoTest) keeps E2E traffic out of production analytics.

No committed secrets were found in the diff — the .gitignore additions and the env-template approach are appreciated.

Comment thread examples/nextjs-app/e2e/env.ts Outdated
Comment thread examples/nextjs-app/e2e/playwright.config.ts Outdated
Comment thread examples/nextjs-app/e2e/playwright.config.ts
Comment thread examples/nextjs-app/e2e/playwright.config.ts Outdated
Comment thread examples/nextjs-app/package.json Outdated
Comment thread examples/nextjs-app/package.json Outdated
Comment thread packages/connectkit/src/components/Pages/PayWithToken/index.tsx Outdated
Comment thread examples/nextjs-app/app/providers.tsx Outdated
Comment thread examples/nextjs-app/e2e/payment-flows/deposit/stellar-to-evm.spec.ts Outdated

@shawnmuggle shawnmuggle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-reviewed after the updates — this round addressed nearly everything. Confirmed fixed:

  • ✅ Hardcoded fallback destination addresses removed; all specs now guard both source credential and destination address, plus the new global-setup.ts validation
  • trace: "retain-on-failure" for real-funds projects
  • setup-wallets builds both MetaMask and Phantom profiles
  • ✅ The 9 same-chain phantom projects and their scripts are gone
  • headless: true for the mocked and Stellar-source projects
  • ✅ E2E Stellar kit is now build-time gated behind NEXT_PUBLIC_E2E (set by the webServer config) — out of production bundles
  • ✅ Deposit specs use E2E.depositAmount (respects E2E_AMOUNT with the 0.1 floor)
  • appId resolution extracted into the validated resolveOrderAppId helper, and the useCallback deps fixed

Two blockers remain — both inline below. Everything else from the previous review I'm leaving as non-blocking.

  1. CI breaks on every run: .github/workflows/test.yml runs pnpm test:e2e with no env configured, and the new globalSetup throws when any of the six vars is missing — so the CI job now fails before a single test runs.
  2. The mocked project still hits the production API: the config comment now says tests "must mock outbound API calls via page.route()", but checkout.spec.ts (which this project runs) has no mocking — its "Create Payment API flow" block makes real createPayment() POSTs, creating real unpaid payment rows in the production DB on every CI run (× retries).

Fix those two and this is good to merge from my side.

Comment thread examples/nextjs-app/e2e/global-setup.ts
Comment thread examples/nextjs-app/e2e/playwright.config.ts
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.

2 participants