chainwright 0.8.11 → 0.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</picture>
|
|
7
7
|
<br><br>
|
|
8
8
|
<span align="center">
|
|
9
|
-
Test, automate, and verify every wallet interaction
|
|
9
|
+
Test, automate, and verify every wallet interaction with the precision, your users expect.
|
|
10
10
|
</span>
|
|
11
11
|
<br><br>
|
|
12
12
|
<span align="center">
|
|
@@ -16,7 +16,7 @@ Test, automate, and verify every wallet interaction, with the precision your use
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
Chainwright is an end-to-end testing toolkit for Web3 dapps built on
|
|
19
|
+
Chainwright is an end-to-end testing toolkit for Web3 dapps built on Playwright. It helps you build and cache browser wallet extension state, then reuse it in your end-to-end tests through ready-made fixtures.
|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
@@ -24,45 +24,52 @@ Chainwright is an end-to-end testing toolkit for Web3 dapps built on top of Play
|
|
|
24
24
|
- Playwright fixtures for wallet + Dapp testing
|
|
25
25
|
- Support for multiple wallet profiles per wallet
|
|
26
26
|
- Wallet action APIs for onboarding, account switching, transaction confirmation, and more
|
|
27
|
-
- Multiple wallet profile caching
|
|
28
27
|
|
|
29
28
|
## Supported Wallets
|
|
30
29
|
|
|
30
|
+
- Keplr
|
|
31
31
|
- MetaMask
|
|
32
|
-
-
|
|
32
|
+
- Meteor
|
|
33
33
|
- Petra
|
|
34
34
|
- Phantom
|
|
35
|
-
-
|
|
36
|
-
- Keplr
|
|
35
|
+
- Solflare
|
|
37
36
|
|
|
38
37
|
## Requirements
|
|
39
38
|
|
|
40
39
|
- Node.js `>=22`
|
|
41
40
|
- `@playwright/test@1.60.0` (peer dependency)
|
|
42
41
|
|
|
42
|
+
## Operating Systems
|
|
43
|
+
Supports the following operating systems:
|
|
44
|
+
- MacOS
|
|
45
|
+
- Linux
|
|
46
|
+
- Windows
|
|
47
|
+
|
|
43
48
|
## Installation
|
|
44
49
|
|
|
50
|
+
Before installing Chainwright, ensure to install Playwright's browser using the command below.
|
|
51
|
+
|
|
45
52
|
```bash
|
|
46
|
-
|
|
53
|
+
npx playwright install chromium
|
|
47
54
|
```
|
|
55
|
+
|
|
48
56
|
```bash
|
|
49
|
-
|
|
57
|
+
bunx playwright install chromium
|
|
50
58
|
```
|
|
59
|
+
|
|
60
|
+
After Installing Playwright's browser, install `Chainwright` and `@playwright/test`
|
|
61
|
+
|
|
51
62
|
```bash
|
|
52
|
-
|
|
63
|
+
pnpm add -D chainwright @playwright/test
|
|
53
64
|
```
|
|
54
65
|
```bash
|
|
55
|
-
|
|
66
|
+
bun add -D chainwright @playwright/test
|
|
56
67
|
```
|
|
57
|
-
|
|
58
|
-
After installation, make sure Playwright browsers are installed in your machine.
|
|
59
|
-
|
|
60
68
|
```bash
|
|
61
|
-
|
|
69
|
+
npm install --save-dev chainwright @playwright/test
|
|
62
70
|
```
|
|
63
|
-
|
|
64
71
|
```bash
|
|
65
|
-
|
|
72
|
+
yarn add -D chainwright @playwright/test
|
|
66
73
|
```
|
|
67
74
|
|
|
68
75
|
## Quick Start
|
|
@@ -71,7 +78,8 @@ bunx playwright install --chromium #Optional
|
|
|
71
78
|
|
|
72
79
|
Create a setup directory (default: `tests/wallet-setup`) and add `*.setup.ts` files with a wallet name in the filename, for example:
|
|
73
80
|
|
|
74
|
-
- `
|
|
81
|
+
- `base.setup.ts`
|
|
82
|
+
- `base-two.setup.ts`
|
|
75
83
|
- `petra.setup.ts`
|
|
76
84
|
- `phantom-team-a.setup.ts`
|
|
77
85
|
|
|
@@ -135,14 +143,49 @@ export default defineWalletSetup(
|
|
|
135
143
|
);
|
|
136
144
|
```
|
|
137
145
|
|
|
146
|
+
To support multiple profiles in a single wallet (for example, MetaMask), only setup files from the second profile onward need an explicit, distinct profile name.
|
|
147
|
+
|
|
148
|
+
`main.setup.ts` can use the default profile, while `main-two.setup.ts` (and any additional setup files) should declare a unique profile name. Then, in any fixture that should use that profile, pass the exact same profileName value.
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
- `main.setup.ts`: uses the default profile
|
|
152
|
+
- `main-two.setup.ts`: defines `profileName: "profile two"`
|
|
153
|
+
- Fixture usage: `metamaskFixture({ profileName: "profile two" })`
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// tests/wallet-setup/main-two.setup.ts
|
|
157
|
+
import { defineWalletSetup } from "chainwright/core";
|
|
158
|
+
import { Metamask } from "chainwright/metamask";
|
|
159
|
+
|
|
160
|
+
const PASSWORD = "test1234"; // For Petra wallet, you have to use a strong password. e.g. PlayerPetra45!!
|
|
161
|
+
const SEED_PHRASE = "test test test test test test test test test test test test test";
|
|
162
|
+
|
|
163
|
+
export default defineWalletSetup(
|
|
164
|
+
PASSWORD,
|
|
165
|
+
async ({ walletPage }) => {
|
|
166
|
+
const metamask = new Metamask(walletPage);
|
|
167
|
+
|
|
168
|
+
await metamask.onboard({
|
|
169
|
+
mode: "import",
|
|
170
|
+
secretRecoveryPhrase: SEED_PHRASE,
|
|
171
|
+
mainAccountName: "Main",
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
profileName: "profile two"
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
138
180
|
### 2. Build wallet cache
|
|
139
181
|
|
|
140
|
-
Run setup with the CLI (Supports npx
|
|
182
|
+
Run setup with the CLI (Supports **npx**, **bun**, **pnpm**, and **yarn**):
|
|
141
183
|
|
|
142
|
-
>
|
|
184
|
+
>[!NOTE]
|
|
185
|
+
> By default, Chainwright looks for `tests/wallet-setup` in your base directory. However, you can specify the directory you want Chainwright to get your setup files from.
|
|
143
186
|
|
|
144
187
|
```bash
|
|
145
|
-
bun chainwright --wallets
|
|
188
|
+
bun chainwright --wallets <Wallets you want to support>
|
|
146
189
|
```
|
|
147
190
|
|
|
148
191
|
To specify a directory:
|
|
@@ -153,6 +196,7 @@ bun chainwright <directory path> <wallet> -f #Optional flag
|
|
|
153
196
|
|
|
154
197
|
Useful flags:
|
|
155
198
|
|
|
199
|
+
- `-h, --help` shows you all the commands
|
|
156
200
|
- `-f, --force` overwrite existing cache
|
|
157
201
|
- `--wallets <wallets...>` select wallets (`metamask`, `solflare`, `petra`, `phantom`, `meteor`, `keplr`). Setup multiple wallets at the same time.
|
|
158
202
|
- `-a, --all` setup all wallets
|
|
@@ -163,7 +207,7 @@ Useful flags:
|
|
|
163
207
|
- `--ph, --phantom` setup phantom wallet
|
|
164
208
|
- `-s, --solflare` setup solflare wallet
|
|
165
209
|
|
|
166
|
-
|
|
210
|
+
Wallet profile cache is stored under:
|
|
167
211
|
|
|
168
212
|
- `.wallet-cache/<wallet>/wallet-data` (default profile)
|
|
169
213
|
- `.wallet-cache/<wallet>/<profileName>` (custom profile)
|
|
@@ -171,33 +215,34 @@ Cache is stored under:
|
|
|
171
215
|
### 3. Use wallet fixtures in Playwright tests
|
|
172
216
|
|
|
173
217
|
```ts
|
|
174
|
-
import { expect } from "@playwright/test";
|
|
218
|
+
import { expect, type Page } from "@playwright/test";
|
|
175
219
|
import { testWithChainwright } from "chainwright/core";
|
|
176
220
|
import { metamaskFixture } from "chainwright/metamask";
|
|
177
221
|
|
|
178
|
-
// Fixture
|
|
222
|
+
// Chainwright's Fixture
|
|
179
223
|
export const testWithMetamask = testWithChainwright(metamaskFixture());
|
|
180
224
|
|
|
181
225
|
// Extend Chainwright's metamaskFixture to suit your need
|
|
182
|
-
export const testDappFixture = testWithMetamask.extend<
|
|
183
|
-
context: async({ context: _ }, use) => {
|
|
184
|
-
//...Context content here
|
|
185
|
-
},
|
|
226
|
+
export const testDappFixture = testWithMetamask.extend<{dAppPage: Page}>({
|
|
186
227
|
dappPage: async ({ page, baseURL }, use) => {
|
|
187
|
-
await page.goto(`
|
|
228
|
+
await page.goto(`https://your-dapp.example`);
|
|
188
229
|
await use(page);
|
|
189
230
|
},
|
|
190
231
|
});
|
|
191
232
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
233
|
+
// Then in your tests do:
|
|
234
|
+
const test = testDappFixture;
|
|
235
|
+
test.describe("Example tests", () => {
|
|
236
|
+
test("connect wallet to dapp", async ({ dappPage, metamask }) => {
|
|
237
|
+
const connectButton = dappPage.getByRole("button", { name: /Connect/i})
|
|
238
|
+
await connectButton.click();
|
|
239
|
+
await metamask.connectToApp("Account 1");
|
|
240
|
+
await expect(dappPage.getByText("Connected")).toBeVisible();
|
|
241
|
+
});
|
|
242
|
+
})
|
|
199
243
|
```
|
|
200
|
-
>
|
|
244
|
+
> [!NOTE]
|
|
245
|
+
> The wallet fixture will make use of the `default` wallet profile. If you specified a `profile-name` at the point of setting up, make sure to include it in the fixture.
|
|
201
246
|
|
|
202
247
|
```ts
|
|
203
248
|
// No profile name is specified at setup time
|
|
@@ -207,33 +252,119 @@ const testWithFixture = testWithChainwright(fixture())
|
|
|
207
252
|
const testWithFixture = testWithChainwright(fixture({ profileName: "profile name" }))
|
|
208
253
|
```
|
|
209
254
|
|
|
210
|
-
`Wallet fixture parameters
|
|
211
|
-
- `profileName
|
|
212
|
-
- `slowMo
|
|
255
|
+
`Wallet fixture parameters`:
|
|
256
|
+
- `profileName`?: string,
|
|
257
|
+
- `slowMo`?: number
|
|
213
258
|
|
|
214
259
|
## Worker-Scoped Fixture
|
|
215
260
|
|
|
216
|
-
Use worker-scoped fixtures when
|
|
261
|
+
Use worker-scoped fixtures when tests in a `test.describe()` block can safely share a wallet context. Setup and teardown run once per worker instead of per test, which speeds up CI runs and reduces flakiness caused by repeated wallet initialization.
|
|
217
262
|
|
|
218
263
|
```ts
|
|
264
|
+
import { type Page } from "@playwright/test";
|
|
265
|
+
import { testWithChainwright } from "chainwright/core";
|
|
219
266
|
import { metamaskWorkerScopeFixture } from "chainwright/metamask";
|
|
220
267
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
268
|
+
// Chainwright's worker scoped fixture
|
|
269
|
+
export const testWithFixture = testWithChainwright(metamaskWorkerScopeFixture());
|
|
270
|
+
|
|
271
|
+
// Your worker scoped fixture that extends Chainwright's worker scoped fixture
|
|
272
|
+
export const workerScopedFixture = testWithFixture.extend<{dAppPage: Page}>({
|
|
273
|
+
dappPage: [
|
|
274
|
+
async ({ workerScopeContents }, use) => {
|
|
275
|
+
const { context, wallet, walletPage } = workerScopeContents;
|
|
276
|
+
/** N.B:
|
|
277
|
+
* wallet represents -> metamask, phantom, keplr, etc...
|
|
278
|
+
* walletPage represents -> metamask wallet page, phantom wallet page, keplr wallet page, etc...
|
|
279
|
+
*/
|
|
280
|
+
const _dappPage = await context.newPage();
|
|
281
|
+
await _dappPage.goto(`http://example-site.com`);
|
|
282
|
+
await use(_dappPage);
|
|
283
|
+
},
|
|
284
|
+
{ scope: "worker" },
|
|
285
|
+
],
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
// Then in your tests do:
|
|
289
|
+
const test = workerScopedFixture;
|
|
290
|
+
|
|
291
|
+
test.describe("Example test", () => {
|
|
292
|
+
test("Should confirm transaction", ({ dappPage, workerScopeContents}) => {
|
|
293
|
+
const { wallet: metamask } = workerScopeContents
|
|
294
|
+
await dappPage.getByRole("button", { name: "Send Tx" }).click();
|
|
295
|
+
await metamask.confirmTransaction();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("Should reject transaction", async ({ dappPage, workerScopeContents })=> {
|
|
299
|
+
const { wallet: metamask } = workerScopeContents
|
|
300
|
+
await dappPage.getByRole("button", { name: "Send Tx" }).click();
|
|
301
|
+
await metamask.rejectTransaction();
|
|
302
|
+
});
|
|
303
|
+
})
|
|
225
304
|
|
|
226
|
-
test("confirm transaction", async ({ dappPage, metamask }) => {
|
|
227
|
-
await dappPage.getByRole("button", { name: "Send Tx" }).click();
|
|
228
|
-
await metamask.confirmTransaction();
|
|
229
|
-
});
|
|
230
305
|
```
|
|
231
306
|
|
|
232
|
-
`Worker scoped fixture parameters
|
|
307
|
+
`Worker scoped fixture parameters`:
|
|
233
308
|
|
|
234
309
|
- `profileName?: string`
|
|
235
310
|
- `slowMo?: number`
|
|
236
|
-
|
|
311
|
+
|
|
312
|
+
### 4. Running in CI (GitHub Actions)
|
|
313
|
+
Running Chainwright in CI is very similar to running Playwright in CI. The only additional requirement is a cache-build step before executing tests, as shown below:
|
|
314
|
+
|
|
315
|
+
Why we make use of **xvfb**:
|
|
316
|
+
> [!IMPORTANT]
|
|
317
|
+
> Browser extensions don't load in headless Chromium, so the tests have to run in headed mode. CI machines have no display, so launching a headed browser fails. xvfb provides a fake virtual display, letting Chromium run headed in CI as if a screen were attached.
|
|
318
|
+
|
|
319
|
+
```yml
|
|
320
|
+
name: CI
|
|
321
|
+
|
|
322
|
+
on:
|
|
323
|
+
workflow_dispatch:
|
|
324
|
+
pull_request:
|
|
325
|
+
branches: ["main"]
|
|
326
|
+
|
|
327
|
+
jobs:
|
|
328
|
+
test:
|
|
329
|
+
runs-on: ubuntu-22.04
|
|
330
|
+
timeout-minutes: 60
|
|
331
|
+
|
|
332
|
+
steps:
|
|
333
|
+
- name: Checkout code
|
|
334
|
+
uses: actions/checkout@v5
|
|
335
|
+
with:
|
|
336
|
+
submodules: "recursive"
|
|
337
|
+
fetch-depth: 0
|
|
338
|
+
|
|
339
|
+
- name: Install pnpm
|
|
340
|
+
uses: pnpm/action-setup@v5
|
|
341
|
+
with:
|
|
342
|
+
version: 10
|
|
343
|
+
|
|
344
|
+
- name: Use Node LTS
|
|
345
|
+
uses: actions/setup-node@v6
|
|
346
|
+
with:
|
|
347
|
+
node-version: 24.x
|
|
348
|
+
cache: "pnpm"
|
|
349
|
+
|
|
350
|
+
- name: Install dependencies
|
|
351
|
+
run: pnpm install
|
|
352
|
+
|
|
353
|
+
- name: Install XVFB
|
|
354
|
+
run: sudo apt-get install -y xvfb
|
|
355
|
+
|
|
356
|
+
- name: Install Playwright browsers
|
|
357
|
+
run: pnpx playwright install chromium
|
|
358
|
+
|
|
359
|
+
- name: Install Foundry
|
|
360
|
+
uses: foundry-rs/foundry-toolchain@v1
|
|
361
|
+
|
|
362
|
+
- name: Build cache
|
|
363
|
+
run: xvfb-run pnpm run setup-wallets
|
|
364
|
+
|
|
365
|
+
- name: Run end-to-end tests (Headful)
|
|
366
|
+
run: xvfb-run pnpm playwright test --config=tests/playwright.config.ts
|
|
367
|
+
```
|
|
237
368
|
|
|
238
369
|
## Wallets By Module
|
|
239
370
|
|
|
@@ -269,9 +400,9 @@ Extra Phantom/Solflare fixtures:
|
|
|
269
400
|
defineWalletSetup(password, setupFn, config?)
|
|
270
401
|
```
|
|
271
402
|
|
|
272
|
-
- `password: string` wallet unlock password saved in cache metadata
|
|
273
|
-
- `setupFn: ({ context, walletPage }) => Promise<void>` runs onboarding/import flow
|
|
274
|
-
- `config?: { profileName?: string; slowMo?: number }`
|
|
403
|
+
- `password: string` - wallet unlock password saved in cache metadata
|
|
404
|
+
- `setupFn: ({ context, walletPage }) => Promise<void>` - runs onboarding/import flow
|
|
405
|
+
- `config?: { profileName?: string; slowMo?: number }` - useful for setting up multiple wallet profiles and running the setup in slow motion `slowMo`.
|
|
275
406
|
|
|
276
407
|
### `testWithChainwright`
|
|
277
408
|
|
package/package.json
CHANGED