Files
Jules ac76ed4f06 test(e2e): wallet extension send/swap suites on Anvil forks
* refactor(e2e): extract shared extension launcher from MetaMask fixture

Move the persistent-context + temp-profile + SW-derived extension-id
logic into launchExtensionContext (with beforeLaunch, afterClose,
extraChromeArgs, profilePrefix, viewport options) so other extensions
can reuse it. MetaMask fixture becomes a thin wrapper; behavior
unchanged for the existing Hub suite.

* feat(e2e): add congestion and balance controls to Anvil helper

Add setNextBlockBaseFee, setBlockGasLimit, disableAutoMining,
dropTransaction, setEthBalanceFor, and getEthBalance for staging
network conditions and localizing accounts on the forks.

* feat(e2e): add rpc-router and opt-in wallet test-server orchestration

Anvil only answers JSON-RPC at / while apps/api appends path suffixes
(/ethereum/mainnet/alchemy), so add a small host-side router that
strips the prefix and forwards by chain segment to the forks.

Wire wallet-send/wallet-swap Playwright projects behind the
RUN_WALLET_E2E env flag (env rather than argv: workers re-evaluate the
config without --project) with apps/api and the router as web servers,
narrow the @wallet grep so it no longer swallows the new tags, and add
setup:wallet / test:wallet-* scripts. setup:wallet passes
WXT_STATUS_API_URL explicitly because wxt build --mode development
otherwise bakes the production API URL.

* chore: add changeset

* ci: point changesets and workflow triggers at master

The main branch was renamed to master, so origin/main no longer exists:
the changeset status step failed to find the divergence point, and the
CI/E2E/Release push triggers never fired.

* feat(e2e): add wallet page objects

Onboarding (seed import), portfolio, send modal (Max fees parsing,
password step, Confirm-with-new-gas-price, success toast), and a thin
LiFi swap-drawer wrapper. Add a clickVisible helper to BasePage since
the wallet renders controls twice (desktop + hidden mobile variant).

* feat(e2e): add wallet extension fixture stack with API data seam and LiFi mock

wallet-extension.fixture provides anvilRpc with per-test
snapshot/revert isolation, the dev-built extension context, seed-import
onboarding (compliant password decoupled from WALLET_PASSWORD), and
page-object fixtures.

status-api-mocks intercepts only the Alchemy enhanced-API tRPC routes
(assets.all, assets.nativeToken, activities.page) with fork-consistent
fixtures and exposes setFeeRate for the gas-shift test; rpc.proxy and
nodes.* pass through to the real backend and fork. lifi-mock serves
every li.quest endpoint the widget/SDK call; the mocked step calldata
is a real WETH9 deposit() so the swap executes genuinely on the fork
with an exactly assertable 1:1 output.

* test(e2e): add wallet send network-condition suite

Happy path with on-chain delta assertion, slow inclusion (no state
change until mineBlock), high base fee reflected in quoted Max fees and
actually paid, and a deterministic GasShiftedError -> Confirm with new
gas price flow via a pinned fee quote bumped mid-signing. The planned
tight-gas-limit row was dropped: Anvil estimates a 21k transfer fine
under a 20k block limit, making the scenario artificial.

* test(e2e): add wallet swap suite against mocked LiFi quotes

ETH -> WETH via the exchange drawer with recorded li.quest fixtures:
happy path with exact 1:1 on-chain output assertion, slow inclusion
(widget pending until mineBlock), and high base fee (swap executes,
elevated gas provably paid). Deliberately shallow on adverse-condition
UX, which LiFi owns; the deep matrix lives in the send suite.

* ci: add wallet e2e job

Build the extension in dev mode (WXT_STATUS_API_URL pointed at the
local api), start the forks (optionally against an archive upstream
via the E2E_MAINNET_FORK_URL secret), and run the wallet-send and
wallet-swap projects under xvfb with RUN_WALLET_E2E=1. apps/api boots
with dummy secrets since chain calls hit the fork and enhanced
endpoints are seam-mocked.

* chore: add changeset

* fix(e2e): disambiguate send success toast from its aria-live announcer

Radix Toast renders a hidden screen-reader announcer that duplicates the
toast message as "Notification Transaction signed and sent", so the
substring locator intermittently resolved to two elements and tripped
Playwright strict mode in CI.

* fix(e2e): keep vault accounting consistent when seeding shares

The live WETH pre-deposit vault drained below 1 token of totalSupply, so
seeding 1e18 shares via a bare balance-slot write made the on-chain
withdraw revert InvalidState() (amount exceeds stored totalAssets, burn
exceeds totalSupply) -- the forks track the latest block, so state drift
broke the test without a code change.

