Feat - Implement E2E test with Playwright and Chainwright#38
Feat - Implement E2E test with Playwright and Chainwright#38akbarsaputrait wants to merge 11 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
shawnmuggle
left a comment
There was a problem hiding this comment.
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)
- Hardcoded fallback destination addresses in
e2e/env.tscan silently misdirect real USDC. trace: "on"records the injected Stellar secret key into trace artifacts on every run.- The
mockedproject has no mocking — it hits the live production API and creates real unpaid payments on every CI run. - Undisclosed production change to fee-quote
appIdresolution in three connectkit components, shipped in v0.1.28 without a CHANGELOG entry. setup-walletsnever builds the MetaMask profile, so every documented EVM-source flow fails out of the box.- 9 of 27 Playwright projects (and their package.json scripts) reference spec files that don't exist.
- The
mockedproject setsheadless: false, which breaks the documented secrets-free CI path on runners without a display server. - The E2E signer ships in the production bundle and moves
@creit.tech/stellar-wallets-kitfrom a lazy chunk onto the critical path. - All 6 deposit specs hardcode
"0.1", ignoringE2E_AMOUNTdespite 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
dependencieschain 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=Xwithout--no-depsre-runs the whole upstream chain with real money.workers: 1already serializes; consider depending only onmocked. test:e2eruns all real-funds flows by default once.env.e2eexists. 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.chainwrightis 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.loadEnvFileneeds >=20.12, but there's noenginesfield and@types/nodeis^20. - Root
.gitignorehas no.env.e2eentry — it's only ignored insideexamples/nextjs-app/. Sincee2e/env.tsloads env files cwd-relative, a.env.e2ecreated elsewhere in the monorepo is committable. pnpm-workspace.yamlpinsplaywright-coreto exactly1.60.0while@playwright/testfloats 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
mockedcomment says "Headless" but code saysheadless: false;stellar-to-evm/stellar-to-solanacomments 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.mdshows 19 projects and a different dependency chain than the shipped config;TEST_IDS.mdlinks toE2E_INTEGRATION.md, which doesn't exist (the docs live indocs/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:
waitForPayoutCompletedasserts exact UI copy ("Payment Completed") at the end of an 8-minute real-money flow — adata-testidfor the terminal state would survive copy changes. The Stellar pay-in picks the source token via first/USDC/itext match instead of the exactrozopay-option-{id}testid used on the EVM/Solana paths. clickIfVisibleswallows all errors, not just not-visible timeouts — a failed click on an existing chain-picker button silently proceeds on the wrong chain. Catch onlyTimeoutError.bundle-analysis.htmlis 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
rozoDemoagainst 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.
shawnmuggle
left a comment
There was a problem hiding this comment.
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.tsvalidation - ✅
trace: "retain-on-failure"for real-funds projects - ✅
setup-walletsbuilds both MetaMask and Phantom profiles - ✅ The 9 same-chain phantom projects and their scripts are gone
- ✅
headless: truefor 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(respectsE2E_AMOUNTwith the 0.1 floor) - ✅
appIdresolution extracted into the validatedresolveOrderAppIdhelper, and theuseCallbackdeps fixed
Two blockers remain — both inline below. Everything else from the previous review I'm leaving as non-blocking.
- CI breaks on every run:
.github/workflows/test.ymlrunspnpm test:e2ewith no env configured, and the newglobalSetupthrows when any of the six vars is missing — so the CI job now fails before a single test runs. - The
mockedproject still hits the production API: the config comment now says tests "must mock outbound API calls via page.route()", butcheckout.spec.ts(which this project runs) has no mocking — its "Create Payment API flow" block makes realcreatePayment()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.
…al API integration
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
chainwrightextension driver for EVM/Solana, and a novel in-page headless signer for Stellar (no browser extension required).Changes Made
e2e/env.tsmodule as the single source of truth for all E2E env vars (wallet credentials, addresses, amounts), loaded consistently by the config, wallet setup, and spec filespayment-flow.spec.tswith per-flow spec files undere2e/payment-flows/bridge/,checkout/, anddeposit/— one file per source→destination pairhelpers.tsto add high-levelstartBridgePayment,startCheckoutPayment,startDepositPayment,enterDepositAmount,payInWithMetaMask,payInWithPhantom,payInWithStellarHeadlesshelpers shared across all specswallet-setup/metamask.setup.tsandwallet-setup/phantom.setup.tsfor cached chainwright wallet profileslib/e2e-stellar-kit.ts— an in-pageHeadlessSecretKeyModulethat implementsModuleInterfaceusing@stellar/stellar-sdkso Playwright can drive Stellar pay-ins without Freighter or any extensionlib/e2e-stellar-constants.tsto avoid transitive@stellar/freighter-apiimport failures under the Playwright ESM loaderplaywright.config.ts: replaced the singlechromiumproject with a sequenced set of named projects (mocked→evm-to-stellar→stellar-to-evm→ … →deposit-stellar-to-evm), each with appropriate headless/headed mode, 10-minute timeout, anddependencieschaining.env.e2e.exampledocumenting all required secrets, andE2E_INTEGRATION.mdwith full setup and run instructionsHow to Test
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 addresseswaitForPayoutCompletedand pass. Failed runs produce a trace, screenshot, and video inplaywright-report/.Testing Checklist
clickIfVisible; tests skip gracefully when credentials are absentpayment-flows/Reviewer Notes
test.skip()if absent — CI runs only themockedproject unless credentials are explicitly provided.HeadlessSecretKeyModuleinjects a secret key at runtime viawindow.__E2E_STELLAR_SECRET__(set by Playwright'spage.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.dependencieschain (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.tswas removed. It was superseded by thechainwrightfixture approach. Confirm nothing outside this branch still imports it.Related