chainwright 0.8.10 → 0.8.12

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 CHANGED
@@ -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 top of Playwright. It helps you prebuild browser extension wallet state, then reuse it in tests through ready-made fixtures.
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
- - Solflare
32
+ - Meteor
33
33
  - Petra
34
34
  - Phantom
35
- - Meteor
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
- pnpm add -D chainwright @playwright/test
53
+ npx playwright install chromium
47
54
  ```
55
+
48
56
  ```bash
49
- bun add -D chainwright @playwright/test
57
+ bunx playwright install chromium
50
58
  ```
59
+
60
+ After Installing Playwright's browser, install `Chainwright` and `@playwright/test`
61
+
51
62
  ```bash
52
- npm install --save-dev chainwright @playwright/test
63
+ pnpm add -D chainwright @playwright/test
53
64
  ```
54
65
  ```bash
55
- yarn add -D chainwright @playwright/test
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
- npx playwright install --chromium #Optional
69
+ npm install --save-dev chainwright @playwright/test
62
70
  ```
63
-
64
71
  ```bash
65
- bunx playwright install --chromium #Optional
72
+ yarn add -D chainwright @playwright/test
66
73
  ```
67
74
 
68
75
  ## Quick Start
@@ -71,19 +78,20 @@ 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
- - `metamask.setup.ts`
81
+ - `base.setup.ts`
82
+ - `base-two.setup.ts`
75
83
  - `petra.setup.ts`
76
84
  - `phantom-team-a.setup.ts`
77
85
 
78
- Each file should export `default defineWalletSetup(...)`.
86
+ Each file must export `default defineWalletSetup(...)`.
79
87
 
80
88
  ```ts
81
89
  // tests/wallet-setup/metamask.setup.ts
82
90
  import { defineWalletSetup } from "chainwright/core";
83
91
  import { Metamask } from "chainwright/metamask";
84
92
 
85
- const PASSWORD = "test1234";
86
- const SEED_PHRASE = "debris dress width prepare table repair index athlete divide avoid month member";
93
+ const PASSWORD = "test1234"; // For Petra wallet, you have to use a strong password. e.g. PlayerPetra45!!
94
+ const SEED_PHRASE = "test test test test test test test test test test test test test";
87
95
 
88
96
  export default defineWalletSetup(
89
97
  PASSWORD,
@@ -97,27 +105,109 @@ export default defineWalletSetup(
97
105
  });
98
106
  },
99
107
  {
100
- profileName: "default", // optional
101
- slowMo: 200, // optional
108
+ ...//Optional prarmeters here
109
+ },
110
+ );
111
+ ```
112
+
113
+ **For Wallets with additional accounts**
114
+
115
+ ```ts
116
+ // tests/wallet-setup/metamask.setup.ts
117
+ import { defineWalletSetup } from "chainwright/core";
118
+ import { Petra } from "chainwright/petra";
119
+
120
+ const PASSWORD = "PlayerPetra45!!";
121
+
122
+ export default defineWalletSetup(
123
+ PASSWORD,
124
+ async ({ walletPage }) => {
125
+ const petra = new Petra(walletPage);
126
+
127
+ await petra.onboard({
128
+ mode: "importMnemonic",
129
+ accountName: "default",
130
+ secretRecoveryPhrase: "test test test...", // Seed phrase for the main account
131
+ additionalAccounts: [
132
+ {
133
+ accountName: "nw-account",
134
+ mode: "mnemonic",
135
+ mnemonicPhrase: "test test test..." // Seed Phrase for this account
136
+ },
137
+ ]
138
+ });
139
+ },
140
+ {
141
+ ...//Optional prarmeters here
142
+ }
143
+ );
144
+ ```
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"
102
176
  },
103
177
  );
104
178
  ```
105
179
 
106
180
  ### 2. Build wallet cache
107
181
 
108
- Run setup with the CLI:
182
+ Run setup with the CLI (Supports **npx**, **bun**, **pnpm**, and **yarn**):
183
+
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.
109
186
 
110
187
  ```bash
111
- chainwright ./tests/wallet-setup --wallets metamask
188
+ bun chainwright --wallets <Wallets you want to support>
189
+ ```
190
+
191
+ To specify a directory:
192
+
193
+ ```bash
194
+ bun chainwright <directory path> <wallet> -f #Optional flag
112
195
  ```
113
196
 
114
197
  Useful flags:
115
198
 
199
+ - `-h, --help` shows you all the commands
116
200
  - `-f, --force` overwrite existing cache
117
- - `--wallets <wallets...>` select wallets (`metamask`, `solflare`, `petra`, `phantom`, `meteor`, `keplr`)
201
+ - `--wallets <wallets...>` select wallets (`metamask`, `solflare`, `petra`, `phantom`, `meteor`, `keplr`). Setup multiple wallets at the same time.
118
202
  - `-a, --all` setup all wallets
203
+ - `--kp, --keplr` setup keplr wallet
204
+ - `-m, --metamask` setup metamask wallet
205
+ - `--mt, --meteor` setup the meteor wallet
206
+ - `--pt, --petra` setup petra wallet
207
+ - `--ph, --phantom` setup phantom wallet
208
+ - `-s, --solflare` setup solflare wallet
119
209
 
120
- Cache is stored under:
210
+ Wallet profile cache is stored under:
121
211
 
122
212
  - `.wallet-cache/<wallet>/wallet-data` (default profile)
123
213
  - `.wallet-cache/<wallet>/<profileName>` (custom profile)
@@ -125,48 +215,158 @@ Cache is stored under:
125
215
  ### 3. Use wallet fixtures in Playwright tests
126
216
 
127
217
  ```ts
128
- import { expect } from "@playwright/test";
218
+ import { expect, type Page } from "@playwright/test";
129
219
  import { testWithChainwright } from "chainwright/core";
130
220
  import { metamaskFixture } from "chainwright/metamask";
131
221
 
132
- const test = testWithChainwright(
133
- metamaskFixture({
134
- profileName: "default",
135
- }),
136
- );
222
+ // Chainwright's Fixture
223
+ export const testWithMetamask = testWithChainwright(metamaskFixture());
137
224
 
138
- test("connect wallet to dapp", async ({ page, metamask }) => {
139
- await page.goto("https://your-dapp.example");
140
- await metamask.connectToApp("Account 1");
141
- await expect(page.getByText("Connected")).toBeVisible();
225
+ // Extend Chainwright's metamaskFixture to suit your need
226
+ export const testDappFixture = testWithMetamask.extend<{dAppPage: Page}>({
227
+ dappPage: async ({ page, baseURL }, use) => {
228
+ await page.goto(`https://your-dapp.example`);
229
+ await use(page);
230
+ },
142
231
  });
232
+
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
+ })
243
+ ```
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.
246
+
247
+ ```ts
248
+ // No profile name is specified at setup time
249
+ const testWithFixture = testWithChainwright(fixture())
250
+
251
+ // If a profile name is specified at setup time.
252
+ const testWithFixture = testWithChainwright(fixture({ profileName: "profile name" }))
143
253
  ```
144
254
 
255
+ `Wallet fixture parameters`:
256
+ - `profileName`?: string,
257
+ - `slowMo`?: number
258
+
145
259
  ## Worker-Scoped Fixture
146
260
 
147
- Use worker-scoped fixtures when you want one wallet context per worker and a prepared `dappPage`.
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.
148
262
 
149
263
  ```ts
264
+ import { type Page } from "@playwright/test";
265
+ import { testWithChainwright } from "chainwright/core";
150
266
  import { metamaskWorkerScopeFixture } from "chainwright/metamask";
151
267
 
152
- export const test = metamaskWorkerScopeFixture({
153
- profileName: "default",
154
- dappUrl: "https://your-dapp.example",
155
- });
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
+ })
156
304
 
157
- test("confirm transaction", async ({ dappPage, metamask }) => {
158
- await dappPage.getByRole("button", { name: "Send Tx" }).click();
159
- await metamask.confirmTransaction();
160
- });
161
305
  ```
162
306
 
163
- `WorkerScopeFixtureArgs`:
307
+ `Worker scoped fixture parameters`:
164
308
 
165
309
  - `profileName?: string`
166
310
  - `slowMo?: number`
167
- - `dappUrl?: string`
168
311
 
169
- ## Wallet Fixtures By Module
312
+ ### 4. Using 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
+ ```
368
+
369
+ ## Wallets By Module
170
370
 
171
371
  Each wallet module exports:
172
372
 
@@ -200,9 +400,9 @@ Extra Phantom/Solflare fixtures:
200
400
  defineWalletSetup(password, setupFn, config?)