fundVaultShares now bumps the vault's totalSupply and stored totalAssets
by the seeded delta. Backing slots are discovered by value match plus a
write probe (the vaults keep both values equal, so a match alone is
ambiguous) and cached per vault.

* chore: add changeset
2026-07-10 19:25:43 +09:00

177 lines
5.4 KiB
YAML

name: E2E Tests
on:
push:
branches: ['master', 'develop']
pull_request:
types: [opened, synchronize]
workflow_call:
env:
METAMASK_VERSION: '13.18.1'
jobs:
e2e:
name: E2E Tests
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Build the Hub's workspace deps (dist/) so the Next dev server the e2e
# webServer starts can resolve them. Cached, so the webServer's own build
# step is then a no-op.
- name: Build Hub dependencies
run: pnpm exec turbo run build --filter=hub^...
- name: Install Playwright browsers
run: cd e2e && pnpm exec playwright install chromium --with-deps
- name: Cache MetaMask extension
id: metamask-cache
uses: actions/cache@v4
with:
path: e2e/.extensions/metamask
key: metamask-v${{ env.METAMASK_VERSION }}-${{ hashFiles('e2e/download-metamask-extension.ts') }}
- name: Download MetaMask extension
if: steps.metamask-cache.outputs.cache-hit != 'true'
run: cd e2e && pnpm setup:metamask
# Local Anvil forks for the @anvil on-chain tests.
- name: Start Anvil forks
run: cd e2e && pnpm anvil:up
# No BASE_URL → Playwright's webServer builds + serves this branch's Hub
# on localhost:3003, so all projects exercise local code.
- name: Run E2E tests
run: cd e2e && xvfb-run --auto-servernum -- pnpm test
env:
WALLET_SEED_PHRASE: ${{ secrets.E2E_WALLET_SEED_PHRASE }}
WALLET_PASSWORD: ${{ secrets.E2E_WALLET_PASSWORD }}
CI: true
- name: Stop Anvil forks
if: always()
run: cd e2e && pnpm anvil:down
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-report
path: e2e/test-results/
retention-days: 14
- name: Upload test artifacts
uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-artifacts
path: |
e2e/test-results/artifacts/
retention-days: 7
e2e-wallet:
name: E2E Tests (Wallet extension)
runs-on: ubuntu-latest
timeout-minutes: 20
# The wallet-send flow needs the local status-api backend (apps/api), but no
# real API keys: chain calls go through the rpc-router to the Anvil fork, and
# the enhanced/CoinGecko-backed endpoints are mocked by the data seam. Dummy
# values just satisfy apps/api's env validation. ETH_RPC_PROXY_URL is set by
# the Playwright webServer (-> rpc-router). For lower flakiness, point the fork
# at an archive RPC by adding an E2E_MAINNET_FORK_URL secret (see below).
env:
ALCHEMY_API_KEYS: test
INFURA_API_KEY: test
COINGECKO_API_KEY: test
ETH_RPC_PROXY_AUTH_USERNAME: test
ETH_RPC_PROXY_AUTH_PASSWORD: test
MARKET_PROXY_URL: http://localhost:9999
MARKET_PROXY_AUTH_USERNAME: test
MARKET_PROXY_AUTH_PASSWORD: test
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Playwright browsers
run: cd e2e && pnpm exec playwright install chromium --with-deps
# Build the wallet's workspace deps (dist/), then the extension itself in
# dev mode so WXT_STATUS_API_URL points at the local apps/api (:3030).
- name: Build wallet dependencies
run: pnpm exec turbo run build --filter=wallet^...
- name: Build wallet extension
run: cd e2e && pnpm setup:wallet
# Local mainnet + Linea Anvil forks. To reduce archive-related flakiness,
# set MAINNET_FORK_URL to an archive RPC via the E2E_MAINNET_FORK_URL secret.
- name: Start Anvil forks
run: cd e2e && pnpm anvil:up
env:
MAINNET_FORK_URL: ${{ secrets.E2E_MAINNET_FORK_URL || 'https://ethereum-rpc.publicnode.com' }}
# Playwright's webServer starts apps/api (:3030) + rpc-router (:8548) for the
# wallet projects (no Hub, no MetaMask). Headed extension tests need xvfb.
- name: Run wallet E2E tests
run: cd e2e && xvfb-run --auto-servernum -- pnpm exec playwright test --project=wallet-send --project=wallet-swap
env:
RUN_WALLET_E2E: '1'
WALLET_SEED_PHRASE: ${{ secrets.E2E_WALLET_SEED_PHRASE }}
CI: true
- name: Stop Anvil forks
if: always()
run: cd e2e && pnpm anvil:down
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-wallet-report
path: e2e/test-results/
retention-days: 14
- name: Upload test artifacts
uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-wallet-artifacts
path: |
e2e/test-results/artifacts/
retention-days: 7