Skip to content

Commit 269782c

Browse files
ffmcgee725jiexiadonesky1
authored
test: react native app e2e appwright test coverage (multichain) (#26435)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR establishes E2E test coverage for multichain MetaMask Connect flows within the React Native Playground app, ensuring the MetaMask Connect correctly manages simultaneous connections across multiple networks and that permissions, request routing, and session lifecycle all function end-to-end. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/WAPI-1053 ## **Manual testing steps** ~~1. Pull https://github.com/MetaMask/connect-monorepo 2. `yarn && yarn build` 3. Run appropriate test dapp locally `integrations/wagmi` or `playground/legacy-evm-react-vite-playground` using `yarn dev --host`~~ 4. In the mobile repo, update `appwright/appwright.config.ts` for the `mm-connect-android-local` entry 5. You will need a prefined SRP android build. You can find one **[here](https://app.bitrise.io/build/2f2254fc-34bf-4291-bbb5-d525aa01d717?tab=artifacts)**. 6. Add entry for `E2E_PASSWORD` in `.js.env` and source it with `source .js.env`. You can get the password from someone in slack. 7. Determine which `appwright/tests/mm-connect/connection-*.spec.js` you want to run ~~8. Update the dapp url constant to use `10.0.2.2` for the host~~ 9. Ensure the other tests in the suite are marked `.skip` 10. Follow instructions in `tests/performance/mm-connect/README.md` (L#51 - L#105) to create APK for react native playground to be used in e2e test 11. `yarn appwright test --project mm-connect-android-local --config appwright/appwright.config.ts` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds a new Appwright E2E flow that installs/uninstalls a separate Android APK via `adb` and drives two apps (wallet + playground), which can increase test/CI flakiness and introduces new local setup dependencies. > > **Overview** > Adds a new MM Connect Appwright spec (`tests/performance/mm-connect/multichain-rn-connect.spec.js`) that drives the **React Native Playground APK** alongside the MetaMask wallet to validate multichain connect, EVM read/write routing, Solana signing via Snap UI, and session disconnect. > > Introduces supporting infrastructure: `PLAYGROUND_PACKAGE_ID`, an `ensurePlaygroundInstalled()` helper that (re)installs a prebuilt playground release APK from a sibling `connect-monorepo` checkout, new page objects (`wdio/.../RNPlaygroundDapp.js`, `wdio/.../SnapSignModal.js`), updated perf docs/README for setup, and minor `.gitignore` tweaks for new report output. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3fc5660. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Jiexi Luan <jiexiluan@gmail.com> Co-authored-by: Alex Donesky <adonesky@gmail.com>
1 parent 569facc commit 269782c

8 files changed

Lines changed: 937 additions & 2 deletions

File tree

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ tests/artifacts
115115
# E2E Reports generated by tests
116116
tests/reports
117117
# Legacy e2e reports
118-
e2e/reports
118+
e2e/reports
119119

120120
# Unit Coverage Reports generated by tests
121121
tests/coverage
@@ -136,6 +136,7 @@ appwright/test-reports/
136136
test-results/
137137
test-reports/
138138
appwright/reporters/reports/*
139+
tests/reporters/reports/
139140
*.apk
140141

141142
# anvil binaries
@@ -180,4 +181,4 @@ temp/
180181

181182
tests/coverage-systems/
182183

183-
runway-artifacts/
184+
runway-artifacts/

tests/framework/Constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export const APP_PACKAGE_IDS = {
3333
ANDROID: 'io.metamask',
3434
} as const;
3535

36+
// Package ID for the React Native playground APK (pre-installed on the device)
37+
export const PLAYGROUND_PACKAGE_ID = 'com.anonymous.multichainrnplayground';
38+
3639
export const DEFAULT_TEST_DAPP_PATH = path.join(
3740
'..',
3841
'..',

tests/performance/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ Integration tests for MetaMask Connect:
275275
- `connection-evm.spec.js` - EVM connection performance
276276
- `connection-multichain.spec.js` - Multichain connection performance
277277
- `connection-wagmi.spec.js` - Wagmi integration performance
278+
- `multichain-rn-connect.spec.js` - Multichain + Solana via the React Native Playground APK
279+
280+
> The RN playground test requires a separate APK to be built and installed on the
281+
> emulator before running. See [`tests/performance/mm-connect/README.md`](mm-connect/README.md)
282+
> for full setup instructions.
278283
279284
## Performance Tracking System
280285
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# MetaMask Connect E2E Tests
2+
3+
This directory contains Appwright-based E2E tests for MetaMask Connect flows.
4+
5+
## Test Files
6+
7+
| File | Description |
8+
| ------------------------------- | ----------------------------------------------------------- |
9+
| `connection-evm.spec.js` | Legacy EVM connection via Browser Playground in Chrome |
10+
| `connection-multichain.spec.js` | Multichain API connection via Browser Playground in Chrome |
11+
| `connection-wagmi.spec.js` | Wagmi connector via Browser Playground in Chrome |
12+
| `multichain-rn-connect.spec.js` | **Multichain + Solana via the React Native Playground APK** |
13+
14+
## React Native Playground Setup
15+
16+
The `multichain-rn-connect` test interacts with **two native Android apps** at the
17+
same time — the MetaMask wallet and the React Native Playground
18+
(`@metamask/react-native-playground`). The wallet APK is installed by Appwright;
19+
the playground APK must be **built beforehand**. The test's `beforeAll` hook
20+
automatically **uninstalls and reinstalls** it on the emulator every run.
21+
22+
### Prerequisites
23+
24+
| Requirement | Details |
25+
| --------------------------- | ------------------------------------------------------------ |
26+
| Android emulator | Running and reachable via `adb` |
27+
| MetaMask wallet APK | Built or pointed to via `buildPath` in `appwright.config.ts` |
28+
| `connect-monorepo` checkout | Cloned **next to** `metamask-mobile` (same parent directory) |
29+
30+
Expected directory layout:
31+
32+
```
33+
Documents/MetaMask/ # or wherever you keep these repos
34+
├── metamask-mobile/ # this repo
35+
└── connect-monorepo/ # https://github.com/MetaMask/connect-monorepo
36+
└── playground/
37+
└── react-native-playground/
38+
```
39+
40+
### Build the Playground APK
41+
42+
You must build the release APK **manually** before running the test, and
43+
**rebuild** whenever you change the playground source code. The release variant
44+
is required because the debug variant expects a Metro dev server at runtime.
45+
46+
#### First-time setup
47+
48+
Run all five steps below from your terminal. Each command is self-contained
49+
(no implicit working directory from a previous step).
50+
51+
**Step 1 — Install monorepo dependencies:**
52+
53+
```bash
54+
cd /path/to/connect-monorepo
55+
yarn install
56+
```
57+
58+
**Step 2 — Build all workspace packages** (so `@metamask/connect-evm`,
59+
`@metamask/playground-ui`, etc. have their `dist/` output):
60+
61+
```bash
62+
cd /path/to/connect-monorepo
63+
yarn build
64+
```
65+
66+
**Step 3 — Configure your Infura API key:**
67+
68+
The playground uses Infura RPC endpoints for blockchain requests. Copy the
69+
example `.env` file and add your key:
70+
71+
```bash
72+
cd /path/to/connect-monorepo/playground/react-native-playground
73+
cp .env.example .env
74+
```
75+
76+
Then edit `.env` and set your Infura API key:
77+
78+
```
79+
EXPO_PUBLIC_INFURA_API_KEY=your_infura_api_key_here
80+
```
81+
82+
> You can reuse the same key from `metamask-mobile/.js.env`
83+
> (`MM_INFURA_PROJECT_ID`).
84+
85+
**Step 4 — Generate the native Android project** (only needed once, or after
86+
deleting the `android/` directory):
87+
88+
```bash
89+
cd /path/to/connect-monorepo/playground/react-native-playground
90+
npx expo prebuild --platform android
91+
```
92+
93+
**Step 5 — Build the release APK:**
94+
95+
```bash
96+
cd /path/to/connect-monorepo/playground/react-native-playground/android
97+
./gradlew assembleRelease
98+
```
99+
100+
The first build takes ~7 minutes. Subsequent builds are incremental and much
101+
faster. The output APK will be at:
102+
103+
```
104+
connect-monorepo/playground/react-native-playground/android/app/build/outputs/apk/release/app-release.apk
105+
```
106+
107+
#### Rebuilding after changes
108+
109+
When you edit the playground source, run the build command again:
110+
111+
```bash
112+
cd /path/to/connect-monorepo/playground/react-native-playground/android
113+
./gradlew assembleRelease
114+
```
115+
116+
If you also changed shared workspace packages (e.g. `@metamask/playground-ui`),
117+
rebuild those first:
118+
119+
```bash
120+
cd /path/to/connect-monorepo
121+
yarn build
122+
```
123+
124+
Then run `./gradlew assembleRelease` as above.
125+
126+
### Automatic Install on Each Test Run
127+
128+
The test's `beforeAll` hook automatically **uninstalls** any existing version
129+
of the playground from the emulator and **installs** the pre-built release APK.
130+
This guarantees the device always has the latest APK you built, and avoids
131+
stale debug-vs-release mismatches.
132+
133+
### Running the Test
134+
135+
Before running, make sure the `mm-connect-android-local` project in
136+
`tests/appwright.config.ts` matches your local emulator:
137+
138+
```ts
139+
// tests/appwright.config.ts — adjust to your emulator
140+
{
141+
name: 'mm-connect-android-local',
142+
use: {
143+
device: {
144+
name: 'Samsung Galaxy S24 Ultra', // ← your AVD name
145+
osVersion: '14', // ← your AVD API level
146+
},
147+
buildPath: 'PATH-TO-BUILD', // ← path to your MetaMask .apk
148+
},
149+
}
150+
```
151+
152+
Then run:
153+
154+
```bash
155+
# From the metamask-mobile root
156+
yarn run-appwright:mm-connect-android-local
157+
```
158+
159+
Or run only the RN playground spec:
160+
161+
```bash
162+
npx appwright test tests/performance/mm-connect/multichain-rn-connect.spec.js \
163+
--project mm-connect-android-local \
164+
--config tests/appwright.config.ts
165+
```
166+
167+
### What the Test Covers
168+
169+
The `multichain-rn-connect` test validates five areas of the multichain
170+
MetaMask Connect flow:
171+
172+
1. **Simultaneous multi-chain connection** — connects to Ethereum, Linea,
173+
Polygon, and Solana in a single session and verifies all scope cards appear.
174+
175+
2. **Read request routing** — invokes `eth_blockNumber` on each EVM chain and
176+
`getGenesisHash` on Solana. These are handled directly by the RPC layer
177+
without opening MetaMask.
178+
179+
3. **Write request routing** — invokes `personal_sign` on each EVM chain and
180+
`signMessage` on Solana. Each write request opens MetaMask for approval,
181+
allowing the test to verify the request was routed to the correct network.
182+
183+
4. **Session termination** — disconnects via the Multichain SDK
184+
(`sdk.disconnect()`, the equivalent of `wallet_revokeSession`) and verifies
185+
the session is fully terminated on both the dApp and wallet sides.
186+
187+
### How It Works
188+
189+
Unlike the browser-based tests (which serve a web dApp in Chrome and use
190+
WebView context switching), this test operates with **two native Android apps**:
191+
192+
```
193+
┌──────────────────┐ deeplink ┌──────────────────┐
194+
│ RN Playground │ ───────────► │ MetaMask Wallet │
195+
│ (separate APK) │ ◄────────── │ (app under test) │
196+
│ │ callback │ │
197+
└──────────────────┘ deeplink └──────────────────┘
198+
```
199+
200+
- Both apps stay in the `NATIVE_APP` Appium context (no WebView switching).
201+
- `device.activateApp(packageId)` switches focus between the two apps.
202+
- The playground calls `Linking.openURL(metamask://…)` to open MetaMask;
203+
MetaMask sends a callback deeplink (`multichainrn://…`) to return.
204+
205+
### Troubleshooting
206+
207+
**`Unable to resolve the launchable activity of 'com.anonymous.multichainrnplayground'`**
208+
209+
The playground APK is not installed on the emulator. Follow the
210+
[Build the Playground APK](#build-the-playground-apk) steps above.
211+
212+
**`Unable to load script. Make sure you're running Metro...`**
213+
214+
You installed the **debug** APK instead of the **release** APK. Debug builds
215+
require a live Metro dev server. Rebuild with `./gradlew assembleRelease` and
216+
reinstall (`adb install app/build/outputs/apk/release/app-release.apk`).
217+
218+
**`RPCErr50: 401 on https://mainnet.infura.io/v3/ for method POST`**
219+
220+
The Infura API key is missing or was not inlined during the APK build. Make
221+
sure the `.env` file in `connect-monorepo/playground/react-native-playground/`
222+
contains a valid `EXPO_PUBLIC_INFURA_API_KEY`. After updating the key you must
223+
clear the build cache and rebuild:
224+
225+
```bash
226+
cd /path/to/connect-monorepo/playground/react-native-playground
227+
rm -rf android/app/build
228+
cd android && ./gradlew assembleRelease
229+
```
230+
231+
**Playground APK is installed but the test can't interact with it**
232+
233+
Make sure only one emulator is running (`adb devices` should show a single
234+
entry). If multiple devices are connected, prefix commands with
235+
`-s emulator-5554` or similar.
236+
237+
**Picker dropdown doesn't select the right method**
238+
239+
The test selects methods by exact text match (`getElementByText`). If the
240+
method list in the playground changes, update the method names in the spec
241+
(e.g., `personal_sign`, `getGenesisHash`, `signMessage`).
242+
243+
**MetaMask auto-locks during the test**
244+
245+
The `unlockIfLockScreenVisible` helper detects the lock screen and re-enters
246+
the password. If the test consistently fails at a late step, consider
247+
increasing the wallet's auto-lock timeout in Settings before running.
248+
249+
## Browser Playground Tests
250+
251+
The other three spec files (`connection-evm`, `connection-multichain`,
252+
`connection-wagmi`) use a **local web server** that serves the Browser
253+
Playground dApp in Chrome. These tests do not require a separate APK — the dApp
254+
server is started automatically in `test.beforeAll`.
255+
256+
See the [parent README](../README.md) for details on BrowserStack Local setup
257+
and other run configurations.

0 commit comments

Comments
 (0)