201
401
  ```
202
402
 
203
- - `password: string` wallet unlock password saved in cache metadata
204
- - `setupFn: ({ context, walletPage }) => Promise<void>` runs onboarding/import flow
205
- - `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`.
206
406
 
207
407
  ### `testWithChainwright`
208
408
 
@@ -234,12 +434,6 @@ Additional wallet-specific actions are available, for example:
234
434
  - Petra/Solflare/Meteor: `switchNetwork`
235
435
  - Meteor: `openSettings`
236
436
 
237
- ## Troubleshooting
238
-
239
- - `Cache for <wallet> ... not found`: run `chainwright` setup first.
240
- - Setup file not detected: ensure file matches `*.setup.ts` or `*.setup.js` and includes a wallet name.
241
- - Existing profile conflict: use `--force` or a unique `profileName`.
242
-
243
437
  ## License
244
438
 
245
439
  MIT
package/dist/cli/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env -S node --import tsx
2
- import j from"path";import{styleText as g}from"util";import be from"@inquirer/checkbox";import{Command as Fe}from"commander";import L from"fs";import z from"path";import{fileURLToPath as ee}from"url";var te=ee(import.meta.url),oe=z.dirname(te),$=z.resolve(oe,"..","..","generated-profile-name.types.ts"),re=e=>e.replace(/(^\w)/g,o=>o.toUpperCase());async function H({walletName:e,profileName:o}){let t=re(e),a=`export type ${t}Profiles = "${o}";`;if(L.existsSync($)){let r=L.readFileSync($,"utf-8"),n=r.match(new RegExp(`export type ${t}Profiles = ("[^"]+"(?:\\s*\\|\\s*"[^"]+")*)`));if(n){let l=n[0];if(!l.includes(`"${o}"`)){let s=l.concat(` | "${o}"`),c=r.replace(l,s);try{L.writeFileSync($,c)}catch(i){console.error("Error updating existing profile name type: ",i)}}}else try{L.appendFileSync($,`
3
- ${a}`,{encoding:"utf-8"})}catch(l){console.error("Error appending new profile name type: ",l)}}else try{L.writeFileSync($,a)}catch(r){console.error("Error writing new profile name type: ",r)}}import ne from"path";import{pathToFileURL as se}from"url";import{styleText as ie}from"util";import{glob as le}from"glob";import ae from"path";function R(e){let o=ae.basename(e),t=o.match(/^([a-z0-9_]+)(?:-[a-z0-9_]+)*\.setup\.(?:ts|js|mjs)$/i);if(!t)throw new Error(`Invalid wallet setup filename: ${o} (expected "<name>[ -variant].setup.{ts,js,mjs}")`);return t[1]}var pe=e=>e.replace(/\\/g,"/"),ce=e=>`${pe(ne.resolve(e))}/**/*.setup.{ts,js,}`,me=e=>import(new URL(se(e)).href);async function I({walletSetupDir:e,selectedWallets:o}){let t=ce(e),a=(await le(t,{dot:!0,absolute:!0,nodir:!0,windowsPathsNoEscape:!0})).sort(),r=o.length===1?o[0]:o,n=["metamask","solflare","petra","meteor","keplr","phantom"];Array.isArray(r)&&r.forEach(i=>{n.includes(i)||console.warn(ie("magenta",`Unsupported wallet: "${i}". Supported wallets are: ${n.join(", ")}`,{validateStream:!1}))});let s=(r==="all"?a:Array.isArray(r)?a.filter(i=>r.some(p=>i.includes(p))):a.filter(i=>i.includes(r))).map(i=>({filePath:i,walletName:R(i)}));if(!s.length||s.length===0)throw new Error([`No wallet setup file found at ${e} for wallet: "${o}".`,'Setup files must use a ".setup.{ts,js,mjs}" extension and include a valid wallet name.','Examples: "metamask.setup.ts", "solflare.setup.ts", "phantom.setup.ts", "metamask-connected.setup.ts"'].join(`
4
- `));return await Promise.all(s.map(async({filePath:i,walletName:p})=>{let f=await(await me(i)).default,{fn:E,config:h,password:w}=f;return{walletName:p,fileList:s,config:h,walletPassword:w,setupFunction:E}}))}import x from"fs";import O from"path";import{styleText as S}from"util";import{chromium as _e}from"@playwright/test";import ye from"path";var K=".wallet-cache";var V="wallet-setup",G="13.22.0",C="https://github.com/amaify/chainwright/releases/download/v0.1.0/",fe=`https://github.com/MetaMask/metamask-extension/releases/download/v${G}/metamask-chrome-${G}.zip`,ue=`${C}solflare-wallet-extension-v2.19.1.zip`,de=`${C}petra-wallet-extension-v2.4.8.zip`,we=`${C}phantom-wallet-extension-v26.10.0.zip`,ge=`${C}meteor-wallet-extension-v0.7.0.zip`,he=`${C}keplr-wallet-extension-v0.13.3.zip`,F={metamask:{downloadUrl:fe,extensionName:"MetaMask"},solflare:{downloadUrl:ue,extensionName:"Solflare Wallet"},petra:{downloadUrl:de,extensionName:"Petra Aptos Wallet"},phantom:{downloadUrl:we,extensionName:"Phantom"},meteor:{downloadUrl:ge,extensionName:"Meteor Wallet"},keplr:{downloadUrl:he,extensionName:"Keplr"}};function P(e){return ye.resolve(process.cwd(),K,e)}import{z as T}from"zod";var xe=T.object({id:T.string(),name:T.string()}),Se=T.array(xe);async function X(e,o){let t=await e.newPage();await t.goto("chrome://extensions");let a=await t.evaluate("chrome.management.getAll()"),r=Se.parse(a),n=r.find(l=>l.name.toLowerCase()===o.toLowerCase());if(!n)throw new Error([`[GetExtensionId] Extension with name ${o} not found.`,`Available extensions: ${r.map(l=>l.name).join(", ")}`].join(`
5
- `));return await t.close(),n.id}import v from"fs";import k from"path";import{styleText as W}from"util";import $e from"adm-zip";import{createWriteStream as Ee}from"fs";import{Readable as Ae}from"stream";import{styleText as N}from"util";import ve from"cli-progress";var Le=12e4;async function Y({url:e,destination:o}){let t=new AbortController,a=setTimeout(()=>t.abort(),Le),r=await fetch(e,{redirect:"follow",signal:t.signal});r.ok||(console.error(N("redBright",`\u274C Download failed: HTTP ${r.status}`,{validateStream:!1})),t.abort(),process.exit(1));let n=parseInt(r.headers.get("content-length")||"0",10),l=0,s=Ae.fromWeb(r.body);try{let c=new ve.SingleBar({format:`Downloading ${N("cyan","{bar}",{validateStream:!1})} {percentage}%`,clearOnComplete:!0,barCompleteChar:"\u2588",barIncompleteChar:"\u2591",hideCursor:!0});c.start(n,0,{speed:"N/A"}),await new Promise((i,p)=>{let m=Ee(o);s.pipe(m),s.on("data",f=>{l+=f.length,c.update(l)}),m.on("error",p),m.on("finish",()=>{c.stop(),i(void 0)})})}catch(c){console.error(N("redBright",`\u274C Download failed: ${c}`,{validateStream:!1})),process.exit(1)}finally{clearTimeout(a)}}var U=!1;async function q({downloadUrl:e,name:o,force:t}){let a=P(o),n=F[o].extensionName,l=k.join(a,`${o}-extension.zip`),s=k.join(a,`${o}-extension`);if(t&&v.existsSync(a)&&!U&&(v.rmSync(a,{recursive:!0}),console.info(W("magenta",`\u{1F9F9} Removed ${n} because of the force flag`,{validateStream:!1}))),v.existsSync(a)||v.mkdir(a,{recursive:!0},i=>{if(i)throw Error("Failed to create cache directory");console.info(`\u2705 ${n} Cache directory created successfully.`)}),v.existsSync(s)?console.info(`\u2705 ${n} Version is downloaded already.`):(console.info(W("cyanBright",`\u{1F4E5} Downloading ${n} extension...`,{validateStream:!1})),await Y({url:e,destination:l}),console.info(W("green",`\u2705 ${o.toUpperCase()} Extension downloaded successfully.`,{validateStream:!1}))),!v.existsSync(s))console.info("\u{1F4E6} Extracting extension..."),new $e(l).extractAllTo(s,!0),console.info(`\u2705 ${n} Extension extracted successfully.`);else{if(U)return console.info(W("magentaBright",`Using the cached ${n} extension for profile creation.`,{validateStream:!1})),s;console.info(W("yellow",`\u26A0\uFE0F Skipping ${n} cache creation: Cache already exists at ${s}. Use --force to overwrite.`,{validateStream:!1}))}let c=k.join(s,"manifest.json");if(!v.existsSync(c))throw new Error(`\u274C (${n}) Invalid extension: manifest.json not found`);return U=!0,s}import{styleText as Ce}from"util";function M(e){return new Promise(o=>setTimeout(o,e))}var D=20,Pe=6e3,Z=4e3;async function We(e){return e.pages().find(a=>{try{return a.url().startsWith("chrome-extension://")}catch(r){return console.error("[WaitForExtensionOnLoadPage] Error checking page URL:",r),!1}})}async function J(e,o){let t=0,a=null;if(o==="meteor")return await e.newPage();for(console.info(`Waiting ${Z}ms for browser to initialize...`),await M(Z);t<=D;)try{if(console.info(`Looking for extension page (attempt ${t+1}/${D})...`),t===D)throw new Error("Extension page not found after maximum retries");let r=await We(e);if(r){console.info(`Found extension page after ${t+1} polling attempts`),a=r;break}r||(t++,console.info(`Extension page not found, retrying (${t}/${D})...`),await M(Pe))}catch(r){throw console.error("Error waiting for extension page:",r instanceof Error?r.message:"Unknown error"),new Error(`Extension failed to load properly after ${t} attempts!`)}return console.info(Ce("greenBright","\u2705 Extension page is properly loaded and ready",{validateStream:!1})),a}async function B({walletName:e,force:o,config:t,fileList:a,walletPassword:r,setupFunction:n}){let{downloadUrl:l,extensionName:s}=F[e],c=P(e),i=t?.profileName,p=i?`${i}`:"wallet-data",m=O.resolve(c,"extension-id.txt"),f=O.resolve(c,"extension-path.txt"),E=O.resolve(c,"password.txt"),h=O.resolve(c,p),w=await q({downloadUrl:l,name:e,force:o}),y=[`--disable-extensions-except=${w}`,`--load-extension=${w}`];if(x.existsSync(h)&&a.length>1)throw Error([S("yellowBright",[`\u274C ${p} directory already exists for ${s}.`,`
2
+ import z from"path";import{styleText as g}from"util";import Fe from"@inquirer/checkbox";import{Command as Te}from"commander";import $ from"fs";import H from"path";import{fileURLToPath as te}from"url";var oe=te(import.meta.url),re=H.dirname(oe),P=H.resolve(re,"..","..","generated-profile-name.types.ts"),ae=e=>e.replace(/(^\w)/g,o=>o.toUpperCase());async function G({walletName:e,profileName:o}){let t=ae(e),a=`export type ${t}Profiles = "${o}";`;if($.existsSync(P)){let r=$.readFileSync(P,"utf-8"),n=r.match(new RegExp(`export type ${t}Profiles = ("[^"]+"(?:\\s*\\|\\s*"[^"]+")*)`));if(n){let l=n[0];if(!l.includes(`"${o}"`)){let s=l.concat(` | "${o}"`),c=r.replace(l,s);try{$.writeFileSync(P,c)}catch(i){console.error("Error updating existing profile name type: ",i)}}}else try{$.appendFileSync(P,`
3
+ ${a}`,{encoding:"utf-8"})}catch(l){console.error("Error appending new profile name type: ",l)}}else try{$.writeFileSync(P,a)}catch(r){console.error("Error writing new profile name type: ",r)}}import se from"path";import{pathToFileURL as ie}from"url";import{styleText as le}from"util";import{glob as pe}from"glob";import ne from"path";function I(e){let o=ne.basename(e),t=o.match(/^([a-z0-9_]+)(?:-[a-z0-9_]+)*\.setup\.(?:ts|js|mjs)$/i);if(!t)throw new Error(`Invalid wallet setup filename: ${o} (expected "<name>[ -variant].setup.{ts,js,mjs}")`);return t[1]}var ce=e=>e.replace(/\\/g,"/"),me=e=>`${ce(se.resolve(e))}/**/*.setup.{ts,js,}`,fe=e=>import(new URL(ie(e)).href);async function N({walletSetupDir:e,selectedWallets:o}){let t=me(e),a=(await pe(t,{dot:!0,absolute:!0,nodir:!0,windowsPathsNoEscape:!0})).sort(),r=o.length===1?o[0]:o,n=["metamask","solflare","petra","meteor","keplr","phantom"];Array.isArray(r)&&r.forEach(i=>{n.includes(i)||console.warn(le("magenta",`Unsupported wallet: "${i}". Supported wallets are: ${n.join(", ")}`,{validateStream:!1}))});let s=(r==="all"?a:Array.isArray(r)?a.filter(i=>r.some(p=>i.includes(p))):a.filter(i=>i.includes(r))).map(i=>({filePath:i,walletName:I(i)}));if(!s.length||s.length===0)throw new Error([`No wallet setup file found at ${e} for wallet: "${o}".`,'Setup files must use a ".setup.{ts,js,mjs}" extension and include a valid wallet name.','Examples: "metamask.setup.ts", "solflare.setup.ts", "phantom.setup.ts", "metamask-connected.setup.ts"'].join(`
4
+ `));return await Promise.all(s.map(async({filePath:i,walletName:p})=>{let f=await(await fe(i)).default,{fn:E,config:h,password:w}=f;return{walletName:p,fileList:s,config:h,walletPassword:w,setupFunction:E}}))}import x from"fs";import R from"path";import{styleText as S}from"util";import{chromium as be}from"@playwright/test";import xe from"path";var V=".wallet-cache";var X="wallet-setup",K="13.22.0",C="https://github.com/amaify/chainwright/releases/download/v0.1.0/",ue=`https://github.com/MetaMask/metamask-extension/releases/download/v${K}/metamask-chrome-${K}.zip`,de=`${C}solflare-wallet-extension-v2.19.1.zip`,we=`${C}petra-wallet-extension-v2.4.8.zip`,ge=`${C}phantom-wallet-extension-v26.10.0.zip`,he=`${C}meteor-wallet-extension-v0.7.0.zip`,ye=`${C}keplr-wallet-extension-v0.13.3.zip`,T={metamask:{downloadUrl:ue,extensionName:"MetaMask"},solflare:{downloadUrl:de,extensionName:"Solflare Wallet"},petra:{downloadUrl:we,extensionName:"Petra Aptos Wallet"},phantom:{downloadUrl:ge,extensionName:"Phantom"},meteor:{downloadUrl:he,extensionName:"Meteor Wallet"},keplr:{downloadUrl:ye,extensionName:"Keplr"}};function W(e){return xe.resolve(process.cwd(),V,e)}import{z as D}from"zod";var Se=D.object({id:D.string(),name:D.string()}),Ee=D.array(Se);async function Y(e,o){let t=await e.newPage();await t.goto("chrome://extensions");let a=await t.evaluate("chrome.management.getAll()"),r=Ee.parse(a),n=r.find(l=>l.name.toLowerCase()===o.toLowerCase());if(!n)throw new Error([`[GetExtensionId] Extension with name ${o} not found.`,`Available extensions: ${r.map(l=>l.name).join(", ")}`].join(`
5
+ `));return await t.close(),n.id}import v from"fs";import U from"path";import{styleText as _}from"util";import Pe from"adm-zip";import{createWriteStream as Ae}from"fs";import{Readable as ve}from"stream";import{styleText as k}from"util";import Le from"cli-progress";var $e=12e4;async function q({url:e,destination:o}){let t=new AbortController,a=setTimeout(()=>t.abort(),$e),r=await fetch(e,{redirect:"follow",signal:t.signal});r.ok||(console.error(k("redBright",`\u274C Download failed: HTTP ${r.status}`,{validateStream:!1})),t.abort(),process.exit(1));let n=parseInt(r.headers.get("content-length")||"0",10),l=0,s=ve.fromWeb(r.body);try{let c=new Le.SingleBar({format:`Downloading ${k("cyan","{bar}",{validateStream:!1})} {percentage}%`,clearOnComplete:!0,barCompleteChar:"\u2588",barIncompleteChar:"\u2591",hideCursor:!0});c.start(n,0,{speed:"N/A"}),await new Promise((i,p)=>{let m=Ae(o);s.pipe(m),s.on("data",f=>{l+=f.length,c.update(l)}),m.on("error",p),m.on("finish",()=>{c.stop(),i(void 0)})})}catch(c){console.error(k("redBright",`\u274C Download failed: ${c}`,{validateStream:!1})),process.exit(1)}finally{clearTimeout(a)}}var L=!1,M="";async function Z({downloadUrl:e,name:o,force:t}){let a=W(o),n=T[o].extensionName,l=U.join(a,`${o}-extension.zip`),s=U.join(a,`${o}-extension`);if(L?M!==n&&L&&(L=!1,M=n):M=n,t&&v.existsSync(a)&&!L&&(v.rmSync(a,{recursive:!0}),console.info(_("magenta",`\u{1F9F9} Removed ${n} because of the force flag`,{validateStream:!1}))),v.existsSync(a)||v.mkdir(a,{recursive:!0},i=>{if(i)throw Error("Failed to create cache directory");console.info(`\u2705 ${n} Cache directory created successfully.`)}),v.existsSync(s)?console.info(`\u2705 ${n} Version is downloaded already.`):(console.info(_("cyanBright",`\u{1F4E5} Downloading ${n} extension...`,{validateStream:!1})),await q({url:e,destination:l}),console.info(_("green",`\u2705 ${o.toUpperCase()} Extension downloaded successfully.`,{validateStream:!1}))),!v.existsSync(s))console.info("\u{1F4E6} Extracting extension..."),new Pe(l).extractAllTo(s,!0),console.info(`\u2705 ${n} Extension extracted successfully.`);else{if(L)return console.info(_("magentaBright",`Using the cached ${n} extension for profile creation.`,{validateStream:!1})),s;console.info(_("yellow",`\u26A0\uFE0F Skipping ${n} cache creation: Cache already exists at ${s}. Use --force to overwrite.`,{validateStream:!1}))}let c=U.join(s,"manifest.json");if(!v.existsSync(c))throw new Error(`\u274C (${n}) Invalid extension: manifest.json not found`);return L=!0,s}import{styleText as Ce}from"util";function B(e){return new Promise(o=>setTimeout(o,e))}var O=20,We=6e3,J=4e3;async function _e(e){return e.pages().find(a=>{try{return a.url().startsWith("chrome-extension://")}catch(r){return console.error("[WaitForExtensionOnLoadPage] Error checking page URL:",r),!1}})}async function Q(e,o){let t=0,a=null;if(o==="meteor")return await e.newPage();for(console.info(`Waiting ${J}ms for browser to initialize...`),await B(J);t<=O;)try{if(console.info(`Looking for extension page (attempt ${t+1}/${O})...`),t===O)throw new Error("Extension page not found after maximum retries");let r=await _e(e);if(r){console.info(`Found extension page after ${t+1} polling attempts`),a=r;break}r||(t++,console.info(`Extension page not found, retrying (${t}/${O})...`),await B(We))}catch(r){throw console.error("Error waiting for extension page:",r instanceof Error?r.message:"Unknown error"),new Error(`Extension failed to load properly after ${t} attempts!`)}return console.info(Ce("greenBright","\u2705 Extension page is properly loaded and ready",{validateStream:!1})),a}async function j({walletName:e,force:o,config:t,fileList:a,walletPassword:r,setupFunction:n}){let{downloadUrl:l,extensionName:s}=T[e],c=W(e),i=t?.profileName,p=i?`${i}`:"wallet-data",m=R.resolve(c,"extension-id.txt"),f=R.resolve(c,"extension-path.txt"),E=R.resolve(c,"password.txt"),h=R.resolve(c,p),w=await Z({downloadUrl:l,name:e,force:o}),y=[`--disable-extensions-except=${w}`,`--load-extension=${w}`];if(x.existsSync(h)&&a.length>1)throw Error([S("yellowBright",[`\u274C ${p} directory already exists for ${s}.`,`
6
6
  To setup another wallet profile, add a profile name to the wallet setup function.`,S(["blueBright","italic"],'Example: defineWalletSetup(async ({ context, walletPage }) => { ... }, { profileName: "profile-name" });'),S("italic","You can also use the --force flag to overwrite the existing cache.")].join(`
7
7
  `),{validateStream:!1})].join(`
8
- `));if(x.existsSync(h))return;let A=await _e.launchPersistentContext(h,{headless:!1,args:y,slowMo:t?.slowMo??0});console.info(S("magentaBright",`\u{1F9E9}\u{1F680} Starting Chrome extension for ${e.toUpperCase()}`,{validateStream:!1}));let Q=await J(A,e);if(!x.existsSync(m)&&!x.existsSync(f)){let b=await X(A,s);console.info(S("magentaBright",`\u{1F194} ${s} extension ID: ${b}`,{validateStream:!1})),x.writeFileSync(m,b,"utf-8"),console.info(S("cyanBright",`\u{1F4BE} Saved extension ID to: ${m}`,{validateStream:!1})),x.writeFileSync(f,w,"utf-8"),console.info(S("blueBright",`\u{1F4C1} Saved extension Path to: ${f}`,{validateStream:!1})),x.writeFileSync(E,r,"utf-8"),console.info(S("yellowBright",`\u{1F511} Saved ${e} password to: ${E}`,{validateStream:!1}))}try{await n({context:A,walletPage:Q})}catch(b){await A.close(),x.rmSync(c,{force:!0,recursive:!0}),console.error("Error setting up wallet: ",b.message)}await A.close()}var Te=j.resolve(process.cwd(),"tests",V),_=2;async function De(){let e=new Fe;e.name(g("yellow","Chainwright")).description(g("green","A CLI tool for setting up wallet cache for E2E testing of web3 applications")).version(g("blue","0.0.0")),e.argument("[dir]","Directory containing the wallet setup functions",j.resolve(Te)).option("--headless","Build cache in the headless browser mode. Alternatively, set the `HEADLESS` env variable to `true`",!1).option("-f, --force","Force the creation of cache even if it already exists",!1).option("-a, --all","Setup all wallets","all").option("--kp, --keplr","Setup Keplr","keplr").option("-m, --metamask","Setup MetaMask","metamask").option("--mt, --meteor","Setup Meteor","meteor").option("--pt, --petra","Setup Petra","petra").option("--ph, --phantom","Setup Phantom","phantom").option("-s, --solflare","Setup Solflare","solflare").option("--wls, --wallets <wallets...>","Specify wallets to setup (e.g., --wallets keplr metamask)").action(async(o,t)=>{let a=["all","metamask","solflare","petra","meteor","keplr","phantom"],r=Object.keys(t).filter(p=>a.includes(p)?t[p]===!0:!1),n=r.length>0,l=t.wallets,s=l||(n?r:await be({message:"Select the wallet you want to setup",choices:[{name:"All",value:"all"},{name:"Keplr",value:"keplr"},{name:"MetaMask",value:"metamask"},{name:"Meteor",value:"meteor"},{name:"Petra",value:"petra"},{name:"Phantom",value:"phantom"},{name:"Solflare",value:"solflare"}],validate:p=>{let m=p.map(f=>f.value);return m.includes("all")&&m.length>1?'Select either "All" or specific wallets, not both.':!0},pageSize:10})),c=j.resolve(process.cwd(),o);t.headless&&(process.env.HEADLESS=!0);let i=await I({walletSetupDir:c,selectedWallets:s});for(let{walletName:p,config:m,walletPassword:f,setupFunction:E,fileList:h}of i)try{console.info(g("cyanBright",`
9
- Setting up cache for ${p}...`,{validateStream:!1})),await B({walletName:p,config:m,setupFunction:E,fileList:h,force:t.force,walletPassword:f}),m.profileName&&await H({walletName:p,profileName:m.profileName})}catch(w){if(w.message.includes("directory already exists")&&console.warn(w.message),!w.message.includes("directory already exists")){console.error(g("redBright",`
10
- \u274C Failed to setup cache for ${p}: ${w.message}`,{validateStream:!1}));let y=0;for(;_>y;){console.info(`${g("yellow",`Retry Attempt ${y+1} of ${_} for ${p}...`,{validateStream:!1})}`),console.info(g("yellow","Retrying wallet setup...",{validateStream:!1}));try{await B({walletName:p,config:m,setupFunction:E,fileList:h,force:t.force,walletPassword:f});break}catch(A){y+1<_&&console.error(g("redBright",`
11
- \u274C Attempt ${y+1} failed! Retrying...`,{validateStream:!1})),y++,y===_&&console.error(g("redBright",`\u274C Failed to setup cache after ${_} attempts for ${p}: ${A.message}`,{validateStream:!1}))}}}}}),await e.parseAsync(process.argv)}De().catch(e=>console.error(g("redBright",`Failed to run the CLI: ${e.message})`,{validateStream:!1})));export{De as clientEntry};
8
+ `));if(x.existsSync(h))return;let A=await be.launchPersistentContext(h,{headless:!1,args:y,slowMo:t?.slowMo??0});console.info(S("magentaBright",`\u{1F9E9}\u{1F680} Starting Chrome extension for ${e.toUpperCase()}`,{validateStream:!1}));let ee=await Q(A,e);if(!x.existsSync(m)&&!x.existsSync(f)){let F=await Y(A,s);console.info(S("magentaBright",`\u{1F194} ${s} extension ID: ${F}`,{validateStream:!1})),x.writeFileSync(m,F,"utf-8"),console.info(S("cyanBright",`\u{1F4BE} Saved extension ID to: ${m}`,{validateStream:!1})),x.writeFileSync(f,w,"utf-8"),console.info(S("blueBright",`\u{1F4C1} Saved extension Path to: ${f}`,{validateStream:!1})),x.writeFileSync(E,r,"utf-8"),console.info(S("yellowBright",`\u{1F511} Saved ${e} password to: ${E}`,{validateStream:!1}))}try{await n({context:A,walletPage:ee})}catch(F){await A.close(),x.rmSync(c,{force:!0,recursive:!0}),console.error("Error setting up wallet: ",F.message)}await A.close()}var De=z.resolve(process.cwd(),"tests",X),b=2;async function Oe(){let e=new Te;e.name(g("yellow","Chainwright")).description(g("green","A CLI tool for setting up wallet cache for E2E testing of web3 applications")).version(g("blue","0.0.0")),e.argument("[dir]","Directory containing the wallet setup functions",z.resolve(De)).option("--headless","Build cache in the headless browser mode. Alternatively, set the `HEADLESS` env variable to `true`",!1).option("-f, --force","Force the creation of cache even if it already exists",!1).option("-a, --all","Setup all wallets","all").option("--kp, --keplr","Setup Keplr","keplr").option("-m, --metamask","Setup MetaMask","metamask").option("--mt, --meteor","Setup Meteor","meteor").option("--pt, --petra","Setup Petra","petra").option("--ph, --phantom","Setup Phantom","phantom").option("-s, --solflare","Setup Solflare","solflare").option("--wls, --wallets <wallets...>","Specify wallets to setup (e.g., --wallets keplr metamask)").action(async(o,t)=>{let a=["all","metamask","solflare","petra","meteor","keplr","phantom"],r=Object.keys(t).filter(p=>a.includes(p)?t[p]===!0:!1),n=r.length>0,l=t.wallets,s=l||(n?r:await Fe({message:"Select the wallet you want to setup",choices:[{name:"All",value:"all"},{name:"Keplr",value:"keplr"},{name:"MetaMask",value:"metamask"},{name:"Meteor",value:"meteor"},{name:"Petra",value:"petra"},{name:"Phantom",value:"phantom"},{name:"Solflare",value:"solflare"}],validate:p=>{let m=p.map(f=>f.value);return m.includes("all")&&m.length>1?'Select either "All" or specific wallets, not both.':!0},pageSize:10})),c=z.resolve(process.cwd(),o);t.headless&&(process.env.HEADLESS=!0);let i=await N({walletSetupDir:c,selectedWallets:s});for(let{walletName:p,config:m,walletPassword:f,setupFunction:E,fileList:h}of i)try{console.info(g("cyanBright",`
9
+ Setting up cache for ${p}...`,{validateStream:!1})),await j({walletName:p,config:m,setupFunction:E,fileList:h,force:t.force,walletPassword:f}),m.profileName&&await G({walletName:p,profileName:m.profileName})}catch(w){if(w.message.includes("directory already exists")&&console.warn(w.message),!w.message.includes("directory already exists")){console.error(g("redBright",`
10
+ \u274C Failed to setup cache for ${p}: ${w.message}`,{validateStream:!1}));let y=0;for(;b>y;){console.info(`${g("yellow",`Retry Attempt ${y+1} of ${b} for ${p}...`,{validateStream:!1})}`),console.info(g("yellow","Retrying wallet setup...",{validateStream:!1}));try{await j({walletName:p,config:m,setupFunction:E,fileList:h,force:t.force,walletPassword:f});break}catch(A){y+1<b&&console.error(g("redBright",`
11
+ \u274C Attempt ${y+1} failed! Retrying...`,{validateStream:!1})),y++,y===b&&console.error(g("redBright",`\u274C Failed to setup cache after ${b} attempts for ${p}: ${A.message}`,{validateStream:!1}))}}}}}),await e.parseAsync(process.argv)}Oe().catch(e=>console.error(g("redBright",`Failed to run the CLI: ${e.message})`,{validateStream:!1})));export{Oe as clientEntry};
@@ -1,4 +1,4 @@
1
- import { b as WalletSetupFunction, a as WalletSetupConfig } from '../types-B56pinWs.js';
1
+ import { b as WalletSetupFunction, a as WalletSetupConfig } from '../types-DK8rutb5.js';
2
2
  import { Fixtures, TestType } from '@playwright/test';
3
3
 
4
4
  declare function defineWalletSetup(password: string, fn: WalletSetupFunction, config?: WalletSetupConfig): Promise<{
@@ -11,14 +11,9 @@ type WalletSetupConfig = {
11
11
  profileName?: string;
12
12
  slowMo?: number;
13
13
  };
14
- type WorkerScopeFixtureArgs = {
15
- slowMo?: number;
16
- profileName?: string;
17
- dappUrl?: string;
18
- };
19
14
  type WalletProfileFixtureArgs = {
20
15
  slowMo?: number;
21
16
  profileName?: string;
22
17
  };
23
18
 
24
- export type { SupportedWallets as S, WalletProfileFixtureArgs as W, WalletSetupConfig as a, WalletSetupFunction as b, WorkerScopeFixtureArgs as c };
19
+ export type { SupportedWallets as S, WalletProfileFixtureArgs as W, WalletSetupConfig as a, WalletSetupFunction as b };
@@ -1,6 +1,6 @@
1
- import { W as WalletProfileFixtureArgs, c as WorkerScopeFixtureArgs } from '../../types-B56pinWs.js';
2
- import { W as WorkerScopeFixture } from '../../worker-scope-context-DiN3_Sig.js';
3
- export { w as workerScopeContext } from '../../worker-scope-context-DiN3_Sig.js';
1
+ import { W as WalletProfileFixtureArgs } from '../../types-DK8rutb5.js';
2
+ import { W as WorkerScopeFixture } from '../../worker-scope-context-CNAfiliw.js';
3
+ export { w as workerScopeContext } from '../../worker-scope-context-CNAfiliw.js';
4
4
  import * as _playwright_test from '@playwright/test';
5
5
  import { BrowserContext, Page } from '@playwright/test';
6
6
  import z from 'zod';
@@ -148,6 +148,6 @@ declare class Keplr extends KeplrProfile {
148
148
 
149
149
  declare const keplrFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & KeplrFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
150
150
 
151
- declare const keplrWorkerScopeFixture: ({ slowMo, profileName, dappUrl }?: WorkerScopeFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & KeplrFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Keplr>>;
151
+ declare const keplrWorkerScopeFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & KeplrFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Keplr>>;
152
152
 
153
- export { Keplr, WalletProfileFixtureArgs, WorkerScopeFixture, WorkerScopeFixtureArgs, keplrFixture, keplrWorkerScopeFixture };
153
+ export { Keplr, WalletProfileFixtureArgs, WorkerScopeFixture, keplrFixture, keplrWorkerScopeFixture };
@@ -1,3 +1,3 @@
1
- import Z from"fs";import vt from"path";import{chromium as bt}from"@playwright/test";import yt from"path";var Y=".wallet-cache",q=".wallet-context";var J="13.22.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Kt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ut=`${_}solflare-wallet-extension-v2.19.1.zip`,Mt=`${_}petra-wallet-extension-v2.4.8.zip`,Vt=`${_}phantom-wallet-extension-v26.10.0.zip`,Ht=`${_}meteor-wallet-extension-v0.7.0.zip`,jt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function N(t){return yt.resolve(process.cwd(),q,t)}import At from"path";function w(t){return At.resolve(process.cwd(),Y,t)}import Q from"fs";import Pt from"path";async function R(t){try{let e=w(t),o=Pt.resolve(e,"extension-path.txt");if(!Q.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=Q.readFileSync(o,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function tt({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let r=await N(e.workerIndex.toString()),c=w(t.name),s=vt.resolve(c,o??"wallet-data");if(!Z.existsSync(s))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);Z.cpSync(s,r,{recursive:!0,force:!0});let p=await R(t.name),m=await bt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:a}),u=await t.indexUrl(),l=m.pages()[0];return l||(l=await m.newPage()),await l.goto(u),{context:m,walletPage:l,contextPath:r}}import{expect as nt}from"@playwright/test";function W(t){return new Promise(e=>setTimeout(e,t))}async function C(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import et from"fs";import kt from"path";async function L(t){let e=w(t),o=kt.resolve(e,"password.txt");try{if(!et.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Ct}from"@playwright/test";async function ot({context:t,path:e,locator:o}){let a;try{await Ct.poll(async()=>(a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(c=>c.url().startsWith("chrome-extension://")).map(c=>c.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await St(a,o),await a.setViewportSize({width:360,height:592}),a}async function St(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import at from"fs";import Bt from"path";async function rt(t){let e=w(t),o=Bt.resolve(e,"extension-id.txt");try{if(!at.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return at.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var g=class{name="keplr";onboardingPath="register.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/sidePanel.html`}async onboardingUrl(){return`chrome-extension://${await this.extensionId()}/${this.onboardingPath}`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async extensionId(){return await rt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ot({context:e,path:o,locator:"div[data-simplebar='init']"})}};var h={importExistingWalletButton:"button:has-text('Import an existing wallet')",usePrivateKeyButton:"button:has-text('Use recovery phrase or private key')",privateKeyTabButton:"button:has-text('Private key')",privateKeyInput:"input[type='password']",importButton:"button:has-text('Import')",walletNameInput:"input[name='name']",walletPasswordInput:"input[name='password']",confirmWalletPasswordInput:"input[name='confirmPassword']",nextButton:"button:has-text('Next')",searchNetworkInput:"input[placeholder='Search networks']",saveButton:"button:has-text('Save')",finishButton:"button:has-text('Finish')"};function Tt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function E({page:t,privateKey:e,walletName:o,chains:a,mode:r="onboard"}){let c=await L("keplr"),s=new g;if(await t.locator(h.importExistingWalletButton).click(),await t.locator(h.usePrivateKeyButton).click(),await t.getByRole("button",{name:"Private key",exact:!0}).click(),await t.locator(h.privateKeyInput).fill(e),await t.getByRole("button",{name:"Import",exact:!0}).click(),await t.locator(h.walletNameInput).fill(o),r==="onboard"){let A=t.locator(h.walletPasswordInput),F=t.locator(h.confirmWalletPasswordInput);await A.fill(c),await F.fill(c)}await t.locator(h.nextButton).click();let y=t.locator("div:has-text('All Native Chains')").nth(-4),P=t.locator("div[cursor='pointer']:has-text('Cosmos Hub')"),b=await y.locator("input[type='checkbox']").getAttribute("checked"),k=await P.locator("input[type='checkbox']").getAttribute("checked");b!==null&&await y.click(),k!==null&&await P.click();let D=t.locator(h.searchNetworkInput);for(let A of a){await D.fill(A);let T=t.locator("div[class='simplebar-content']").locator("div[cursor] > div").first().locator("div").filter({hasText:new RegExp(`^${Tt(A)}$`,"i")}).nth(2).locator("../../../../..");await T.waitFor({state:"visible",timeout:2e4}),await T.locator("input[type='checkbox']").getAttribute("checked")===null&&await T.click()}let B=t.locator(h.saveButton);if(await B.scrollIntoViewIfNeeded(),await B.click(),await W(2e3),r==="onboard"){await t.goto(await s.indexUrl());return}if(r==="add-account-single"){let A=t.locator(h.finishButton);await A.waitFor({state:"visible",timeout:2e4}),await nt(A).toBeEnabled({timeout:2e4}),await A.click()}}async function $(t){let o=await new g().onboardingUrl();await t.getByRole("link",{name:"Settings",exact:!0}).click(),await t.locator("div[cursor='pointer']").first().click(),await t.getByRole("button",{name:"Add Wallet",exact:!0}).click();let s;if(await nt.poll(async()=>(s=t.context().pages().find(p=>p.url().match(o)),!!s),{timeout:3e4}).toBe(!0).catch(p=>{console.error(`Failed to find onboarding page with URL matching ${o}. Original error: ${p}`)}),!s)throw new Error(`Onboarding page not found. Expected URL: ${o}`);return await C(s),await s.bringToFront(),s}async function it({page:t,privateKey:e,chains:o,walletName:a,mode:r}){let c=await $(t);await E({page:c,privateKey:e,walletName:a,chains:o,mode:r}),await t.locator("div:has(div:has-text('Select Wallet'))").nth(-4).locator("div:has(> div > svg)").first().click(),await t.getByRole("link",{name:"Home",exact:!0}).click()}import{expect as Wt}from"@playwright/test";var K={approveButton:"button:has-text('Approve')",rejectButton:"button[color='secondary']"};async function ct(t){let e=t.locator(K.approveButton);await Wt(e).toBeEnabled({timeout:2e4}),await e.click()}async function st(t){t.getByRole("button",{name:"Approve",exact:!0}).click(),await W(1e3)}import{expect as Et}from"@playwright/test";import f from"zod";var lt=f.discriminatedUnion("chain",[f.object({chain:f.literal(["Injective","Injective (Testnet)","Polygon"]),walletName:f.string().min(1,"Wallet name cannot be an empty string")}),f.object({chain:f.literal(["Bitcoin","Bitcoin Signet","Bitcoin Testnet"]),chainTag:f.literal(["Taproot","Native Segwit"]),walletName:f.string().min(1,"Wallet name cannot be an empty string")})]),pt=f.object({currentAccountName:f.string().min(1,"Current account name cannot be an empty string"),newAccountName:f.string().min(1,"New account name cannot be an empty string")});async function mt({page:t,...e}){let o=lt.parse({...e});await t.getByRole("textbox",{name:"Search for asset or chain (i.e. ATOM, Cosmos)",exact:!0}).fill(o.chain);let r=t.locator(`div:has-text("${o.chain}")`).nth(-2).filter({hasNot:t.locator("span")});if(await r.waitFor({state:"attached",timeout:2e4}),!await r.isVisible().catch(()=>!1))throw Error(`Make sure "${o.chain}" is activated.`);let s=await r.locator("div").all();Et(s.length).toBeGreaterThan(0),await t.locator(`div:has(div:has-text('${o.walletName}'))`).nth(-3).locator("div:has(> div > svg)").click();let u=t.locator("div:has(> div[data-simplebar='init'])").last(),d=u.locator("div:has(> div > input)").locator("input");await d.fill(o.chain);let v=await u.locator("div[cursor='pointer']",{hasText:o.chain}).all(),y;for(let b of v){let k;"chainTag"in e&&(k=e.chainTag);let D=b.locator("div",{hasText:o.chain}).last(),B=k?b.locator("div",{hasText:k}).last():null,F=(B?await B?.isVisible().catch(()=>!1):!1)?await B?.textContent():null,T=await D.textContent(),X=F?`${T} ${F}`:T,xt=k?`${o.chain} ${k}`:o.chain;if(X===xt){y=D.locator("xpath=../../../.."),await d.clear();break}}if(!y)throw Error(`Address for ${o.walletName} account on "${o.chain}" chain not found.`);return await y.hover(),await y.scrollIntoViewIfNeeded(),await y.click(),await t.evaluate(async()=>await navigator.clipboard.readText())}var O={openSidebarMenuButton:"div[cursor='pointer']:has(> div[cursor='pointer'])",menuPopupContent:"div[id='modal-root-3']",lockWalletButton:"div:has(> div:has-text('Lock Wallet'))",settingsButton:"div:has(a[href='#/settings'])"},H={unlockButton:"button[type='submit']:has-text('Unlock')",passwordInput:"input[placeholder='Type Your Password']"};async function ut(t){await t.locator(O.openSidebarMenuButton).click(),await t.locator(O.lockWalletButton).nth(-1).click(),await t.getByText("Welcome Back").waitFor({state:"visible",timeout:3e4})}import{styleText as dt}from"util";import{expect as It}from"@playwright/test";async function U(t,e){let o=t.locator("div[color]").nth(1);if(await o.textContent()===e){console.info(`
2
- Already on ${e} account. No need to switch.`);return}await o.click();let r=t.locator("div[class='simplebar-content'] > div").locator("> div",{hasText:e});if(!await r.isVisible().catch(()=>!1))throw new Error(`Account "${e}" not found. Make sure the account is onboarded or verify the account name.`);let s=t.locator("div:has-text('Select Wallet')").last();await r.click(),await s.waitFor({state:"detached",timeout:3e4})}async function j({page:t,onboard:e}){if(console.info(dt("yellowBright",`
3
- Keplr onboarding started...`,{validateStream:!1})),e.length===1)for(let{privateKey:o,walletName:a,chains:r}of e)await E({page:t,privateKey:o,walletName:a,chains:r,mode:"onboard"});if(e.length>1){let o=e[0];if(o){let{privateKey:l,walletName:d,chains:x}=o;await E({page:t,privateKey:l,walletName:d,chains:x,mode:"onboard"})}let a=e.slice(1);for(let{privateKey:l,walletName:d,chains:x}of a){let v=await $(t);await E({page:v,privateKey:l,walletName:d,chains:x,mode:"add-account-single"})}await t.locator("div",{hasText:"Select Wallet"}).last().locator("../../..").locator("div > svg").click(),await t.getByRole("link",{name:"Home",exact:!0}).click();let p=t.locator(O.openSidebarMenuButton);await It(p).toBeVisible({timeout:3e4});let m=e.at(-1)?.walletName,u=e[0]?.walletName;m&&u&&await U(t,u)}await W(3e3),console.info(dt("greenBright","\u2728 Keplr onboarding completed successfully",{validateStream:!1}))}async function wt(t){let e=t.locator(K.rejectButton);await e.waitFor({state:"visible",timeout:2e4}),await e.click()}async function gt({page:t,currentAccountName:e,newAccountName:o}){let a=pt.parse({currentAccountName:e,newAccountName:o});await t.getByRole("link",{name:"Settings",exact:!0}).click(),await t.locator("div[cursor='pointer']").first().click();let s=t.locator("div",{hasText:a.currentAccountName}).nth(-4);if(!await s.isVisible().catch(()=>!1))throw Error(`Account with name "${a.currentAccountName}" not found`);await s.locator("div[cursor='pointer'] svg").click(),await t.locator("div > div[cursor='pointer'] > div:has-text('Change Wallet Name')").last().click(),await t.locator("input[name='name']").fill(a.newAccountName),await t.locator("button:has-text('Save')").click()}async function M(t){let e=await L("keplr");await t.locator(H.passwordInput).fill(e),await t.locator(H.unlockButton).click(),await t.locator("div:has-text('Deposit')").last().waitFor({state:"visible",timeout:3e4})}var S=class extends g{page;constructor(e){super(),this.page=e}async onboard(e){await j({page:this.page,onboard:e})}async unlock(){await M(this.page)}async lock(){await ut(this.page)}async renameAccount({currentAccountName:e,newAccountName:o}){await gt({page:this.page,currentAccountName:e,newAccountName:o})}async switchAccount(e){await U(this.page,e)}async getAccountAddress({...e}){return await mt({page:this.page,...e})}async addAccount({chains:e,privateKey:o,walletName:a,mode:r="add-account-multiple"}){await it({page:this.page,privateKey:o,walletName:a,chains:e,mode:r})}async connectToApp(){await st(await this.promptPage(this.page.context()))}async confirmTransaction(){await ct(await this.promptPage(this.page.context()))}async rejectTransaction(){await wt(await this.promptPage(this.page.context()))}};import ft from"fs";import Ot from"path";import{test as Dt,chromium as Rt}from"@playwright/test";import{expect as Ft}from"@playwright/test";async function z(t,e){let o=await t.newPage();return await Ft(async()=>{await o.goto(e),await C(o)}).toPass(),o}async function G(t,e){let o=await e.newPage();for(let{origin:a,localStorage:r}of t){let c=o.mainFrame();await c.goto(a),await c.evaluate(s=>{s.forEach(({name:p,value:m})=>{window.localStorage.setItem(p,m)})},r)}await o.close()}import _t from"fs/promises";async function ht(t){await _t.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Nt=35e3;async function V(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Nt))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await ht(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var I,Pa=({slowMo:t=0,profileName:e}={})=>Dt.extend({contextPath:async({browserName:o},a,r)=>{let c=await N(`${o}-${r.testId}`);await a(c)},context:async({context:o,contextPath:a},r)=>{let c=new g,s=w(c.name),p=await R(c.name),m=Ot.resolve(s,e??"wallet-data");if(!ft.existsSync(m))throw new Error("\u274C Cache for Keplr wallet data not found. Create it first");ft.cpSync(m,a,{recursive:!0,force:!0});let u=[`--disable-extensions-except=${p}`,`--load-extension=${p}`];process.env.HEADLESS&&(u.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await Rt.launchPersistentContext(a,{headless:!1,args:u,slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:d,origins:x}=await o.storageState();d&&await l.addCookies(d),x&&x.length>0&&G(x,l);let v=await c.indexUrl();I=l.pages().find(P=>P.url().startsWith(v))||await z(l,v),await C(I);for(let P of l.pages()){let b=P.url();(b.includes("about:blank")||b.includes(c.onboardingPath))&&await P.close()}await I.bringToFront(),await M(I),await r(l),await V(l,a)},keplrPage:async({context:o},a)=>{await a(I)},keplr:async({context:o},a)=>{let r=new S(I);await a(r)}});import{test as Lt}from"@playwright/test";var Wa=({slowMo:t,profileName:e,dappUrl:o}={})=>Lt.extend({workerScopeContents:[async({browser:a},r,c)=>{let s=new g,{context:p,contextPath:m,walletPage:u}=await tt({wallet:s,workerInfo:c,profileName:e,slowMo:t});await p.grantPermissions(["clipboard-read"]);for(let d of p.pages())d.url().includes("about:blank")&&await d.close();let l=new S(u);await l.unlock(),await r({wallet:l,walletPage:u,context:p}),await V(p,m)},{scope:"worker"}],dappPage:[async({workerScopeContents:a},r)=>{let{context:c}=a,s=await c.newPage();o&&await s.goto(o),await r(s)},{scope:"worker"}],keplrPage:async({workerScopeContents:a},r)=>{await r(a.walletPage)},keplr:async({workerScopeContents:a},r)=>{let c=new S(a.walletPage);await r(c)}});export{S as Keplr,Pa as keplrFixture,Wa as keplrWorkerScopeFixture,tt as workerScopeContext};
1
+ import Z from"fs";import vt from"path";import{chromium as bt}from"@playwright/test";import yt from"path";var Y=".wallet-cache",q=".wallet-context";var J="13.22.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Kt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ut=`${_}solflare-wallet-extension-v2.19.1.zip`,Mt=`${_}petra-wallet-extension-v2.4.8.zip`,Vt=`${_}phantom-wallet-extension-v26.10.0.zip`,Ht=`${_}meteor-wallet-extension-v0.7.0.zip`,jt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function N(t){return yt.resolve(process.cwd(),q,t)}import At from"path";function w(t){return At.resolve(process.cwd(),Y,t)}import Q from"fs";import Pt from"path";async function R(t){try{let e=w(t),o=Pt.resolve(e,"extension-path.txt");if(!Q.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=Q.readFileSync(o,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function tt({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let r=await N(e.workerIndex.toString()),s=w(t.name),c=vt.resolve(s,o??"wallet-data");if(!Z.existsSync(c))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);Z.cpSync(c,r,{recursive:!0,force:!0});let p=await R(t.name),m=await bt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:a}),u=await t.indexUrl(),l=m.pages()[0];return l||(l=await m.newPage()),await l.goto(u),{context:m,walletPage:l,contextPath:r}}import{expect as nt}from"@playwright/test";function T(t){return new Promise(e=>setTimeout(e,t))}async function C(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import et from"fs";import kt from"path";async function L(t){let e=w(t),o=kt.resolve(e,"password.txt");try{if(!et.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Ct}from"@playwright/test";async function ot({context:t,path:e,locator:o}){let a;try{await Ct.poll(async()=>(a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(s=>s.url().startsWith("chrome-extension://")).map(s=>s.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await St(a,o),await a.setViewportSize({width:360,height:592}),a}async function St(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import at from"fs";import Bt from"path";async function rt(t){let e=w(t),o=Bt.resolve(e,"extension-id.txt");try{if(!at.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return at.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var h=class{name="keplr";onboardingPath="register.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/sidePanel.html`}async onboardingUrl(){return`chrome-extension://${await this.extensionId()}/${this.onboardingPath}`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async extensionId(){return await rt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ot({context:e,path:o,locator:"div[data-simplebar='init']"})}};var g={importExistingWalletButton:"button:has-text('Import an existing wallet')",usePrivateKeyButton:"button:has-text('Use recovery phrase or private key')",privateKeyTabButton:"button:has-text('Private key')",privateKeyInput:"input[type='password']",importButton:"button:has-text('Import')",walletNameInput:"input[name='name']",walletPasswordInput:"input[name='password']",confirmWalletPasswordInput:"input[name='confirmPassword']",nextButton:"button:has-text('Next')",searchNetworkInput:"input[placeholder='Search networks']",saveButton:"button:has-text('Save')",finishButton:"button:has-text('Finish')"};function Tt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function W({page:t,privateKey:e,walletName:o,chains:a,mode:r="onboard"}){let s=await L("keplr"),c=new h;if(await t.locator(g.importExistingWalletButton).click(),await t.locator(g.usePrivateKeyButton).click(),await t.getByRole("button",{name:"Private key",exact:!0}).click(),await t.locator(g.privateKeyInput).fill(e),await t.getByRole("button",{name:"Import",exact:!0}).click(),await t.locator(g.walletNameInput).fill(o),r==="onboard"){let A=t.locator(g.walletPasswordInput),F=t.locator(g.confirmWalletPasswordInput);await A.fill(s),await F.fill(s)}await t.locator(g.nextButton).click();let y=t.locator("div:has-text('All Native Chains')").nth(-4),P=t.locator("div[cursor='pointer']:has-text('Cosmos Hub')"),b=await y.locator("input[type='checkbox']").getAttribute("checked"),k=await P.locator("input[type='checkbox']").getAttribute("checked");b!==null&&await y.click(),k!==null&&await P.click();let D=t.locator(g.searchNetworkInput);for(let A of a){await D.fill(A);let B=t.locator("div[class='simplebar-content']").locator("div[cursor] > div").first().locator("div").filter({hasText:new RegExp(`^${Tt(A)}$`,"i")}).nth(2).locator("../../../../..");await B.waitFor({state:"visible",timeout:2e4}),await B.locator("input[type='checkbox']").getAttribute("checked")===null&&await B.click()}let S=t.locator(g.saveButton);if(await S.scrollIntoViewIfNeeded(),await S.click(),await T(2e3),r==="onboard"){await t.goto(await c.indexUrl());return}if(r==="add-account-single"){let A=t.locator(g.finishButton);await A.waitFor({state:"visible",timeout:2e4}),await nt(A).toBeEnabled({timeout:2e4}),await A.click()}}async function $(t){let o=await new h().onboardingUrl();await t.getByRole("link",{name:"Settings",exact:!0}).click(),await t.locator("div[cursor='pointer']").first().click(),await t.getByRole("button",{name:"Add Wallet",exact:!0}).click();let c;if(await nt.poll(async()=>(c=t.context().pages().find(p=>p.url().match(o)),!!c),{timeout:3e4}).toBe(!0).catch(p=>{console.error(`Failed to find onboarding page with URL matching ${o}. Original error: ${p}`)}),!c)throw new Error(`Onboarding page not found. Expected URL: ${o}`);return await C(c),await c.bringToFront(),c}async function it({page:t,privateKey:e,chains:o,walletName:a,mode:r}){let s=await $(t);await W({page:s,privateKey:e,walletName:a,chains:o,mode:r}),await t.locator("div:has(div:has-text('Select Wallet'))").nth(-4).locator("div:has(> div > svg)").first().click(),await t.getByRole("link",{name:"Home",exact:!0}).click()}import{expect as Wt}from"@playwright/test";var K={approveButton:"button:has-text('Approve')",rejectButton:"button[color='secondary']"};async function ct(t){let e=t.locator(K.approveButton);await Wt(e).toBeEnabled({timeout:2e4}),await e.click()}async function st(t){t.getByRole("button",{name:"Approve",exact:!0}).click(),await T(1e3)}import{expect as Et}from"@playwright/test";import f from"zod";var lt=f.discriminatedUnion("chain",[f.object({chain:f.literal(["Injective","Injective (Testnet)","Polygon"]),walletName:f.string().min(1,"Wallet name cannot be an empty string")}),f.object({chain:f.literal(["Bitcoin","Bitcoin Signet","Bitcoin Testnet"]),chainTag:f.literal(["Taproot","Native Segwit"]),walletName:f.string().min(1,"Wallet name cannot be an empty string")})]),pt=f.object({currentAccountName:f.string().min(1,"Current account name cannot be an empty string"),newAccountName:f.string().min(1,"New account name cannot be an empty string")});async function mt({page:t,...e}){let o=lt.parse({...e});await t.getByRole("textbox",{name:"Search for asset or chain (i.e. ATOM, Cosmos)",exact:!0}).fill(o.chain);let r=t.locator(`div:has-text("${o.chain}")`).nth(-2).filter({hasNot:t.locator("span")});if(await r.waitFor({state:"attached",timeout:2e4}),!await r.isVisible().catch(()=>!1))throw Error(`Make sure "${o.chain}" is activated.`);let c=await r.locator("div").all();Et(c.length).toBeGreaterThan(0),await t.locator(`div:has(div:has-text('${o.walletName}'))`).nth(-3).locator("div:has(> div > svg)").click();let u=t.locator("div:has(> div[data-simplebar='init'])").last(),d=u.locator("div:has(> div > input)").locator("input");await d.fill(o.chain);let v=await u.locator("div[cursor='pointer']",{hasText:o.chain}).all(),y;for(let b of v){let k;"chainTag"in e&&(k=e.chainTag);let D=b.locator("div",{hasText:o.chain}).last(),S=k?b.locator("div",{hasText:k}).last():null,F=(S?await S?.isVisible().catch(()=>!1):!1)?await S?.textContent():null,B=await D.textContent(),X=F?`${B} ${F}`:B,xt=k?`${o.chain} ${k}`:o.chain;if(X===xt){y=D.locator("xpath=../../../.."),await d.clear();break}}if(!y)throw Error(`Address for ${o.walletName} account on "${o.chain}" chain not found.`);return await y.hover(),await y.scrollIntoViewIfNeeded(),await y.click(),await t.evaluate(async()=>await navigator.clipboard.readText())}var O={openSidebarMenuButton:"div[cursor='pointer']:has(> div[cursor='pointer'])",menuPopupContent:"div[id='modal-root-3']",lockWalletButton:"div:has(> div:has-text('Lock Wallet'))",settingsButton:"div:has(a[href='#/settings'])"},H={unlockButton:"button[type='submit']:has-text('Unlock')",passwordInput:"input[placeholder='Type Your Password']"};async function ut(t){await t.locator(O.openSidebarMenuButton).click(),await t.locator(O.lockWalletButton).nth(-1).click(),await t.getByText("Welcome Back").waitFor({state:"visible",timeout:3e4})}import{styleText as dt}from"util";import{expect as It}from"@playwright/test";async function U(t,e){let o=t.locator("div[color]").nth(1);if(await o.textContent()===e){console.info(`
2
+ Already on ${e} account. No need to switch.`);return}await o.click();let r=t.locator("div[class='simplebar-content'] > div").locator("> div",{hasText:e});if(!await r.isVisible().catch(()=>!1))throw new Error(`Account "${e}" not found. Make sure the account is onboarded or verify the account name.`);let c=t.locator("div:has-text('Select Wallet')").last();await r.click(),await c.waitFor({state:"detached",timeout:3e4})}async function j({page:t,onboard:e}){if(console.info(dt("yellowBright",`
3
+ Keplr onboarding started...`,{validateStream:!1})),e.length===1)for(let{privateKey:o,walletName:a,chains:r}of e)await W({page:t,privateKey:o,walletName:a,chains:r,mode:"onboard"});if(e.length>1){let o=e[0];if(o){let{privateKey:l,walletName:d,chains:x}=o;await W({page:t,privateKey:l,walletName:d,chains:x,mode:"onboard"})}let a=e.slice(1);for(let{privateKey:l,walletName:d,chains:x}of a){let v=await $(t);await W({page:v,privateKey:l,walletName:d,chains:x,mode:"add-account-single"})}await t.locator("div",{hasText:"Select Wallet"}).last().locator("../../..").locator("div > svg").click(),await t.getByRole("link",{name:"Home",exact:!0}).click();let p=t.locator(O.openSidebarMenuButton);await It(p).toBeVisible({timeout:3e4});let m=e.at(-1)?.walletName,u=e[0]?.walletName;m&&u&&await U(t,u)}await T(3e3),console.info(dt("greenBright","\u2728 Keplr onboarding completed successfully",{validateStream:!1}))}async function wt(t){let e=t.locator(K.rejectButton);await e.waitFor({state:"visible",timeout:2e4}),await e.click()}async function ht({page:t,currentAccountName:e,newAccountName:o}){let a=pt.parse({currentAccountName:e,newAccountName:o});await t.getByRole("link",{name:"Settings",exact:!0}).click(),await t.locator("div[cursor='pointer']").first().click();let c=t.locator("div",{hasText:a.currentAccountName}).nth(-4);if(!await c.isVisible().catch(()=>!1))throw Error(`Account with name "${a.currentAccountName}" not found`);await c.locator("div[cursor='pointer'] svg").click(),await t.locator("div > div[cursor='pointer'] > div:has-text('Change Wallet Name')").last().click(),await t.locator("input[name='name']").fill(a.newAccountName),await t.locator("button:has-text('Save')").click()}async function M(t){let e=await L("keplr");await t.locator(H.passwordInput).fill(e),await t.locator(H.unlockButton).click(),await t.locator("div:has-text('Deposit')").last().waitFor({state:"visible",timeout:3e4})}var E=class extends h{page;constructor(e){super(),this.page=e}async onboard(e){await j({page:this.page,onboard:e})}async unlock(){await M(this.page)}async lock(){await ut(this.page)}async renameAccount({currentAccountName:e,newAccountName:o}){await ht({page:this.page,currentAccountName:e,newAccountName:o})}async switchAccount(e){await U(this.page,e)}async getAccountAddress({...e}){return await mt({page:this.page,...e})}async addAccount({chains:e,privateKey:o,walletName:a,mode:r="add-account-multiple"}){await it({page:this.page,privateKey:o,walletName:a,chains:e,mode:r})}async connectToApp(){await st(await this.promptPage(this.page.context()))}async confirmTransaction(){await ct(await this.promptPage(this.page.context()))}async rejectTransaction(){await wt(await this.promptPage(this.page.context()))}};import ft from"fs";import Ot from"path";import{test as Dt,chromium as Rt}from"@playwright/test";import{expect as Ft}from"@playwright/test";async function z(t,e){let o=await t.newPage();return await Ft(async()=>{await o.goto(e),await C(o)}).toPass(),o}async function G(t,e){let o=await e.newPage();for(let{origin:a,localStorage:r}of t){let s=o.mainFrame();await s.goto(a),await s.evaluate(c=>{c.forEach(({name:p,value:m})=>{window.localStorage.setItem(p,m)})},r)}await o.close()}import _t from"fs/promises";async function gt(t){await _t.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Nt=35e3;async function V(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Nt))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await gt(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var I,Pa=({slowMo:t=0,profileName:e}={})=>Dt.extend({contextPath:async({browserName:o},a,r)=>{let s=await N(`${o}-${r.testId}`);await a(s)},context:async({context:o,contextPath:a},r)=>{let s=new h,c=w(s.name),p=await R(s.name),m=Ot.resolve(c,e??"wallet-data");if(!ft.existsSync(m))throw new Error("\u274C Cache for Keplr wallet data not found. Create it first");ft.cpSync(m,a,{recursive:!0,force:!0});let u=[`--disable-extensions-except=${p}`,`--load-extension=${p}`];process.env.HEADLESS&&(u.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await Rt.launchPersistentContext(a,{headless:!1,args:u,slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:d,origins:x}=await o.storageState();d&&await l.addCookies(d),x&&x.length>0&&G(x,l);let v=await s.indexUrl();I=l.pages().find(P=>P.url().startsWith(v))||await z(l,v),await C(I);for(let P of l.pages()){let b=P.url();(b.includes("about:blank")||b.includes(s.onboardingPath))&&await P.close()}await I.bringToFront(),await M(I),await r(l),await V(l,a)},keplrPage:async({context:o},a)=>{await a(I)},keplr:async({context:o},a)=>{let r=new E(I);await a(r)}});import{test as Lt}from"@playwright/test";var Wa=({slowMo:t,profileName:e}={})=>Lt.extend({workerScopeContents:[async({browser:o},a,r)=>{let s=new h,{context:c,contextPath:p,walletPage:m}=await tt({wallet:s,workerInfo:r,profileName:e,slowMo:t});await c.grantPermissions(["clipboard-read"]);for(let l of c.pages())l.url().includes("about:blank")&&await l.close();let u=new E(m);await u.unlock(),await a({wallet:u,walletPage:m,context:c}),await V(c,p)},{scope:"worker"}]});export{E as Keplr,Pa as keplrFixture,Wa as keplrWorkerScopeFixture,tt as workerScopeContext};