chainwright 0.8.9 → 0.8.11
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 +86 -23
- package/dist/cli/index.js +8 -8
- package/dist/core/index.d.ts +1 -1
- package/dist/{types-B56pinWs.d.ts → types-DK8rutb5.d.ts} +1 -6
- package/dist/wallets/keplr/index.d.ts +5 -5
- package/dist/wallets/keplr/index.js +3 -3
- package/dist/wallets/metamask/index.d.ts +5 -5
- package/dist/wallets/metamask/index.js +3 -3
- package/dist/wallets/meteor/index.d.ts +5 -5
- package/dist/wallets/meteor/index.js +7 -7
- package/dist/wallets/petra/index.d.ts +5 -5
- package/dist/wallets/petra/index.js +3 -3
- package/dist/wallets/phantom/index.d.ts +5 -5
- package/dist/wallets/phantom/index.js +2 -2
- package/dist/wallets/solflare/index.d.ts +5 -5
- package/dist/wallets/solflare/index.js +4 -2
- package/dist/{worker-scope-context-DiN3_Sig.d.ts → worker-scope-context-CNAfiliw.d.ts} +1 -1
- package/package.json +2 -2
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 top of Playwright. It helps you prebuild browser extension wallet state, then reuse it in your end-to-end tests through ready-made fixtures.
|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
@@ -75,15 +75,15 @@ Create a setup directory (default: `tests/wallet-setup`) and add `*.setup.ts` fi
|
|
|
75
75
|
- `petra.setup.ts`
|
|
76
76
|
- `phantom-team-a.setup.ts`
|
|
77
77
|
|
|
78
|
-
Each file
|
|
78
|
+
Each file must export `default defineWalletSetup(...)`.
|
|
79
79
|
|
|
80
80
|
```ts
|
|
81
81
|
// tests/wallet-setup/metamask.setup.ts
|
|
82
82
|
import { defineWalletSetup } from "chainwright/core";
|
|
83
83
|
import { Metamask } from "chainwright/metamask";
|
|
84
84
|
|
|
85
|
-
const PASSWORD = "test1234";
|
|
86
|
-
const SEED_PHRASE = "
|
|
85
|
+
const PASSWORD = "test1234"; // For Petra wallet, you have to use a strong password. e.g. PlayerPetra45!!
|
|
86
|
+
const SEED_PHRASE = "test test test test test test test test test test test test test";
|
|
87
87
|
|
|
88
88
|
export default defineWalletSetup(
|
|
89
89
|
PASSWORD,
|
|
@@ -97,25 +97,71 @@ export default defineWalletSetup(
|
|
|
97
97
|
});
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
|
-
|
|
101
|
-
slowMo: 200, // optional
|
|
100
|
+
...//Optional prarmeters here
|
|
102
101
|
},
|
|
103
102
|
);
|
|
104
103
|
```
|
|
105
104
|
|
|
105
|
+
**For Wallets with additional accounts**
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// tests/wallet-setup/metamask.setup.ts
|
|
109
|
+
import { defineWalletSetup } from "chainwright/core";
|
|
110
|
+
import { Petra } from "chainwright/petra";
|
|
111
|
+
|
|
112
|
+
const PASSWORD = "PlayerPetra45!!";
|
|
113
|
+
|
|
114
|
+
export default defineWalletSetup(
|
|
115
|
+
PASSWORD,
|
|
116
|
+
async ({ walletPage }) => {
|
|
117
|
+
const petra = new Petra(walletPage);
|
|
118
|
+
|
|
119
|
+
await petra.onboard({
|
|
120
|
+
mode: "importMnemonic",
|
|
121
|
+
accountName: "default",
|
|
122
|
+
secretRecoveryPhrase: "test test test...", // Seed phrase for the main account
|
|
123
|
+
additionalAccounts: [
|
|
124
|
+
{
|
|
125
|
+
accountName: "nw-account",
|
|
126
|
+
mode: "mnemonic",
|
|
127
|
+
mnemonicPhrase: "test test test..." // Seed Phrase for this account
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
...//Optional prarmeters here
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
```
|
|
137
|
+
|
|
106
138
|
### 2. Build wallet cache
|
|
107
139
|
|
|
108
|
-
Run setup with the CLI:
|
|
140
|
+
Run setup with the CLI (Supports npx, bun, pnpm, and yarn):
|
|
141
|
+
|
|
142
|
+
> NB: 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
143
|
|
|
110
144
|
```bash
|
|
111
|
-
chainwright
|
|
145
|
+
bun chainwright --wallets metamask
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
To specify a directory:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bun chainwright <directory path> <wallet> -f #Optional flag
|
|
112
152
|
```
|
|
113
153
|
|
|
114
154
|
Useful flags:
|
|
115
155
|
|
|
116
156
|
- `-f, --force` overwrite existing cache
|
|
117
|
-
- `--wallets <wallets...>` select wallets (`metamask`, `solflare`, `petra`, `phantom`, `meteor`, `keplr`)
|
|
157
|
+
- `--wallets <wallets...>` select wallets (`metamask`, `solflare`, `petra`, `phantom`, `meteor`, `keplr`). Setup multiple wallets at the same time.
|
|
118
158
|
- `-a, --all` setup all wallets
|
|
159
|
+
- `--kp, --keplr` setup keplr wallet
|
|
160
|
+
- `-m, --metamask` setup metamask wallet
|
|
161
|
+
- `--mt, --meteor` setup the meteor wallet
|
|
162
|
+
- `--pt, --petra` setup petra wallet
|
|
163
|
+
- `--ph, --phantom` setup phantom wallet
|
|
164
|
+
- `-s, --solflare` setup solflare wallet
|
|
119
165
|
|
|
120
166
|
Cache is stored under:
|
|
121
167
|
|
|
@@ -129,22 +175,45 @@ import { expect } from "@playwright/test";
|
|
|
129
175
|
import { testWithChainwright } from "chainwright/core";
|
|
130
176
|
import { metamaskFixture } from "chainwright/metamask";
|
|
131
177
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
178
|
+
// Fixture
|
|
179
|
+
export const testWithMetamask = testWithChainwright(metamaskFixture());
|
|
180
|
+
|
|
181
|
+
// Extend Chainwright's metamaskFixture to suit your need
|
|
182
|
+
export const testDappFixture = testWithMetamask.extend<TestDappFixture>({
|
|
183
|
+
context: async({ context: _ }, use) => {
|
|
184
|
+
//...Context content here
|
|
185
|
+
},
|
|
186
|
+
dappPage: async ({ page, baseURL }, use) => {
|
|
187
|
+
await page.goto(`dApp's url`);
|
|
188
|
+
await use(page);
|
|
189
|
+
},
|
|
190
|
+
});
|
|
137
191
|
|
|
138
192
|
test("connect wallet to dapp", async ({ page, metamask }) => {
|
|
139
193
|
await page.goto("https://your-dapp.example");
|
|
194
|
+
const connectButton = page.getByRole("button", { name: /Connect/i})
|
|
195
|
+
await connectButton.click();
|
|
140
196
|
await metamask.connectToApp("Account 1");
|
|
141
197
|
await expect(page.getByText("Connected")).toBeVisible();
|
|
142
198
|
});
|
|
143
199
|
```
|
|
200
|
+
> N.B: 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
|
+
|
|
202
|
+
```ts
|
|
203
|
+
// No profile name is specified at setup time
|
|
204
|
+
const testWithFixture = testWithChainwright(fixture())
|
|
205
|
+
|
|
206
|
+
// If a profile name is specified at setup time.
|
|
207
|
+
const testWithFixture = testWithChainwright(fixture({ profileName: "profile name" }))
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`Wallet fixture parameters` (Optinoal):
|
|
211
|
+
- `profileName`: string,
|
|
212
|
+
- `slowMo`: number
|
|
144
213
|
|
|
145
214
|
## Worker-Scoped Fixture
|
|
146
215
|
|
|
147
|
-
Use worker-scoped fixtures when you want one wallet context
|
|
216
|
+
Use worker-scoped fixtures when you want one wallet context for the duration of a test suite. This is important for saving time on the setup and teardown cost when running tests in CI
|
|
148
217
|
|
|
149
218
|
```ts
|
|
150
219
|
import { metamaskWorkerScopeFixture } from "chainwright/metamask";
|
|
@@ -160,13 +229,13 @@ test("confirm transaction", async ({ dappPage, metamask }) => {
|
|
|
160
229
|
});
|
|
161
230
|
```
|
|
162
231
|
|
|
163
|
-
`
|
|
232
|
+
`Worker scoped fixture parameters` (Optional):
|
|
164
233
|
|
|
165
234
|
- `profileName?: string`
|
|
166
235
|
- `slowMo?: number`
|
|
167
236
|
- `dappUrl?: string`
|
|
168
237
|
|
|
169
|
-
##
|
|
238
|
+
## Wallets By Module
|
|
170
239
|
|
|
171
240
|
Each wallet module exports:
|
|
172
241
|
|
|
@@ -234,12 +303,6 @@ Additional wallet-specific actions are available, for example:
|
|
|
234
303
|
- Petra/Solflare/Meteor: `switchNetwork`
|
|
235
304
|
- Meteor: `openSettings`
|
|
236
305
|
|
|
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
306
|
## License
|
|
244
307
|
|
|
245
308
|
MIT
|
package/dist/cli/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --import tsx
|
|
2
|
-
import
|
|
3
|
-
${a}`,{encoding:"utf-8"})}catch(l){console.error("Error appending new profile name type: ",l)}}else try{
|
|
4
|
-
`));return await Promise.all(s.map(async({filePath:i,walletName:p})=>{let f=await(await
|
|
5
|
-
`));return await t.close(),n.id}import v from"fs";import
|
|
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
|
|
9
|
-
Setting up cache for ${p}...`,{validateStream:!1})),await
|
|
10
|
-
\u274C Failed to setup cache for ${p}: ${w.message}`,{validateStream:!1}));let y=0;for(;
|
|
11
|
-
\u274C Attempt ${y+1} failed! Retrying...`,{validateStream:!1})),y++,y===
|
|
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};
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as WalletSetupFunction, a as WalletSetupConfig } from '../types-
|
|
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
|
|
19
|
+
export type { SupportedWallets as S, WalletProfileFixtureArgs as W, WalletSetupConfig as a, WalletSetupFunction as b };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WalletProfileFixtureArgs
|
|
2
|
-
import { W as WorkerScopeFixture } from '../../worker-scope-context-
|
|
3
|
-
export { w as workerScopeContext } from '../../worker-scope-context-
|
|
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
|
|
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,
|
|
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
|
|
3
|
-
Keplr onboarding started...`,{validateStream:!1})),e.length===1)for(let{privateKey:o,walletName:a,chains:r}of e)await
|
|
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};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WalletProfileFixtureArgs
|
|
2
|
-
import { W as WorkerScopeFixture } from '../../worker-scope-context-
|
|
3
|
-
export { w as workerScopeContext } from '../../worker-scope-context-
|
|
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 { Page, BrowserContext } from '@playwright/test';
|
|
6
6
|
import z from 'zod';
|
|
@@ -433,6 +433,6 @@ declare class Metamask extends MetamaskProfile {
|
|
|
433
433
|
|
|
434
434
|
declare const metamaskFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & MetamaskFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
435
435
|
|
|
436
|
-
declare const metamaskWorkerScopeFixture: ({ profileName,
|
|
436
|
+
declare const metamaskWorkerScopeFixture: ({ profileName, slowMo }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & MetamaskFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Metamask>>;
|
|
437
437
|
|
|
438
|
-
export { type AddAccountArgs, type AddCustomNetwork, type AnvilNodeInstance, type AnvilNodeOptions, type CreateAnvilNodeResult, type GasFeeSettings, type GetAccountAddressChains, Metamask, type MetamaskFixture, type OnboardingArgs, type SwitchNetwork, WalletProfileFixtureArgs, WorkerScopeFixture,
|
|
438
|
+
export { type AddAccountArgs, type AddCustomNetwork, type AnvilNodeInstance, type AnvilNodeOptions, type CreateAnvilNodeResult, type GasFeeSettings, type GetAccountAddressChains, Metamask, type MetamaskFixture, type OnboardingArgs, type SwitchNetwork, WalletProfileFixtureArgs, WorkerScopeFixture, addCustomNetworkSchema, metamaskFixture, metamaskWorkerScopeFixture };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import dt from"fs";import Vt from"path";import{chromium as Ut}from"@playwright/test";import Wt from"path";var st=".wallet-cache",ct=".wallet-context";var it="13.22.0",U="https://github.com/amaify/chainwright/releases/download/v0.1.0/",we=`https://github.com/MetaMask/metamask-extension/releases/download/v${it}/metamask-chrome-${it}.zip`,fe=`${U}solflare-wallet-extension-v2.19.1.zip`,ge=`${U}petra-wallet-extension-v2.4.8.zip`,ye=`${U}phantom-wallet-extension-v26.10.0.zip`,he=`${U}meteor-wallet-extension-v0.7.0.zip`,xe=`${U}keplr-wallet-extension-v0.13.3.zip`;async function R(t){return Wt.resolve(process.cwd(),ct,t)}import Lt from"path";function h(t){return Lt.resolve(process.cwd(),st,t)}import lt from"fs";import Dt from"path";async function
|
|
2
|
-
\u26A0\uFE0F Skipping test: ${e}`),Ot.skip())}var w={buyButton:"eth-overview-buy",swapButton:"eth-overview-swap",sendButton:"eth-overview-send",receiveButton:"eth-overview-receive",openSettingsButton:"account-options-menu-button",accountMenuButton:"account-menu-icon",accountCell:"multichain-account-cell-entropy",openNetworkSelectorButton:"sort-by-networks",accountAddressesButton:"networks-subtitle-test-id",accountAddressesElements:"multichain-address-row",accountAddressQRCode:"multichain-address-row-qr-button"},M={lockButton:"global-menu-lock",networksButton:"global-menu-networks"},O={passwordInput:"unlock-password",unlockButton:"unlock-submit"},P={accountOptionsMenuButton:"multichain-account-cell-end-accessory",accountDetailsLabel:"Account details",renameAccountLabel:"Rename",addressesLabel:"Addresses",pinToTopLabel:"Pin to top",hideAccountLabel:"Hide account",backButton:"back",addMultichainAccountButton:"add-multichain-account-button",addWalletButton:"account-list-add-wallet-button",importWalletButton:"add-wallet-modal-import-wallet",importAccountButton:"add-wallet-modal-import-account"};var g={createWalletButton:"onboarding-create-wallet",importWalletButton:"onboarding-import-wallet",useSecretRecoveryPhraseButton:"onboarding-create-with-srp-button",createNewPasswordInput:"create-password-new-input",confirmNewPasswordInput:"create-password-confirm-input",confirmPasswordCheckbox:"create-password-terms",createPasswordButton:"create-password-submit",revealSecretRecoveryPhraseButton:"recovery-phrase-reveal",recoveryPhraseRemindMeLaterButton:"recovery-phrase-remind-later",metamaskMetricsIAgreeButton:"metametrics-i-agree",onboardingDoneButton:"onboarding-complete-done",importUsingSecretRecoveryPhraseButton:"onboarding-import-with-srp-button",secretRecoveryPhraseTextAreaInput:"srp-input-import__srp-note",importWalletConfirmButton:"import-srp-confirm",importAccountConfirmButton:"import-account-confirm-button",importSRPError:"bannerAlert"};async function pt({page:t,privateKey:e,accountName:o}){let n=ut.string().min(1,"Account name cannot be an empty string").trim().parse(o),a=ut.string().min(1,"Private key cannot be an empty string").trim().parse(e),r=t.getByTestId(w.accountMenuButton);await x(r).toBeVisible({timeout:3e4}),await r.click(),await x(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let i=t.getByTestId(P.addWalletButton),c=await i.textContent();c?.includes("Syncing")&&await x.poll(async()=>(await i.textContent())?.trim()??"",{timeout:12e4}).not.toBe(c),await x(i).toBeEnabled({timeout:6e4}),await i.click();let s=t.getByRole("dialog");await x(s).toContainText(/add wallet/i),await t.getByTestId(P.importAccountButton).click(),await t.locator("input[id='private-key-box']").fill(a);let p=t.getByTestId(g.importAccountConfirmButton);await x(p).toBeEnabled(),await p.click();let f=t.getByTestId(g.importSRPError),B=await f.isVisible().catch(()=>!1);B&&mt(B,`${(await f.textContent())?.split(".")[0]}`);let S=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])"),A=(await S.textContent())?.split("$")[0];A&&await $t({page:t,accountName:n,activeAccountLocator:S,activeAccountName:A}),await t.locator("button[aria-label='Back']").first().click()}async function $t({page:t,accountName:e,activeAccountLocator:o,activeAccountName:n}){let a=o.locator(`div[aria-label='${n} options']`);await x(a).toBeVisible(),await a.click(),await x(t.getByRole("tooltip")).toBeVisible();let r=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await x(r).toBeVisible(),await r.click();let i=t.getByRole("dialog"),c=i.getByRole("heading",{name:/rename/i});await x(c).toBeVisible();let s=i.getByRole("textbox");await x(s).toBeVisible(),await s.fill(e);let u=i.getByRole("button",{name:/confirm/i});await x(u).toBeEnabled(),await u.click(),await i.waitFor({state:"detached",timeout:2e4});let l=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])");await x(l).toContainText(e)}import{expect as $}from"@playwright/test";import W from"zod";var wt=W.object({networkName:W.string().min(1,"Network name cannot be an empty string"),rpcUrl:W.url(),chainId:W.number().or(W.string().includes("0x")),currencySymbol:W.string().toUpperCase().min(1,"Currency symbol cannot be an empty string")});import{expect as Ht}from"@playwright/test";async function j(t){let e=t.locator("div:has(> p[data-testid='notifications-tag-counter__unread-dot'])"),o=t.getByTestId(w.openSettingsButton);await e.isVisible().catch(()=>!1)?await e.click():(await Ht(o).toBeVisible(),await o.click())}async function ft({page:t,...e}){let{chainId:o,currencySymbol:n,networkName:a,rpcUrl:r}=wt.parse({...e},{reportInput:!0});await j(t),await t.getByTestId(M.networksButton).click();let c=t.locator("section[role='dialog']");await $(c).toContainText(/manage networks/i),await t.getByRole("button",{name:/add a custom network/i}).click(),await $(c).toContainText(/Add a custom network/i);let u=t.getByTestId("network-form-network-name"),l=t.getByTestId("test-add-rpc-drop-down"),p=t.getByTestId("network-form-chain-id"),f=t.getByTestId("network-form-ticker-input");await u.fill(a),await l.click(),await t.getByRole("tooltip").locator("div:has(> button:has-text('Add RPC URL'))").click(),await $(c).toContainText(/Add RPC URL/i);let A=t.getByTestId("rpc-url-input-test"),k=t.getByRole("button",{name:/Add URL/i});await A.fill(r),await k.click(),await $(c).toContainText(/Add a custom network/i);let y=t.getByTestId("network-form-chain-id-error");if(await y.isVisible().catch(()=>!1)){let T=await y.textContent();throw Error(`RPC error: ${T}`)}await p.fill(`${o}`),await f.fill(n);let I=t.getByRole("button",{name:/save/i});await $(I).toBeEnabled(),await I.click()}import{expect as gt}from"@playwright/test";function v(t){return new Promise(e=>setTimeout(e,t))}var q={confirmButton:"confirm-footer-button",cancelButton:"confirm-footer-cancel-button"};async function yt(t,e){let o=t.getByTestId(q.confirmButton);await v(2e3);let a=(await o.textContent())?.includes("Review alert"),r=await o.isDisabled().catch(()=>!1);if(a&&r){await t.getByTestId("edit-gas-fees-row").locator("> div").first().click();let l=t.getByRole("dialog");await gt(l).toBeVisible();let p=l.locator("h4",{hasText:"Insufficient funds"}),f=await l.getByTestId("alert-modal__selected-alert").textContent();if(await p.isVisible().catch(()=>!1))throw Error(`${f}`)}if(e){let s=t.getByTestId("edit-gas-fee-icon");if(await s.scrollIntoViewIfNeeded(),await s.click(),e.feeType!=="advanced"&&await t.getByTestId(`gas-option-${e.feeType}`).click(),e.feeType==="advanced"){await t.getByTestId("gas-option-advanced").click();let l=t.getByRole("textbox",{name:"Max base fee"}),p=t.getByRole("textbox",{name:"Priority fee"}),f=t.getByRole("button",{name:"Save",exact:!0});await l.fill(e.maxBaseFee),await p.fill(e.priorityFee),await f.click()}}await gt(o).toBeEnabled(),await o.click();let i=t.getByRole("dialog");await i.isVisible().catch(()=>!1)&&await i.locator("h4",{hasText:"Your assets may be at risk"}).isVisible().catch(()=>!1)&&(await i.getByTestId("alert-modal-acknowledge-checkbox").click(),await i.getByTestId("confirm-alert-modal-submit-button").click()),await t.waitForEvent("close",{timeout:15e3})}import{expect as Gt}from"@playwright/test";import{expect as Y}from"@playwright/test";async function L({page:t,accountName:e}){let o=t.getByTestId(w.accountMenuButton);if(await o.textContent()===e){console.info(`Can't switch account to "${e}", it is already selected.`);return}await Y(o).toBeVisible({timeout:15e3}),await o.click(),await Y(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let a=t.getByTestId(P.addMultichainAccountButton),r=await a.textContent();r?.includes("Syncing")&&await Y.poll(async()=>(await a.textContent())?.trim()??"",{timeout:6e4}).not.toBe(r);let i=await t.getByTestId(/^multichain-account-cell-(?:entropy|keyring):/).all(),c=null;for(let s of i)if(await s.scrollIntoViewIfNeeded(),(await s.textContent())?.includes(e)){c=s;break}if(!c)throw Error(`Account with name "${e}" not found.`);await c?.click()}async function ht(t,e){e&&await L({page:t,accountName:e});let o=t.getByRole("button",{name:"Connect",exact:!0});await o.waitFor({state:"visible",timeout:25e3}),await o.click(),await t.getByRole("heading",{name:"Connecting",exact:!0}).waitFor({state:"detached",timeout:3e4});let a;await Gt.poll(async()=>(a=await t.locator("div[class='permissions-connect']").isVisible().catch(()=>!1),a),{timeout:25e3}).toBe(!0).catch(()=>console.error("Notice dialog did not appear within the timeout period."));let r=t.getByTestId("page-container-footer-next");await r.waitFor({state:"visible",timeout:25e3}),await r.click(),await t.waitForEvent("close",{predicate:()=>!0,timeout:25e3}).catch(()=>console.error("Extension popup did not close within the timeout period when connecting to the DApp."))}async function xt(t,e){return await t.getByTestId(w.accountAddressesButton).hover(),await t.getByTestId("multichain-address-rows-list").getByRole("button",{name:/view all/i}).click(),await t.getByRole("searchbox",{name:/search networks/i}).fill(e),await t.locator(`div[data-testid='${w.accountAddressesElements}']:has-text('${e}')`).getByTestId(w.accountAddressQRCode).click(),await t.getByRole("dialog").locator("div > p[data-testid='account-address']").textContent()}import{expect as qt}from"@playwright/test";import{errors as zt}from"@playwright/test";async function E(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}var Bt={loadingOverlay:"loading-overlay",loadingSpinner:"spinner loading-overlay__spinner"};var Kt=6e4,jt=async(t,e,o)=>{await E(e);try{await e.locator(`div[class="${t}"]`).waitFor({state:"detached",timeout:o})}catch(n){if(n instanceof zt.TimeoutError)console.info(`Loading indicator '${t}' not found - continuing.`);else throw console.error(`Error while waiting for loading indicator '${t}' to disappear`),n}},X=async t=>{try{await jt(Bt.loadingSpinner,t,Kt)}catch(e){console.warn("Warning during MetaMask load:",e)}return await v(300),t};async function kt(t){if(await t.getByTestId(O.unlockButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already locked");return}await j(t);let n=t.getByTestId(M.lockButton);await qt(n).toBeVisible(),await n.click(),await X(t)}import{styleText as Pt}from"util";import{expect as V}from"@playwright/test";import bt from"fs";import Xt from"path";async function Q(t){let e=h(t),o=Xt.resolve(e,"password.txt");try{if(!bt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return bt.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} password from cache: ${n.message}`)}}import{expect as Qt}from"@playwright/test";async function At({context:t,path:e,locator:o}){let n;try{await Qt.poll(async()=>(n=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(e)),!!n),{timeout:3e4}).toBe(!0)}catch{let a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).map(r=>r.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(a)}`)}if(!n)throw new Error(`Popup page with path ${e} not found in context.`);return await Jt(n,o),await n.setViewportSize({width:360,height:592}),n}async function Jt(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 Ct from"fs";import Yt from"path";async function Tt(t){let e=h(t),o=Yt.resolve(e,"extension-id.txt");try{if(!Ct.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return Ct.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${n.message}`)}}var C=class{name="metamask";onboardingPath="/home.html#onboarding";async indexUrl(){return`chrome-extension://${await this.extensionId()}/home.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await Tt(this.name)}async promptPage(e){let o=await this.promptUrl();return await At({context:e,path:o,locator:"div[data-testid='multichain-page']"})}};import{expect as Z}from"@playwright/test";async function D({page:t}){await t.locator(`div:has(> button[data-testid='${w.openSettingsButton}'])`).click(),await t.getByTestId(M.networksButton).click();let n=t.locator("section[role='dialog']");await Z(n).toBeVisible(),await Z(n).toContainText(/manage networks/i);let a="div:has(> p:has-text('Show test networks'))";await n.locator(a).scrollIntoViewIfNeeded();let r=n.locator(a);if(!await r.locator("label[class='toggle-button toggle-button--off']").isVisible().catch(()=>!1)){await n.getByRole("button",{name:/close/i}).click(),console.info("Testnet networks are already visible.");return}await r.locator("label[class='toggle-button toggle-button--off']").click(),await t.getByTestId("Sepolia").scrollIntoViewIfNeeded(),await Z(t.getByTestId("Sepolia")).toBeVisible(),await n.getByRole("button",{name:/close/i}).click()}async function tt({page:t,mainAccountName:e,...o}){console.info(Pt("yellowBright",`
|
|
3
|
-
\u{1F98A} MetaMask onboarding started...`,{validateStream:!1}));let n=await Q("metamask"),a=new C,r=t.locator("img[class='loading-spinner']"),i=t.getByTestId(g.createWalletButton),c=t.getByTestId(g.importWalletButton),s=t.getByTestId(g.createNewPasswordInput),u=t.getByTestId(g.confirmNewPasswordInput),l=t.getByTestId(g.confirmPasswordCheckbox),p=t.getByTestId(g.createPasswordButton),f=t.getByTestId(g.metamaskMetricsIAgreeButton),B=t.getByTestId(g.onboardingDoneButton);if(await r.waitFor({state:"detached",timeout:3e4}),o.mode==="create"){let I=t.getByTestId(g.useSecretRecoveryPhraseButton);await i.click(),await I.click(),await s.fill(n),await u.fill(n),await l.click(),await p.click(),await t.getByTestId(g.revealSecretRecoveryPhraseButton).click(),await t.getByTestId(g.recoveryPhraseRemindMeLaterButton).click(),await f.click()}if(o.mode==="import"){let{secretRecoveryPhrase:I}=o,T=I.split(" "),z=t.getByTestId(g.importUsingSecretRecoveryPhraseButton);await c.click(),await z.click();let at=t.getByTestId(g.secretRecoveryPhraseTextAreaInput);await at.fill(T[0]),await at.press("Space");for(let K=1;K<T.length;K++){let rt=t.getByTestId(`import-srp__srp-word-${K}`);await rt.fill(T[K]),await rt.press("Space")}await t.getByTestId(g.importWalletConfirmButton).click(),await s.fill(n),await u.fill(n),await l.click(),await p.click(),await f.click();let Mt=t.getByTestId("wallet-ready");await V(Mt).toContainText(/your wallet is ready/i)}await B.click();let A=`chrome-extension://${await a.extensionId()}/sidepanel.html`,y=await t.context().browser()?.newBrowserCDPSession(),F;await V.poll(async()=>{if(y){let{targetInfos:I}=await y.send("Target.getTargets"),T=I.find(z=>z.url===A);return F=T,!!T}},{timeout:15e3}).toBe(!0),F&&await y?.send("Target.closeTarget",{targetId:F.targetId}),await t.goto(await a.indexUrl()),await r.waitFor({state:"detached",timeout:3e4}),await V(t.getByTestId(w.buyButton)).toBeVisible(),await V(t.getByTestId(w.swapButton)).toBeVisible(),await V(t.getByTestId(w.sendButton)).toBeVisible(),await V(t.getByTestId(w.receiveButton)).toBeVisible(),await D({page:t}),e&&await L({page:t,accountName:e}),await v(5e3),console.info(Pt("greenBright","\u2728 MetaMask onboarding completed successfully",{validateStream:!1}))}import{expect as Zt}from"@playwright/test";async function St(t){let e=t.getByTestId(q.cancelButton);await v(1e3),await Zt(e).toBeEnabled(),await e.click()}import{expect as b}from"@playwright/test";async function It({page:t,currentAccountName:e,newAccountName:o}){let n=t.getByTestId(w.accountMenuButton);if(await n.textContent()===o)throw Error(`The account to be renamed "${o}" already exists.`);await b(n).toBeVisible({timeout:15e3}),await n.click(),await b(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let r=t.getByTestId(P.addMultichainAccountButton),i=await r.textContent();i?.includes("Syncing")&&await b.poll(async()=>(await r.textContent())?.trim()??"",{timeout:6e4}).not.toBe(i);let c=await t.getByTestId(/^multichain-account-cell/).all(),s=null;for(let y of c)if((await y.textContent())?.includes(e)){s=y;break}if(!s)throw Error(`Account with name "${e}" not found.`);if((await s.textContent())?.split("$")[0]===o)throw Error(`The new account name "${o}" is the same as the old account name "${e}".`);let l=t.locator(`div[aria-label='${e} options']`);await b(l).toBeVisible(),await l.click(),await b(t.getByRole("tooltip")).toBeVisible();let p=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await b(p).toBeVisible(),await p.click();let f=t.getByRole("dialog"),B=f.getByRole("heading",{name:/rename/i});await b(B).toBeVisible();let S=f.getByRole("textbox");await b(S).toBeVisible(),await S.fill(o);let A=f.getByRole("button",{name:/confirm/i});await b(A).toBeEnabled(),await A.click();for(let y of c)if((await y.textContent())?.includes(o)){await b(y).toBeVisible(),await b(y).toContainText(o);break}await t.locator("button[aria-label='Back']").first().click()}import{expect as et}from"@playwright/test";async function vt({page:t,networkType:e,chainName:o}){let n=t.getByTestId(w.openNetworkSelectorButton);await n.click();let a=t.getByTestId("modal-header-close-button");if(e==="testnet"||e==="custom"){let s=t.getByRole("tab",{name:"Custom"});await s.click(),await t.locator("p:has-text('Testnets')").isVisible().catch(()=>!1)||(await a.click(),await D({page:t}),await n.click(),await s.click());let p=t.locator(`div div[data-testid='${o}']`),f=await p.textContent();et(f).toBe(o),await p.click();return}await t.getByRole("tab",{name:"Popular"}).click();let i=t.locator(`div div[data-testid='${o}']`),c=await i.textContent();et(c).toBe(o),await i.click(),await et(n).toContainText(o,{timeout:3e4})}import{expect as Rt}from"@playwright/test";async function H(t){let e=await Q("metamask"),o=t.getByTestId(O.passwordInput);if(await t.getByTestId(w.openNetworkSelectorButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await o.fill(e);let r=t.getByTestId(O.unlockButton);await Rt(r).toBeVisible(),await r.click(),await X(t),await Rt(t.getByTestId(w.buyButton)).toBeVisible({timeout:3e4})}var N=class extends C{page;constructor(e){super(),this.page=e}async onboard(e){await tt({page:this.page,...e})}async unlock(){await H(this.page)}async lock(){await kt(this.page)}async renameAccount({newAccountName:e,currentAccountName:o}){await It({page:this.page,newAccountName:e,currentAccountName:o})}async addAccount({privateKey:e,accountName:o}){await pt({page:this.page,privateKey:e,accountName:o})}async switchAccount({accountName:e}){await L({page:this.page,accountName:e})}async switchNetwork({...e}){await vt({page:this.page,...e})}async getAccountAddress(e){return await xt(this.page,e)}async toggleShowTestnetNetwork(){await D({page:this.page})}async addCustomNetwork({chainId:e,currencySymbol:o,networkName:n,rpcUrl:a}){await ft({page:this.page,chainId:e,currencySymbol:o,networkName:n,rpcUrl:a})}async connectToApp(e){await ht(await this.promptPage(this.page.context()),e)}async confirmTransaction(e){await yt(await this.promptPage(this.page.context()),e)}async rejectTransaction(){await St(await this.promptPage(this.page.context()))}};import Nt from"fs";import ne from"path";import{test as ae,chromium as re}from"@playwright/test";import{Instance as ie,Pool as se}from"prool";import{expect as te}from"@playwright/test";async function ot(t,e){let o=await t.newPage();return await te(async()=>{await o.goto(e),await E(o)}).toPass(),o}async function nt(t,e){let o=await e.newPage();for(let{origin:n,localStorage:a}of t){let r=o.mainFrame();await r.goto(n),await r.evaluate(i=>{i.forEach(({name:c,value:s})=>{window.localStorage.setItem(c,s)})},a)}await o.close()}import ee from"fs/promises";async function Et(t){await ee.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var oe=35e3;async function J(t,e){try{await Promise.race([t.close(),new Promise((o,n)=>setTimeout(()=>n(new Error("Context close timed out")),oe))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await Et(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var G,Ua=({slowMo:t=0,profileName:e}={})=>ae.extend({contextPath:async({browserName:o},n,a)=>{let r=await R(`${o}-${a.testId}`);await n(r)},context:async({context:o,contextPath:n},a)=>{let r=new C,i=h(r.name),c=await _(r.name),s=ne.resolve(i,e??"wallet-data");if(!Nt.existsSync(s))throw new Error("\u274C Cache for MetaMask wallet data not found. Create it first");Nt.cpSync(s,n,{recursive:!0,force:!0});let u=[`--disable-extensions-except=${c}`,`--load-extension=${c}`];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 re.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:t}),{cookies:p,origins:f}=await o.storageState();p&&await l.addCookies(p),f&&f.length>0&&nt(f,l);let B=await r.indexUrl();await l.waitForEvent("page",{predicate:k=>k.url().startsWith(B),timeout:3e4}),G=l.pages().find(k=>k.url().startsWith(B))??await ot(l,B);for(let k of l.pages())k.url().includes("about:blank")&&await k.close();await G.locator("img[class='loading-spinner']").waitFor({state:"detached"}),await H(G),await a(l),await J(l,n)},metamaskPage:async({context:o},n)=>{await n(G)},metamask:async({context:o},n)=>{let a=new N(G);await n(a)},createAnvilNode:async({context:o},n,a)=>{let r=a.workerIndex,i;await n(async c=>{i=se.define({instance:ie.anvil(c)});let s=await i.start(r),u=`http://${s.host}:${s.port}`,p=c?.chainId??31337;return{rpcUrl:u,anvil:s,chainId:p}}),i&&await i.stop(r)},connectToAnvil:async({context:o,metamask:n,createAnvilNode:a},r)=>{await r(async()=>{let{chainId:i,rpcUrl:c}=await a({chainId:2251});await n.addCustomNetwork({chainId:i,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:c})})}});import{test as de}from"@playwright/test";import{Instance as me,Pool as ue}from"prool";import Ft from"fs";import ce from"path";import{chromium as le}from"@playwright/test";async function _t({workerInfo:t,profileName:e,slowMo:o}){let n=new C,a=await R(t.workerIndex.toString()),r=h(n.name),i=ce.resolve(r,e??"wallet-data");if(!Ft.existsSync(i))throw new Error(`Cache for ${n.name} does not exist. Create it first!`);Ft.cpSync(i,a,{recursive:!0,force:!0});let c=await _(n.name),s=await le.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:o}),u=await n.indexUrl();await s.waitForEvent("page",{predicate:p=>p.url().startsWith(u),timeout:4e4});let l=s.pages().find(p=>p.url().startsWith(u));l||(l=await s.newPage(),await l.goto(u),await E(l));for(let p of s.pages())p.url().includes("about:blank")&&await p.close();return{context:s,walletPage:l,contextPath:a}}var ar=({profileName:t,dappUrl:e,slowMo:o}={})=>de.extend({workerScopeContents:[async({browser:n},a,r)=>{let{context:i,contextPath:c,walletPage:s}=await _t({workerInfo:r,profileName:t,slowMo:o});await i.grantPermissions(["clipboard-read"]);let u=new N(s);await u.unlock(),await a({wallet:u,walletPage:s,context:i}),await J(i,c)},{scope:"worker"}],dappPage:[async({workerScopeContents:n},a)=>{let{context:r}=n,i=await r.newPage();e&&await i.goto(e),await a(i)},{scope:"worker"}],metamaskPage:async({workerScopeContents:n},a)=>{await a(n.walletPage)},metamask:async({workerScopeContents:n},a)=>{let r=new N(n.walletPage);await a(r)},createAnvilNode:async({context:n},a,r)=>{let i=r.workerIndex,c;await a(async s=>{c=ue.define({instance:me.anvil(s)});let u=await c.start(i),l=`http://${u.host}:${u.port}`,f=s?.chainId??31337;return{rpcUrl:l,anvil:u,chainId:f}}),c&&await c.stop(i)},connectToAnvil:async({context:n,metamask:a,createAnvilNode:r},i)=>{await i(async()=>{let{chainId:c,rpcUrl:s}=await r({chainId:2251});await a.addCustomNetwork({chainId:c,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:s})})}});export{N as Metamask,Ua as metamaskFixture,ar as metamaskWorkerScopeFixture,Oe as workerScopeContext};
|
|
1
|
+
import dt from"fs";import Vt from"path";import{chromium as Ut}from"@playwright/test";import Wt from"path";var st=".wallet-cache",ct=".wallet-context";var it="13.22.0",U="https://github.com/amaify/chainwright/releases/download/v0.1.0/",we=`https://github.com/MetaMask/metamask-extension/releases/download/v${it}/metamask-chrome-${it}.zip`,fe=`${U}solflare-wallet-extension-v2.19.1.zip`,ge=`${U}petra-wallet-extension-v2.4.8.zip`,ye=`${U}phantom-wallet-extension-v26.10.0.zip`,he=`${U}meteor-wallet-extension-v0.7.0.zip`,xe=`${U}keplr-wallet-extension-v0.13.3.zip`;async function R(t){return Wt.resolve(process.cwd(),ct,t)}import Lt from"path";function h(t){return Lt.resolve(process.cwd(),st,t)}import lt from"fs";import Dt from"path";async function F(t){try{let e=h(t),o=Dt.resolve(e,"extension-path.txt");if(!lt.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let n=lt.readFileSync(o,"utf-8").trim();if(!n)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return n}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function Oe({wallet:t,workerInfo:e,profileName:o,slowMo:n}){let r=await R(e.workerIndex.toString()),a=h(t.name),i=Vt.resolve(a,o??"wallet-data");if(!dt.existsSync(i))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);dt.cpSync(i,r,{recursive:!0,force:!0});let c=await F(t.name),s=await Ut.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:n}),p=await t.indexUrl(),l=s.pages()[0];return l||(l=await s.newPage()),await l.goto(p),{context:s,walletPage:l,contextPath:r}}import{expect as x}from"@playwright/test";import ut from"zod";import{test as Ot}from"@playwright/test";function mt(t,e){t&&(console.warn(`
|
|
2
|
+
\u26A0\uFE0F Skipping test: ${e}`),Ot.skip())}var w={buyButton:"eth-overview-buy",swapButton:"eth-overview-swap",sendButton:"eth-overview-send",receiveButton:"eth-overview-receive",openSettingsButton:"account-options-menu-button",accountMenuButton:"account-menu-icon",accountCell:"multichain-account-cell-entropy",openNetworkSelectorButton:"sort-by-networks",accountAddressesButton:"networks-subtitle-test-id",accountAddressesElements:"multichain-address-row",accountAddressQRCode:"multichain-address-row-qr-button"},_={lockButton:"global-menu-lock",networksButton:"global-menu-networks"},O={passwordInput:"unlock-password",unlockButton:"unlock-submit"},P={accountOptionsMenuButton:"multichain-account-cell-end-accessory",accountDetailsLabel:"Account details",renameAccountLabel:"Rename",addressesLabel:"Addresses",pinToTopLabel:"Pin to top",hideAccountLabel:"Hide account",backButton:"back",addMultichainAccountButton:"add-multichain-account-button",addWalletButton:"account-list-add-wallet-button",importWalletButton:"add-wallet-modal-import-wallet",importAccountButton:"add-wallet-modal-import-account"};var g={createWalletButton:"onboarding-create-wallet",importWalletButton:"onboarding-import-wallet",useSecretRecoveryPhraseButton:"onboarding-create-with-srp-button",createNewPasswordInput:"create-password-new-input",confirmNewPasswordInput:"create-password-confirm-input",confirmPasswordCheckbox:"create-password-terms",createPasswordButton:"create-password-submit",revealSecretRecoveryPhraseButton:"recovery-phrase-reveal",recoveryPhraseRemindMeLaterButton:"recovery-phrase-remind-later",metamaskMetricsIAgreeButton:"metametrics-i-agree",onboardingDoneButton:"onboarding-complete-done",importUsingSecretRecoveryPhraseButton:"onboarding-import-with-srp-button",secretRecoveryPhraseTextAreaInput:"srp-input-import__srp-note",importWalletConfirmButton:"import-srp-confirm",importAccountConfirmButton:"import-account-confirm-button",importSRPError:"bannerAlert"};async function pt({page:t,privateKey:e,accountName:o}){let n=ut.string().min(1,"Account name cannot be an empty string").trim().parse(o),r=ut.string().min(1,"Private key cannot be an empty string").trim().parse(e),a=t.getByTestId(w.accountMenuButton);await x(a).toBeVisible({timeout:3e4}),await a.click(),await x(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let i=t.getByTestId(P.addWalletButton),c=await i.textContent();c?.includes("Syncing")&&await x.poll(async()=>(await i.textContent())?.trim()??"",{timeout:12e4}).not.toBe(c),await x(i).toBeEnabled({timeout:6e4}),await i.click();let s=t.getByRole("dialog");await x(s).toContainText(/add wallet/i),await t.getByTestId(P.importAccountButton).click(),await t.locator("input[id='private-key-box']").fill(r);let u=t.getByTestId(g.importAccountConfirmButton);await x(u).toBeEnabled(),await u.click();let f=t.getByTestId(g.importSRPError),B=await f.isVisible().catch(()=>!1);B&&mt(B,`${(await f.textContent())?.split(".")[0]}`);let S=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])"),A=(await S.textContent())?.split("$")[0];A&&await $t({page:t,accountName:n,activeAccountLocator:S,activeAccountName:A}),await t.locator("button[aria-label='Back']").first().click()}async function $t({page:t,accountName:e,activeAccountLocator:o,activeAccountName:n}){let r=o.locator(`div[aria-label='${n} options']`);await x(r).toBeVisible(),await r.click(),await x(t.getByRole("tooltip")).toBeVisible();let a=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await x(a).toBeVisible(),await a.click();let i=t.getByRole("dialog"),c=i.getByRole("heading",{name:/rename/i});await x(c).toBeVisible();let s=i.getByRole("textbox");await x(s).toBeVisible(),await s.fill(e);let p=i.getByRole("button",{name:/confirm/i});await x(p).toBeEnabled(),await p.click(),await i.waitFor({state:"detached",timeout:2e4});let l=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])");await x(l).toContainText(e)}import{expect as $}from"@playwright/test";import M from"zod";var wt=M.object({networkName:M.string().min(1,"Network name cannot be an empty string"),rpcUrl:M.url(),chainId:M.number().or(M.string().includes("0x")),currencySymbol:M.string().toUpperCase().min(1,"Currency symbol cannot be an empty string")});import{expect as Ht}from"@playwright/test";async function j(t){let e=t.locator("div:has(> p[data-testid='notifications-tag-counter__unread-dot'])"),o=t.getByTestId(w.openSettingsButton);await e.isVisible().catch(()=>!1)?await e.click():(await Ht(o).toBeVisible(),await o.click())}async function ft({page:t,...e}){let{chainId:o,currencySymbol:n,networkName:r,rpcUrl:a}=wt.parse({...e},{reportInput:!0});await j(t),await t.getByTestId(_.networksButton).click();let c=t.locator("section[role='dialog']");await $(c).toContainText(/manage networks/i),await t.getByRole("button",{name:/add a custom network/i}).click(),await $(c).toContainText(/Add a custom network/i);let p=t.getByTestId("network-form-network-name"),l=t.getByTestId("test-add-rpc-drop-down"),u=t.getByTestId("network-form-chain-id"),f=t.getByTestId("network-form-ticker-input");await p.fill(r),await l.click(),await t.getByRole("tooltip").locator("div:has(> button:has-text('Add RPC URL'))").click(),await $(c).toContainText(/Add RPC URL/i);let A=t.getByTestId("rpc-url-input-test"),k=t.getByRole("button",{name:/Add URL/i});await A.fill(a),await k.click(),await $(c).toContainText(/Add a custom network/i);let y=t.getByTestId("network-form-chain-id-error");if(await y.isVisible().catch(()=>!1)){let T=await y.textContent();throw Error(`RPC error: ${T}`)}await u.fill(`${o}`),await f.fill(n);let I=t.getByRole("button",{name:/save/i});await $(I).toBeEnabled(),await I.click()}import{expect as gt}from"@playwright/test";function v(t){return new Promise(e=>setTimeout(e,t))}var q={confirmButton:"confirm-footer-button",cancelButton:"confirm-footer-cancel-button"};async function yt(t,e){let o=t.getByTestId(q.confirmButton);await v(2e3);let r=(await o.textContent())?.includes("Review alert"),a=await o.isDisabled().catch(()=>!1);if(r&&a){await t.getByTestId("edit-gas-fees-row").locator("> div").first().click();let l=t.getByRole("dialog");await gt(l).toBeVisible();let u=l.locator("h4",{hasText:"Insufficient funds"}),f=await l.getByTestId("alert-modal__selected-alert").textContent();if(await u.isVisible().catch(()=>!1))throw Error(`${f}`)}if(e){let s=t.getByTestId("edit-gas-fee-icon");if(await s.scrollIntoViewIfNeeded(),await s.click(),e.feeType!=="advanced"&&await t.getByTestId(`gas-option-${e.feeType}`).click(),e.feeType==="advanced"){await t.getByTestId("gas-option-advanced").click();let l=t.getByRole("textbox",{name:"Max base fee"}),u=t.getByRole("textbox",{name:"Priority fee"}),f=t.getByRole("button",{name:"Save",exact:!0});await l.fill(e.maxBaseFee),await u.fill(e.priorityFee),await f.click()}}await gt(o).toBeEnabled(),await o.click();let i=t.getByRole("dialog");await i.isVisible().catch(()=>!1)&&await i.locator("h4",{hasText:"Your assets may be at risk"}).isVisible().catch(()=>!1)&&(await i.getByTestId("alert-modal-acknowledge-checkbox").click(),await i.getByTestId("confirm-alert-modal-submit-button").click()),await t.waitForEvent("close",{timeout:15e3})}import{expect as Gt}from"@playwright/test";import{expect as Y}from"@playwright/test";async function W({page:t,accountName:e}){let o=t.getByTestId(w.accountMenuButton);if(await o.textContent()===e){console.info(`Can't switch account to "${e}", it is already selected.`);return}await Y(o).toBeVisible({timeout:15e3}),await o.click(),await Y(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let r=t.getByTestId(P.addMultichainAccountButton),a=await r.textContent();a?.includes("Syncing")&&await Y.poll(async()=>(await r.textContent())?.trim()??"",{timeout:6e4}).not.toBe(a);let i=await t.getByTestId(/^multichain-account-cell-(?:entropy|keyring):/).all(),c=null;for(let s of i)if(await s.scrollIntoViewIfNeeded(),(await s.textContent())?.includes(e)){c=s;break}if(!c)throw Error(`Account with name "${e}" not found.`);await c?.click()}async function ht(t,e){e&&await W({page:t,accountName:e});let o=t.getByRole("button",{name:"Connect",exact:!0});await o.waitFor({state:"visible",timeout:25e3}),await o.click(),await t.getByRole("heading",{name:"Connecting",exact:!0}).waitFor({state:"detached",timeout:3e4});let r;await Gt.poll(async()=>(r=await t.locator("div[class='permissions-connect']").isVisible().catch(()=>!1),r),{timeout:25e3}).toBe(!0).catch(()=>console.error("Notice dialog did not appear within the timeout period."));let a=t.getByTestId("page-container-footer-next");await a.waitFor({state:"visible",timeout:25e3}),await a.click(),await t.waitForEvent("close",{predicate:()=>!0,timeout:25e3}).catch(()=>console.error("Extension popup did not close within the timeout period when connecting to the DApp."))}async function xt(t,e){return await t.getByTestId(w.accountAddressesButton).hover(),await t.getByTestId("multichain-address-rows-list").getByRole("button",{name:/view all/i}).click(),await t.getByRole("searchbox",{name:/search networks/i}).fill(e),await t.locator(`div[data-testid='${w.accountAddressesElements}']:has-text('${e}')`).getByTestId(w.accountAddressQRCode).click(),await t.getByRole("dialog").locator("div > p[data-testid='account-address']").textContent()}import{expect as qt}from"@playwright/test";import{errors as zt}from"@playwright/test";async function E(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}var Bt={loadingOverlay:"loading-overlay",loadingSpinner:"spinner loading-overlay__spinner"};var Kt=6e4,jt=async(t,e,o)=>{await E(e);try{await e.locator(`div[class="${t}"]`).waitFor({state:"detached",timeout:o})}catch(n){if(n instanceof zt.TimeoutError)console.info(`Loading indicator '${t}' not found - continuing.`);else throw console.error(`Error while waiting for loading indicator '${t}' to disappear`),n}},X=async t=>{try{await jt(Bt.loadingSpinner,t,Kt)}catch(e){console.warn("Warning during MetaMask load:",e)}return await v(300),t};async function kt(t){if(await t.getByTestId(O.unlockButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already locked");return}await j(t);let n=t.getByTestId(_.lockButton);await qt(n).toBeVisible(),await n.click(),await X(t)}import{styleText as Pt}from"util";import{expect as D}from"@playwright/test";import bt from"fs";import Xt from"path";async function Q(t){let e=h(t),o=Xt.resolve(e,"password.txt");try{if(!bt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return bt.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} password from cache: ${n.message}`)}}import{expect as Qt}from"@playwright/test";async function At({context:t,path:e,locator:o}){let n;try{await Qt.poll(async()=>(n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!n),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).map(a=>a.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!n)throw new Error(`Popup page with path ${e} not found in context.`);return await Jt(n,o),await n.setViewportSize({width:360,height:592}),n}async function Jt(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 Ct from"fs";import Yt from"path";async function Tt(t){let e=h(t),o=Yt.resolve(e,"extension-id.txt");try{if(!Ct.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return Ct.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${n.message}`)}}var C=class{name="metamask";onboardingPath="/home.html#onboarding";async indexUrl(){return`chrome-extension://${await this.extensionId()}/home.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await Tt(this.name)}async promptPage(e){let o=await this.promptUrl();return await At({context:e,path:o,locator:"div[data-testid='multichain-page']"})}};import{expect as Z}from"@playwright/test";async function L({page:t}){await t.locator(`div:has(> button[data-testid='${w.openSettingsButton}'])`).click(),await t.getByTestId(_.networksButton).click();let n=t.locator("section[role='dialog']");await Z(n).toBeVisible(),await Z(n).toContainText(/manage networks/i);let r="div:has(> p:has-text('Show test networks'))";await n.locator(r).scrollIntoViewIfNeeded();let a=n.locator(r);if(!await a.locator("label[class='toggle-button toggle-button--off']").isVisible().catch(()=>!1)){await n.getByRole("button",{name:/close/i}).click(),console.info("Testnet networks are already visible.");return}await a.locator("label[class='toggle-button toggle-button--off']").click(),await t.getByTestId("Sepolia").scrollIntoViewIfNeeded(),await Z(t.getByTestId("Sepolia")).toBeVisible(),await n.getByRole("button",{name:/close/i}).click()}async function tt({page:t,mainAccountName:e,...o}){console.info(Pt("yellowBright",`
|
|
3
|
+
\u{1F98A} MetaMask onboarding started...`,{validateStream:!1}));let n=await Q("metamask"),r=new C,a=t.locator("img[class='loading-spinner']"),i=t.getByTestId(g.createWalletButton),c=t.getByTestId(g.importWalletButton),s=t.getByTestId(g.createNewPasswordInput),p=t.getByTestId(g.confirmNewPasswordInput),l=t.getByTestId(g.confirmPasswordCheckbox),u=t.getByTestId(g.createPasswordButton),f=t.getByTestId(g.metamaskMetricsIAgreeButton),B=t.getByTestId(g.onboardingDoneButton);if(await a.waitFor({state:"detached",timeout:3e4}),o.mode==="create"){let I=t.getByTestId(g.useSecretRecoveryPhraseButton);await i.click(),await I.click(),await s.fill(n),await p.fill(n),await l.click(),await u.click(),await t.getByTestId(g.revealSecretRecoveryPhraseButton).click(),await t.getByTestId(g.recoveryPhraseRemindMeLaterButton).click(),await f.click()}if(o.mode==="import"){let{secretRecoveryPhrase:I}=o,T=I.split(" "),z=t.getByTestId(g.importUsingSecretRecoveryPhraseButton);await c.click(),await z.click();let at=t.getByTestId(g.secretRecoveryPhraseTextAreaInput);await at.fill(T[0]),await at.press("Space");for(let K=1;K<T.length;K++){let rt=t.getByTestId(`import-srp__srp-word-${K}`);await rt.fill(T[K]),await rt.press("Space")}await t.getByTestId(g.importWalletConfirmButton).click(),await s.fill(n),await p.fill(n),await l.click(),await u.click(),await f.click();let Mt=t.getByTestId("wallet-ready");await D(Mt).toContainText(/your wallet is ready/i)}await B.click();let A=`chrome-extension://${await r.extensionId()}/sidepanel.html`,y=await t.context().browser()?.newBrowserCDPSession(),N;await D.poll(async()=>{if(y){let{targetInfos:I}=await y.send("Target.getTargets"),T=I.find(z=>z.url===A);return N=T,!!T}},{timeout:15e3}).toBe(!0),N&&await y?.send("Target.closeTarget",{targetId:N.targetId}),await t.goto(await r.indexUrl()),await a.waitFor({state:"detached",timeout:3e4}),await D(t.getByTestId(w.buyButton)).toBeVisible(),await D(t.getByTestId(w.swapButton)).toBeVisible(),await D(t.getByTestId(w.sendButton)).toBeVisible(),await D(t.getByTestId(w.receiveButton)).toBeVisible(),await L({page:t}),e&&await W({page:t,accountName:e}),await v(5e3),console.info(Pt("greenBright","\u2728 MetaMask onboarding completed successfully",{validateStream:!1}))}import{expect as Zt}from"@playwright/test";async function St(t){let e=t.getByTestId(q.cancelButton);await v(1e3),await Zt(e).toBeEnabled(),await e.click()}import{expect as b}from"@playwright/test";async function It({page:t,currentAccountName:e,newAccountName:o}){let n=t.getByTestId(w.accountMenuButton);if(await n.textContent()===o)throw Error(`The account to be renamed "${o}" already exists.`);await b(n).toBeVisible({timeout:15e3}),await n.click(),await b(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let a=t.getByTestId(P.addMultichainAccountButton),i=await a.textContent();i?.includes("Syncing")&&await b.poll(async()=>(await a.textContent())?.trim()??"",{timeout:6e4}).not.toBe(i);let c=await t.getByTestId(/^multichain-account-cell/).all(),s=null;for(let y of c)if((await y.textContent())?.includes(e)){s=y;break}if(!s)throw Error(`Account with name "${e}" not found.`);if((await s.textContent())?.split("$")[0]===o)throw Error(`The new account name "${o}" is the same as the old account name "${e}".`);let l=t.locator(`div[aria-label='${e} options']`);await b(l).toBeVisible(),await l.click(),await b(t.getByRole("tooltip")).toBeVisible();let u=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await b(u).toBeVisible(),await u.click();let f=t.getByRole("dialog"),B=f.getByRole("heading",{name:/rename/i});await b(B).toBeVisible();let S=f.getByRole("textbox");await b(S).toBeVisible(),await S.fill(o);let A=f.getByRole("button",{name:/confirm/i});await b(A).toBeEnabled(),await A.click();for(let y of c)if((await y.textContent())?.includes(o)){await b(y).toBeVisible(),await b(y).toContainText(o);break}await t.locator("button[aria-label='Back']").first().click()}import{expect as et}from"@playwright/test";async function vt({page:t,networkType:e,chainName:o}){let n=t.getByTestId(w.openNetworkSelectorButton);await n.click();let r=t.getByTestId("modal-header-close-button");if(e==="testnet"||e==="custom"){let s=t.getByRole("tab",{name:"Custom"});await s.click(),await t.locator("p:has-text('Testnets')").isVisible().catch(()=>!1)||(await r.click(),await L({page:t}),await n.click(),await s.click());let u=t.locator(`div div[data-testid='${o}']`),f=await u.textContent();et(f).toBe(o),await u.click();return}await t.getByRole("tab",{name:"Popular"}).click();let i=t.locator(`div div[data-testid='${o}']`),c=await i.textContent();et(c).toBe(o),await i.click(),await et(n).toContainText(o,{timeout:3e4})}import{expect as Rt}from"@playwright/test";async function H(t){let e=await Q("metamask"),o=t.getByTestId(O.passwordInput);if(await t.getByTestId(w.openNetworkSelectorButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await o.fill(e);let a=t.getByTestId(O.unlockButton);await Rt(a).toBeVisible(),await a.click(),await X(t),await Rt(t.getByTestId(w.buyButton)).toBeVisible({timeout:3e4})}var V=class extends C{page;constructor(e){super(),this.page=e}async onboard(e){await tt({page:this.page,...e})}async unlock(){await H(this.page)}async lock(){await kt(this.page)}async renameAccount({newAccountName:e,currentAccountName:o}){await It({page:this.page,newAccountName:e,currentAccountName:o})}async addAccount({privateKey:e,accountName:o}){await pt({page:this.page,privateKey:e,accountName:o})}async switchAccount({accountName:e}){await W({page:this.page,accountName:e})}async switchNetwork({...e}){await vt({page:this.page,...e})}async getAccountAddress(e){return await xt(this.page,e)}async toggleShowTestnetNetwork(){await L({page:this.page})}async addCustomNetwork({chainId:e,currencySymbol:o,networkName:n,rpcUrl:r}){await ft({page:this.page,chainId:e,currencySymbol:o,networkName:n,rpcUrl:r})}async connectToApp(e){await ht(await this.promptPage(this.page.context()),e)}async confirmTransaction(e){await yt(await this.promptPage(this.page.context()),e)}async rejectTransaction(){await St(await this.promptPage(this.page.context()))}};import Nt from"fs";import ne from"path";import{test as ae,chromium as re}from"@playwright/test";import{Instance as ie,Pool as se}from"prool";import{expect as te}from"@playwright/test";async function ot(t,e){let o=await t.newPage();return await te(async()=>{await o.goto(e),await E(o)}).toPass(),o}async function nt(t,e){let o=await e.newPage();for(let{origin:n,localStorage:r}of t){let a=o.mainFrame();await a.goto(n),await a.evaluate(i=>{i.forEach(({name:c,value:s})=>{window.localStorage.setItem(c,s)})},r)}await o.close()}import ee from"fs/promises";async function Et(t){await ee.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var oe=35e3;async function J(t,e){try{await Promise.race([t.close(),new Promise((o,n)=>setTimeout(()=>n(new Error("Context close timed out")),oe))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await Et(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var G,Ua=({slowMo:t=0,profileName:e}={})=>ae.extend({contextPath:async({browserName:o},n,r)=>{let a=await R(`${o}-${r.testId}`);await n(a)},context:async({context:o,contextPath:n},r)=>{let a=new C,i=h(a.name),c=await F(a.name),s=ne.resolve(i,e??"wallet-data");if(!Nt.existsSync(s))throw new Error("\u274C Cache for MetaMask wallet data not found. Create it first");Nt.cpSync(s,n,{recursive:!0,force:!0});let p=[`--disable-extensions-except=${c}`,`--load-extension=${c}`];process.env.HEADLESS&&(p.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await re.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:t}),{cookies:u,origins:f}=await o.storageState();u&&await l.addCookies(u),f&&f.length>0&&nt(f,l);let B=await a.indexUrl();await l.waitForEvent("page",{predicate:k=>k.url().startsWith(B),timeout:3e4}),G=l.pages().find(k=>k.url().startsWith(B))??await ot(l,B);for(let k of l.pages())k.url().includes("about:blank")&&await k.close();await G.locator("img[class='loading-spinner']").waitFor({state:"detached"}),await H(G),await r(l),await J(l,n)},metamaskPage:async({context:o},n)=>{await n(G)},metamask:async({context:o},n)=>{let r=new V(G);await n(r)},createAnvilNode:async({context:o},n,r)=>{let a=r.workerIndex,i;await n(async c=>{i=se.define({instance:ie.anvil(c)});let s=await i.start(a),p=`http://${s.host}:${s.port}`,u=c?.chainId??31337;return{rpcUrl:p,anvil:s,chainId:u}}),i&&await i.stop(a)},connectToAnvil:async({context:o,metamask:n,createAnvilNode:r},a)=>{await a(async()=>{let{chainId:i,rpcUrl:c}=await r({chainId:2251});await n.addCustomNetwork({chainId:i,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:c})})}});import{test as de}from"@playwright/test";import{Instance as me,Pool as ue}from"prool";import Ft from"fs";import ce from"path";import{chromium as le}from"@playwright/test";async function _t({workerInfo:t,profileName:e,slowMo:o}){let n=new C,r=await R(t.workerIndex.toString()),a=h(n.name),i=ce.resolve(a,e??"wallet-data");if(!Ft.existsSync(i))throw new Error(`Cache for ${n.name} does not exist. Create it first!`);Ft.cpSync(i,r,{recursive:!0,force:!0});let c=await F(n.name),s=await le.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:o}),p=await n.indexUrl();await s.waitForEvent("page",{predicate:u=>u.url().startsWith(p),timeout:4e4});let l=s.pages().find(u=>u.url().startsWith(p));l||(l=await s.newPage(),await l.goto(p),await E(l));for(let u of s.pages())u.url().includes("about:blank")&&await u.close();return{context:s,walletPage:l,contextPath:r}}var ar=({profileName:t,slowMo:e}={})=>de.extend({workerScopeContents:[async({browser:o},n,r)=>{let{context:a,contextPath:i,walletPage:c}=await _t({workerInfo:r,profileName:t,slowMo:e});await a.grantPermissions(["clipboard-read"]);let s=new V(c);await s.unlock(),await n({wallet:s,walletPage:c,context:a}),await J(a,i)},{scope:"worker"}],createAnvilNode:async({context:o},n,r)=>{let a=r.workerIndex,i;await n(async c=>{i=ue.define({instance:me.anvil(c)});let s=await i.start(a),p=`http://${s.host}:${s.port}`,u=c?.chainId??31337;return{rpcUrl:p,anvil:s,chainId:u}}),i&&await i.stop(a)},connectToAnvil:async({context:o,createAnvilNode:n,workerScopeContents:r},a)=>{await a(async()=>{let{wallet:i}=r,{chainId:c,rpcUrl:s}=await n({chainId:2251});await i.addCustomNetwork({chainId:c,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:s})})}});export{V as Metamask,Ua as metamaskFixture,ar as metamaskWorkerScopeFixture,Oe as workerScopeContext};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WalletProfileFixtureArgs
|
|
2
|
-
import { W as WorkerScopeFixture } from '../../worker-scope-context-
|
|
3
|
-
export { w as workerScopeContext } from '../../worker-scope-context-
|
|
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
|
|
|
@@ -146,6 +146,6 @@ declare class Meteor extends MeteorProfile {
|
|
|
146
146
|
|
|
147
147
|
declare const meteorFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & MeteorFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
148
148
|
|
|
149
|
-
declare const meteorWorkerScopeFixture: ({ slowMo, profileName
|
|
149
|
+
declare const meteorWorkerScopeFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & MeteorFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Meteor>>;
|
|
150
150
|
|
|
151
|
-
export { Meteor, WalletProfileFixtureArgs, WorkerScopeFixture,
|
|
151
|
+
export { Meteor, WalletProfileFixtureArgs, WorkerScopeFixture, meteorFixture, meteorWorkerScopeFixture };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import tt from"fs";import gt from"path";import{chromium as yt}from"@playwright/test";import ft from"path";var Y=".wallet-cache",Q=".wallet-context";var J="13.22.0",v="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Rt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ut=`${v}solflare-wallet-extension-v2.19.1.zip`,$t=`${v}petra-wallet-extension-v2.4.8.zip`,Vt=`${v}phantom-wallet-extension-v26.10.0.zip`,Kt=`${v}meteor-wallet-extension-v0.7.0.zip`,Ht=`${v}keplr-wallet-extension-v0.13.3.zip`;async function E(t){return ft.resolve(process.cwd(),Q,t)}import ht from"path";function f(t){return ht.resolve(process.cwd(),Y,t)}import Z from"fs";import xt from"path";async function N(t){try{let o=f(t),e=xt.resolve(o,"extension-path.txt");if(!Z.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let r=Z.readFileSync(e,"utf-8").trim();if(!r)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return r}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function ot({wallet:t,workerInfo:o,profileName:e,slowMo:r}){let
|
|
2
|
-
Already on ${o}, no need to switch network.`),"Exit";await r.click();let
|
|
3
|
-
Switching to the ${o} account aborted. Account is already selected.`);return}await t.locator(
|
|
4
|
-
`))}async function R({page:t,privateKey:o,network:e,accountName:r,additionalAccounts:
|
|
5
|
-
Meteor onboarding started...`,{validateStream:!1}));let c=await I("meteor"),
|
|
6
|
-
Retrying search for account. ${
|
|
7
|
-
`),{validateStream:!1}))}await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click();let j=t.locator("section[role='dialog']").locator("button:has-text('Close')").first();if(await j.isVisible().then(()=>!0).catch(()=>!1)&&await j.click(),await
|
|
1
|
+
import tt from"fs";import gt from"path";import{chromium as yt}from"@playwright/test";import ft from"path";var Y=".wallet-cache",Q=".wallet-context";var J="13.22.0",v="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Rt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ut=`${v}solflare-wallet-extension-v2.19.1.zip`,$t=`${v}petra-wallet-extension-v2.4.8.zip`,Vt=`${v}phantom-wallet-extension-v26.10.0.zip`,Kt=`${v}meteor-wallet-extension-v0.7.0.zip`,Ht=`${v}keplr-wallet-extension-v0.13.3.zip`;async function E(t){return ft.resolve(process.cwd(),Q,t)}import ht from"path";function f(t){return ht.resolve(process.cwd(),Y,t)}import Z from"fs";import xt from"path";async function N(t){try{let o=f(t),e=xt.resolve(o,"extension-path.txt");if(!Z.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let r=Z.readFileSync(e,"utf-8").trim();if(!r)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return r}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function ot({wallet:t,workerInfo:o,profileName:e,slowMo:r}){let i=await E(o.workerIndex.toString()),c=f(t.name),u=gt.resolve(c,e??"wallet-data");if(!tt.existsSync(u))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);tt.cpSync(u,i,{recursive:!0,force:!0});let p=await N(t.name),l=await yt.launchPersistentContext(i,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:r}),s=await t.indexUrl(),w=l.pages()[0];return w||(w=await l.newPage()),await w.goto(s),{context:l,walletPage:w,contextPath:i}}var m={openSidebarMenuButton:"button[aria-label='open sidebar']",addWalletButton:"button:has-text('Add Wallet')",settingsButton:"button:has-text('Settings')",settingsMenuBackButton:"button[aria-label='Back']"};var d={importExistingWalletButton:'button:has-text("Import an existing wallet")',switchNetworkButton:"button[id^='menu-button']",privateKeyButton:"button:has-text('Private Key')",findMyAccountButton:"button:has-text('Find my account')"};async function _(t,o,e){let r=t.locator(d.switchNetworkButton).last();await r.scrollIntoViewIfNeeded();let i=await r.textContent(),c=o.split("net")[0]?.toLowerCase()??"";if(i?.toLowerCase()===c)return console.info(`
|
|
2
|
+
Already on ${o}, no need to switch network.`),"Exit";await r.click();let p=t.locator(e).last().locator(`> button:has-text('${o}')`);await p.click(),await t.locator("div > h2:has-text('Meteor Community')").isVisible().catch(()=>!1)&&await p.click()}async function P({page:t,newAccountName:o}){await t.locator(m.openSidebarMenuButton).click(),await t.locator("div:has(> h2):has(> svg)").click();let i=t.locator("input[placeholder='Ex. My Meteor Wallet']"),c=t.locator("button[type='submit']:has-text('Update')");await i.fill(o),await c.click(),await t.locator("div[id='root'] button[aria-label='Close']").click()}async function F({page:t,privateKey:o,accountName:e,network:r}){await t.locator(m.openSidebarMenuButton).click(),await t.locator(m.addWalletButton).click(),await _(t,r,"section[role='dialog'] div[role='menu']"),await t.locator(d.importExistingWalletButton).click(),await t.locator(d.privateKeyButton).click();let l=t.locator('button:has-text("Continue")');await l.scrollIntoViewIfNeeded(),await l.click(),await t.locator("textarea:not([disabled])").fill(o),await t.locator(d.findMyAccountButton).click(),await t.locator("button[type='submit'][data-loading]").waitFor({state:"detached",timeout:25e3}),await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click(),await P({page:t,newAccountName:e})}var A={approveButton:"button:has-text('Approve')",connectButton:"button:has-text('Connect')",cancelButton:"button:has-text('Cancel')"};import et from"fs";import bt from"path";async function I(t){let o=f(t),e=bt.resolve(o,"password.txt");try{if(!et.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} password from cache: ${r.message}`)}}async function h(t){let o=await I("meteor"),e=t.locator("input[placeholder='Enter Password']"),r=t.locator('button:has-text("Unlock")');await e.fill(o),await r.click()}async function rt(t){await h(t),await t.locator(A.approveButton).click(),await t.locator("h2:has-text('Executing Transaction')").waitFor({state:"attached"}),await t.waitForEvent("close",{timeout:15e3})}async function k(t,o){if((await t.locator("div:has(button[type='button'][aria-label='open sidebar'])").nth(-2).locator("div:has(div > h2)").locator("div > h2").textContent())?.toLowerCase()===o.toLowerCase()){console.info(`
|
|
3
|
+
Switching to the ${o} account aborted. Account is already selected.`);return}await t.locator(m.openSidebarMenuButton).click();let l=await t.locator("div:has(div > button[type='button'][aria-label='Close'])").nth(-2).locator("div").nth(2).locator("> div").nth(1).locator("div").nth(1).locator("div > h2").all(),s=null;for(let w of l)if((await w.textContent())?.toLowerCase()===o.toLowerCase()){s=w;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function at(t,o){await h(t),o&&await k(t,o);let e=t.getByRole("heading",{name:"Connect Request",exact:!0});await Promise.all([t.locator(A.connectButton).click(),e.waitFor({state:"detached",timeout:3e4})])}async function nt(t){return await t.locator("div:has(button[type='button'][aria-label='open sidebar'])").nth(-2).locator("div:has(div > h2)").locator("div > svg").click(),await t.evaluate(async()=>await navigator.clipboard.readText())}async function it(t){await t.locator(m.openSidebarMenuButton).click(),await t.locator("button:has-text('Lock Wallet')").click()}import{styleText as O}from"util";function T(t){return new Promise(o=>setTimeout(o,t))}import{expect as Pt}from"@playwright/test";async function ct({context:t,path:o,locator:e}){let r;try{await Pt.poll(async()=>(r=t.pages().filter(i=>i.url().startsWith("chrome-extension://")).find(i=>i.url().match(o)),!!r),{timeout:3e4}).toBe(!0)}catch{let i=t.pages().filter(c=>c.url().startsWith("chrome-extension://")).map(c=>c.url());throw new Error(`Popup page with path "${o}" not found in context after 30s. Pages in context: ${JSON.stringify(i)}`)}if(!r)throw new Error(`Popup page with path ${o} not found in context.`);return await At(r,e),await r.setViewportSize({width:360,height:592}),r}async function At(t,o){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(o).first().waitFor({state:"attached",timeout:4e4})}import st from"fs";import kt from"path";async function lt(t){let o=f(t),e=kt.resolve(o,"extension-id.txt");try{if(!st.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return st.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${r.message}`)}}var x=class{name="meteor";onboardingPath="ext_index_popup.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/ext_index_popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/ext_index.html`}async extensionId(){return await lt(this.name)}async promptPage(o){let e=await this.promptUrl();return await ct({context:o,path:e,locator:"div[id='root']"})}};async function B(t){await t.locator(m.openSidebarMenuButton).click(),await t.locator(m.settingsButton).click()}async function L(t,o){if(await B(t),await _(t,o,"div[role='menu']")==="Exit"){let c=t.locator(m.settingsMenuBackButton);await c.scrollIntoViewIfNeeded(),await c.click();return}let r=t.locator("p:has-text('Available Balance')");if(await T(1e3),!await r.isVisible().catch(()=>!1))throw new Error([`There is no associated account for the ${o} network in your wallet.`,`Please add an account to the ${o} network in your wallet using the "addAccount" method.`,"NOTE: For the account to be persisted across tests, do this when onboarding the wallet."].join(`
|
|
4
|
+
`))}async function R({page:t,privateKey:o,network:e,accountName:r,additionalAccounts:i}){console.info(O("yellowBright",`
|
|
5
|
+
Meteor onboarding started...`,{validateStream:!1}));let c=await I("meteor"),p=await new x().indexUrl();await t.goto(p);let l=t.locator(d.switchNetworkButton),s=await l.textContent(),w=e.split("net")[0]?.toLowerCase()??"";s?.toLowerCase().includes(w)||(await l.click(),await t.locator("div[role='menu']").locator(`> button:has-text('${e}')`).click());let y=t.locator("input[placeholder='Enter Password']"),S=t.locator("input[placeholder='Confirm Password']"),K=t.locator("label.chakra-checkbox .chakra-checkbox__control"),g=t.locator('button:has-text("Continue")');await y.fill(c),await S.fill(c),await K.click(),await g.click(),await t.locator(d.importExistingWalletButton).click(),await t.locator(d.privateKeyButton).click(),await g.scrollIntoViewIfNeeded(),await g.click(),await t.locator("textarea:not([disabled])").fill(o);let H=t.locator(d.findMyAccountButton);await H.click();let z=t.locator("button[type='submit'][data-loading]");if(await z.waitFor({state:"detached",timeout:25e3}),await t.getByRole("status").locator("div[id='toast-1-title']:has-text('No Account Found')").isVisible().catch(()=>!1)){let b=5,M=!1;for(;b>0;){if(console.info(`
|
|
6
|
+
Retrying search for account. ${b} attempts left`),await T(15e3),await H.click(),await z.waitFor({state:"detached",timeout:2e4}),await t.locator("div:has-text('Import Your Account')").nth(-2).locator("button").isVisible().catch(()=>!1)){M=!0;break}b-=1}if(!M)throw Error(O("redBright",["No Account Found","Account associated with the private key not found. Please make sure you are trying to import an account on the correct network(Mainnet/Testnet)."].join(`
|
|
7
|
+
`),{validateStream:!1}))}await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click();let j=t.locator("section[role='dialog']").locator("button:has-text('Close')").first();if(await j.isVisible().then(()=>!0).catch(()=>!1)&&await j.click(),await P({page:t,newAccountName:r}),i&&i.length>0){for(let{privateKey:q,accountName:G,network:dt}of i)await F({page:t,privateKey:q,accountName:G,network:dt});await B(t);let b=t.locator(d.switchNetworkButton).last();await b.scrollIntoViewIfNeeded();let M=await b.textContent(),X=e.split("net")[0]?.toLowerCase()??"";M?.toLowerCase()!==X&&await L(t,e),await k(t,r)}await T(3e3),console.info(O("greenBright","\u2728 Meteor onboarding completed successfully"))}async function pt(t){await h(t),await t.locator(A.cancelButton).click()}var C=class extends x{page;constructor(o){super(),this.page=o}async onboard({network:o,privateKey:e,accountName:r,additionalAccounts:i}){await R({page:this.page,network:o,privateKey:e,accountName:r,additionalAccounts:i})}async unlock(){await h(this.page)}async lock(){await it(this.page)}async renameAccount({newAccountName:o}){await P({page:this.page,newAccountName:o})}async switchNetwork(o){await L(this.page,o)}async switchAccount(o){await k(this.page,o)}async getAccountAddress(){return await nt(this.page)}async addAccount({accountName:o,network:e,privateKey:r}){await F({page:this.page,accountName:o,network:e,privateKey:r})}async openSettings(){await B(this.page)}async connectToApp(o){await at(await this.promptPage(this.page.context()),o)}async confirmTransaction(){await rt(await this.promptPage(this.page.context()))}async rejectTransaction(){await pt(await this.promptPage(this.page.context()))}};import ut from"fs";import Mt from"path";import{test as vt,chromium as Et}from"@playwright/test";import{expect as Bt}from"@playwright/test";async function U(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function $(t,o){let e=await t.newPage();return await Bt(async()=>{await e.goto(o),await U(e)}).toPass(),e}async function V(t,o){let e=await o.newPage();for(let{origin:r,localStorage:i}of t){let c=e.mainFrame();await c.goto(r),await c.evaluate(u=>{u.forEach(({name:p,value:l})=>{window.localStorage.setItem(p,l)})},i)}await e.close()}import Ct from"fs/promises";async function mt(t){await Ct.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var St=35e3;async function D(t,o){try{await Promise.race([t.close(),new Promise((e,r)=>setTimeout(()=>r(new Error("Context close timed out")),St))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await mt(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}var W,Tr=({slowMo:t=0,profileName:o}={})=>vt.extend({contextPath:async({browserName:e},r,i)=>{let c=await E(`${e}-${i.testId}`);await r(c)},context:async({context:e,contextPath:r},i)=>{let c=new x,u=f(c.name),p=await N(c.name),l=Mt.resolve(u,o??"wallet-data");if(!ut.existsSync(l))throw new Error("\u274C Cache for Meteor wallet data not found. Create it first");ut.cpSync(l,r,{recursive:!0,force:!0}),process.env.HEADLESS&&t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!");let s=await Et.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`,`--load-extension=${p}`],slowMo:process.env.HEADLESS?0:t});await s.grantPermissions(["clipboard-read"]);let{cookies:w,origins:y}=await e.storageState();w&&await s.addCookies(w),y&&y.length>0&&V(y,s);let S=await c.indexUrl();W=s.pages().find(g=>g.url().startsWith(S))||await $(s,S);for(let g of s.pages())g.url().includes("about:blank")&&await g.close();await W.bringToFront(),await h(W),await i(s),await D(s,r)},meteorPage:async({context:e},r)=>{await r(W)},meteor:async({context:e},r)=>{let i=new C(W);await r(i)}});import{test as Tt}from"@playwright/test";var Or=({slowMo:t,profileName:o}={})=>Tt.extend({workerScopeContents:[async({browser:e},r,i)=>{let{context:c,contextPath:u,walletPage:p}=await ot({workerInfo:i,profileName:o,slowMo:t,wallet:new x});await c.grantPermissions(["clipboard-read"]);for(let s of c.pages())s.url().includes("about:blank")&&await s.close();let l=new C(p);await l.unlock(),await r({wallet:l,walletPage:p,context:c}),await D(c,u)},{scope:"worker"}]});export{C as Meteor,Tr as meteorFixture,Or as meteorWorkerScopeFixture,ot as workerScopeContext};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WalletProfileFixtureArgs
|
|
2
|
-
import { W as WorkerScopeFixture } from '../../worker-scope-context-
|
|
3
|
-
export { w as workerScopeContext } from '../../worker-scope-context-
|
|
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 { Page, BrowserContext } from '@playwright/test';
|
|
6
6
|
|
|
@@ -153,6 +153,6 @@ declare class Petra extends PetraProfile {
|
|
|
153
153
|
|
|
154
154
|
declare const petraFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PetraFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
155
155
|
|
|
156
|
-
declare const petraWorkerScopeFixture: ({ slowMo, profileName
|
|
156
|
+
declare const petraWorkerScopeFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PetraFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Petra>>;
|
|
157
157
|
|
|
158
|
-
export { Petra, WalletProfileFixtureArgs, WorkerScopeFixture,
|
|
158
|
+
export { Petra, WalletProfileFixtureArgs, WorkerScopeFixture, petraFixture, petraWorkerScopeFixture };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import J from"fs";import yt from"path";import{chromium as gt}from"@playwright/test";import ft from"path";var G=".wallet-cache",X=".wallet-context";var j="13.22.0",W="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Nt=`https://github.com/MetaMask/metamask-extension/releases/download/v${j}/metamask-chrome-${j}.zip`,Rt=`${W}solflare-wallet-extension-v2.19.1.zip`,Ut=`${W}petra-wallet-extension-v2.4.8.zip`,$t=`${W}phantom-wallet-extension-v26.10.0.zip`,Vt=`${W}meteor-wallet-extension-v0.7.0.zip`,Kt=`${W}keplr-wallet-extension-v0.13.3.zip`;async function I(t){return ft.resolve(process.cwd(),X,t)}import xt from"path";function
|
|
2
|
-
Switching to ${o} account aborted because the account is already selected.`);return}await e.click();let
|
|
3
|
-
Petra onboarding started...`,{validateStream:!1}));let e=new
|
|
1
|
+
import J from"fs";import yt from"path";import{chromium as gt}from"@playwright/test";import ft from"path";var G=".wallet-cache",X=".wallet-context";var j="13.22.0",W="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Nt=`https://github.com/MetaMask/metamask-extension/releases/download/v${j}/metamask-chrome-${j}.zip`,Rt=`${W}solflare-wallet-extension-v2.19.1.zip`,Ut=`${W}petra-wallet-extension-v2.4.8.zip`,$t=`${W}phantom-wallet-extension-v26.10.0.zip`,Vt=`${W}meteor-wallet-extension-v0.7.0.zip`,Kt=`${W}keplr-wallet-extension-v0.13.3.zip`;async function I(t){return ft.resolve(process.cwd(),X,t)}import xt from"path";function x(t){return xt.resolve(process.cwd(),G,t)}import Z from"fs";import ht from"path";async function O(t){try{let o=x(t),e=ht.resolve(o,"extension-path.txt");if(!Z.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let r=Z.readFileSync(e,"utf-8").trim();if(!r)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return r}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function Y({wallet:t,workerInfo:o,profileName:e,slowMo:r}){let a=await I(o.workerIndex.toString()),c=x(t.name),s=yt.resolve(c,e??"wallet-data");if(!J.existsSync(s))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);J.cpSync(s,a,{recursive:!0,force:!0});let p=await O(t.name),u=await gt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:r}),d=await t.indexUrl(),l=u.pages()[0];return l||(l=await u.newPage()),await l.goto(d),{context:u,walletPage:l,contextPath:a}}import Bt from"zod";var m={depositButton:"button:has-text('Deposit')",sendButton:"button:has-text('Send')",receiveButton:"button:has-text('Receive')",settingsMenu:"button[aria-label='Settings']",lockButton:"global-menu-lock",accountMenuButton:"button[data-part='trigger']",accountDialog:"div[role='dialog']",backButton:"button[id='back-button']"},$={networkSection:"a[href='/settings/network']",backButton:"button[id='back-button']"},V={passwordInput:"input[name='password']",unlockButton:"button:has-text('Unlock')"},h={accountOptionsMenuButton:"button[data-scope='popover']",editAccountButton:"button[aria-label='Edit account name']",renameAccountInput:"input[name='name']",saveButton:"button:has-text('Save')",cancelButton:"button:has-text('Cancel')",addAccountButton:"button:has-text('Add accounts')",addAccountWithPrivateKeyButton:"button:has-text('Import private key')",addAccountWithMnemonicButton:"button:has-text('Import mnemonic')"};var w={createWalletButton:"button:has-text('Create an account')",createSeedPhraseButton:"button:has-text('Create a seed phrase wallet')",createNewPasswordInput:"input[name='password']",confirmNewPasswordInput:"input[name='confirmPassword']",confirmPasswordCheckbox:"label>div[data-scope='checkbox']",continueButton:"button:has-text('Continue')",skipCopyRecoveryPhraseButton:"button:has-text('Skip')",getStartedButton:"button:has-text('Get started')",onboardingCompleteText:"h1:has-text('Your wallet is ready, you may close this window')",importWalletButton:"button:has-text('Import an account')",importUsingPrivateKeyButton:"button:has-text('Import private key')",importUsingMnemonicButton:"button:has-text('Import mnemonic')",importButton:"button:has-text('Import')",privateKeyInput:"input[name='privateKey']"};import{expect as _}from"@playwright/test";import Pt from"zod";async function A({page:t,newAccountName:o}){let e=Pt.string().min(1,"Account name cannot be an empty string").parse(o);await t.locator(m.settingsMenu).click(),await _(t.getByText("Settings").first()).toBeVisible();let a=t.locator(h.editAccountButton);await _(a).toBeVisible(),await a.click(),await _(t.getByText("Account name").first()).toBeVisible();let c=t.locator(h.renameAccountInput);if(await c.getAttribute("value")===e)throw Error(`The account to be renamed "${e}" already exists.`);await c.fill(e);let p=t.locator(h.saveButton);await _(p).toBeEnabled(),await p.click(),await _(t.getByText(e).first()).toBeVisible(),await t.locator(m.backButton).click(),await Promise.allSettled([t.locator(m.depositButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4})])}async function L({page:t,accountName:o,mode:e,...r}){let a=Bt.string().max(14,"For switching accounts reason, account name should not be longer than 14 characters. The reason for this is because the name will be truncated. Hence, it will be difficult to select the account.").parse(o);if(await t.locator(h.accountOptionsMenuButton).first().click(),await t.getByRole("dialog").locator(h.addAccountButton).click(),e==="privateKey"){let u="privateKey"in r?r.privateKey:"";if(await t.locator(h.addAccountWithPrivateKeyButton).click(),await t.locator(w.privateKeyInput).fill(u),await t.locator(w.importButton).click(),(await t.getByRole("status").locator("div[data-part='description']",{hasText:"Account already exists in wallet"}).textContent({timeout:3e3}).catch(()=>null))?.includes("Account already exists in wallet"))throw Error(`Account ${a} already exists in wallet`);await A({page:t,newAccountName:a})}if(e==="mnemonic"){let d=("mnemonicPhrase"in r?r.mnemonicPhrase:"").split(" ");await t.locator(h.addAccountWithMnemonicButton).click();for(let[y,v]of d.entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+y)}"]`).fill(v);if(await t.locator(w.continueButton).click(),(await t.getByRole("status").locator("div[data-part='description']",{hasText:"Account already exists in wallet"}).textContent({timeout:3e3}).catch(()=>null))?.includes("Account already exists in wallet"))throw Error(`Account ${a} already exists in wallet`);await A({page:t,newAccountName:a})}}function N(t){return new Promise(o=>setTimeout(o,t))}var S={approveButton:'button:has-text("Approve")',cancelButton:'button:has-text("Cancel")'};var bt=/^[A-Z0-9]+(?:_[A-Z0-9]+)+$/;function q(t){return bt.test(t)?t.toLowerCase().replace(/_/g," ").replace(/\b[a-z]/g,o=>o.toUpperCase()):t}async function At(t,o){for(;;){let r=o();if(r||t.isClosed())break;try{let a=t.locator("div:has(> h2:has-text('Simulation error'))");if(await a.isVisible().catch(()=>!1)){let s=await a.locator("p").textContent();throw new Error(`[Confirm Transaction Error]: ${q(s||"Unexpected error!")}`)}}catch(a){if(t.isClosed())break;throw a instanceof Error?a:new Error(`[Confirm Transaction Error]: ${a}`)}if(r||t.isClosed())break;await N(300)}}async function Q(t){let o=!1;At(t,()=>o).catch(async a=>{t.isClosed()||console.error(a.message)}),await t.locator(S.approveButton).click(),o=!0}import{expect as kt}from"@playwright/test";async function C(t,o){let e=t.locator(h.accountOptionsMenuButton).first();if((await e.textContent())?.split("Switch wallet")[1]?.split("0x")[0]?.toLowerCase().trim().includes(o.toLowerCase().trim())){console.info(`
|
|
2
|
+
Switching to ${o} account aborted because the account is already selected.`);return}await e.click();let c=await t.getByRole("dialog").locator("> div > div > button[type='button']").all(),s=null;for(let p of c)if((await p.textContent())?.toLowerCase()?.trim().includes(o.toLowerCase().trim())){s=p;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function tt(t,o){o&&await C(t,o);let e=t.locator(S.approveButton);await kt(e).toBeEnabled({timeout:2e4}),await e.click()}async function ot(t){return await t.getByRole("button",{name:"Copy Address",exact:!0}).click(),await t.evaluate(async()=>await navigator.clipboard.readText())}import{expect as et}from"@playwright/test";async function rt(t){let o=t.locator(m.settingsMenu);await et(o).toBeVisible(),await o.click(),await et(t.getByText("Settings").first()).toBeVisible()}async function at(t){await rt(t),await t.getByRole("button",{name:/lock wallet/i}).click(),await t.getByRole("heading",{name:/welcome/i}).waitFor({state:"visible",timeout:2e4})}import{styleText as lt}from"util";import{expect as B}from"@playwright/test";async function F(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import nt from"fs";import St from"path";async function R(t){let o=x(t),e=St.resolve(o,"password.txt");try{if(!nt.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return nt.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} password from cache: ${r.message}`)}}import{expect as Ct}from"@playwright/test";async function it({context:t,path:o,locator:e}){let r;try{await Ct.poll(async()=>(r=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(o)),!!r),{timeout:3e4}).toBe(!0)}catch{let a=t.pages().filter(c=>c.url().startsWith("chrome-extension://")).map(c=>c.url());throw new Error(`Popup page with path "${o}" not found in context after 30s. Pages in context: ${JSON.stringify(a)}`)}if(!r)throw new Error(`Popup page with path ${o} not found in context.`);return await Et(r,e),await r.setViewportSize({width:360,height:592}),r}async function Et(t,o){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(o).first().waitFor({state:"attached",timeout:4e4})}import ct from"fs";import Tt from"path";async function st(t){let o=x(t),e=Tt.resolve(o,"extension-id.txt");try{if(!ct.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ct.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${r.message}`)}}var g=class{name="petra";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/index.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/prompt.html`}async extensionId(){return await st(this.name)}async promptPage(o){let e=await this.promptUrl();return await it({context:o,path:e,locator:"div[id='prompt']"})}};var E=3e4;async function K({page:t,...o}){console.info(lt("yellowBright",`
|
|
3
|
+
Petra onboarding started...`,{validateStream:!1}));let e=new g,r=await R("petra"),a=t.locator(w.createWalletButton),c=t.locator(w.importWalletButton),s=t.locator(w.createNewPasswordInput),p=t.locator(w.confirmNewPasswordInput),u=t.locator(w.confirmPasswordCheckbox),d=t.locator(w.continueButton),l=t.locator(w.getStartedButton),b=t.locator(w.onboardingCompleteText);if(o.mode==="create"){let f=t.locator(w.createSeedPhraseButton);await a.click(),await f.click(),await s.fill(r),await p.fill(r),await u.click(),await d.click(),await t.locator(w.skipCopyRecoveryPhraseButton).click(),await l.click(),await B(b).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await B(t.locator(m.depositButton)).toBeVisible({timeout:E}),await B(t.locator(m.sendButton)).toBeVisible({timeout:E})}if(o.mode==="importPrivateKey"){let{privateKey:f}=o,P=t.locator(w.importUsingPrivateKeyButton),k=t.locator(w.privateKeyInput),y=t.locator(w.importButton);await c.click(),await P.click(),await k.fill(f),await y.click(),await s.fill(r),await p.fill(r),await u.click(),await d.click(),await l.click(),await B(b).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await F(t),await B(t.locator(m.depositButton)).toBeVisible({timeout:E}),await B(t.locator(m.sendButton)).toBeVisible({timeout:E})}if(o.mode==="importMnemonic"){let{secretRecoveryPhrase:f}=o,P=t.locator(w.importUsingMnemonicButton);await c.click(),await P.click();for(let[k,y]of f.split(" ").entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+k)}"]`).fill(y);await d.click(),await s.fill(r),await p.fill(r),await u.click(),await d.click(),await l.click(),await B(b).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await B(t.locator(m.depositButton)).toBeVisible({timeout:E}),await B(t.locator(m.sendButton)).toBeVisible({timeout:E})}if(await A({page:t,newAccountName:o.accountName}),o.additionalAccounts&&o.additionalAccounts.length>0){for(let{...f}of o.additionalAccounts)await L({page:t,...f});await C(t,o.accountName)}await N(1500),console.info(lt("greenBright","\u2728 Petra onboarding completed successfully",{validateStream:!1}))}import{expect as vt}from"@playwright/test";async function pt(t){let o=t.locator(S.cancelButton);await vt(o).toBeEnabled(),await o.click()}async function ut(t,o){await t.locator(m.settingsMenu).click(),await t.locator($.networkSection).click(),await t.locator(`div:has(> span:has-text("${o}"))`).first().click();let c=t.locator($.backButton);await c.click(),await c.click()}import{expect as mt}from"@playwright/test";async function M(t){let o=await R("petra"),e=t.locator(V.passwordInput);if(!await e.isVisible().then(()=>!0).catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await mt(e).toBeVisible({timeout:15e3}),await e.fill(o);let a=t.locator(V.unlockButton);await mt(a).toBeEnabled(),await a.click(),await Promise.allSettled([t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.receiveButton).waitFor({state:"visible",timeout:2e4})])}var T=class extends g{page;constructor(o){super(),this.page=o}async onboard(o){await K({page:this.page,...o})}async unlock(){await M(this.page)}async lock(){await at(this.page)}async renameAccount({newAccountName:o}){await A({page:this.page,newAccountName:o})}async switchNetwork(o){await ut(this.page,o)}async switchAccount(o){await C(this.page,o)}async getAccountAddress(){return await ot(this.page)}async addAccount({accountName:o,...e}){await L({page:this.page,accountName:o,...e})}async connectToApp(o){await tt(await this.promptPage(this.page.context()),o)}async confirmTransaction(){await Q(await this.promptPage(this.page.context()))}async rejectTransaction(){await pt(await this.promptPage(this.page.context()))}};import dt from"fs";import Ft from"path";import{test as Mt,chromium as Dt}from"@playwright/test";import{expect as Wt}from"@playwright/test";async function z(t,o){let e=await t.newPage();return await Wt(async()=>{await e.goto(o),await F(e)}).toPass(),e}async function H(t,o){let e=await o.newPage();for(let{origin:r,localStorage:a}of t){let c=e.mainFrame();await c.goto(r),await c.evaluate(s=>{s.forEach(({name:p,value:u})=>{window.localStorage.setItem(p,u)})},a)}await e.close()}import It from"fs/promises";async function wt(t){await It.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var _t=35e3;async function U(t,o){try{await Promise.race([t.close(),new Promise((e,r)=>setTimeout(()=>r(new Error("Context close timed out")),_t))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await wt(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}var D,Ir=({slowMo:t=0,profileName:o}={})=>Mt.extend({contextPath:async({browserName:e},r,a)=>{let c=await I(`${e}-${a.testId}`);await r(c)},context:async({context:e,contextPath:r},a)=>{let c=new g,s=x(c.name),p=await O(c.name),u=Ft.resolve(s,o??"wallet-data");if(!dt.existsSync(u))throw new Error("\u274C Cache for Petra wallet data not found. Create it first");dt.cpSync(u,r,{recursive:!0,force:!0});let d=[`--disable-extensions-except=${p}`,`--load-extension=${p}`];process.env.HEADLESS&&(d.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await Dt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:b,origins:f}=await e.storageState();b&&await l.addCookies(b),f&&f.length>0&&H(f,l);let P=await c.indexUrl();D=l.pages().find(y=>y.url().startsWith(P))||await z(l,P);for(let y of l.pages()){let v=y.url();(v.includes("about:blank")||v.includes(c.onboardingPath))&&await y.close()}await D.bringToFront(),await M(D),await a(l),await U(l,r)},petraPage:async({context:e},r)=>{await r(D)},petra:async({context:e},r)=>{let a=new T(D);await r(a)}});import{test as Ot}from"@playwright/test";var Rr=({slowMo:t,profileName:o}={})=>Ot.extend({workerScopeContents:[async({browser:e},r,a)=>{let c=new g,{context:s,contextPath:p,walletPage:u}=await Y({wallet:c,workerInfo:a,profileName:o,slowMo:t});await s.grantPermissions(["clipboard-read"]);for(let l of s.pages())l.url().includes(c.onboardingPath)&&await l.close();let d=new T(u);await d.unlock(),await r({wallet:d,walletPage:u,context:s}),await U(s,p)},{scope:"worker"}]});export{T as Petra,Ir as petraFixture,Rr as petraWorkerScopeFixture,Y as workerScopeContext};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WalletProfileFixtureArgs
|
|
2
|
-
import { W as WorkerScopeFixture } from '../../worker-scope-context-
|
|
3
|
-
export { w as workerScopeContext } from '../../worker-scope-context-
|
|
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
|
|
|
@@ -191,6 +191,6 @@ declare class Phantom extends PhantomProfile {
|
|
|
191
191
|
|
|
192
192
|
declare const phantomFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PhantomFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
193
193
|
|
|
194
|
-
declare const phantomWorkerScopeFixture: ({ slowMo, profileName
|
|
194
|
+
declare const phantomWorkerScopeFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PhantomFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Phantom>>;
|
|
195
195
|
|
|
196
|
-
export { Phantom, WalletProfileFixtureArgs, WorkerScopeFixture,
|
|
196
|
+
export { Phantom, WalletProfileFixtureArgs, WorkerScopeFixture, phantomFixture, phantomWorkerScopeFixture };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import ot from"fs";import Ct from"path";import{chromium as At}from"@playwright/test";import Bt from"path";var Z=".wallet-cache",tt=".wallet-context";var Y="13.22.0",N="https://github.com/amaify/chainwright/releases/download/v0.1.0/",zt=`https://github.com/MetaMask/metamask-extension/releases/download/v${Y}/metamask-chrome-${Y}.zip`,jt=`${N}solflare-wallet-extension-v2.19.1.zip`,Xt=`${N}petra-wallet-extension-v2.4.8.zip`,Jt=`${N}phantom-wallet-extension-v26.10.0.zip`,qt=`${N}meteor-wallet-extension-v0.7.0.zip`,Qt=`${N}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return Bt.resolve(process.cwd(),tt,t)}import Pt from"path";function x(t){return Pt.resolve(process.cwd(),Z,t)}import et from"fs";import kt from"path";async function S(t){try{let e=x(t),o=kt.resolve(e,"extension-path.txt");if(!et.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=et.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 xe({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let n=await T(e.workerIndex.toString()),r=x(t.name),l=Ct.resolve(r,o??"wallet-data");if(!ot.existsSync(l))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);ot.cpSync(l,n,{recursive:!0,force:!0});let i=await S(t.name),m=await At.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:a}),u=await t.indexUrl(),f=m.pages()[0];return f||(f=await m.newPage()),await f.goto(u),{context:m,walletPage:f,contextPath:n}}var h={openMenuButton:"settings-menu-open-button",settingsButton:"sidebar_menu-button-settings",addAccountButton:"sidebar_menu-button-add_account",unlockWalletButton:"unlock-form-submit-button",manageAccountsButton:"sidebar_menu-button-manage_accounts",homeHeaderAccountName:"home-header-account-name"},B={lockWalletButton:"lock-menu-item",closeMenuButton:"settings-menu-close-button",developerSettingsButton:"settings-item-developer-settings",activeNetworksButton:"settings-item-active-networks"},at={accountProfileContainer:"sortable-account-container"};var p={createNewWalletButton:"button:has-text('Create a new wallet')",IAlreadyHaveAWalletButton:"button:has-text('I already have a wallet')",importRecoveryPhraseButton:"button:has-text('Import Recovery Phrase')",importPrivateKeyButton:"button:has-text('Import Private Key')",createSeedPhraseWalletButton:"create-manual-seed-phrase",passwordInput:"onboarding-form-password-input",passwordConfirmInput:"onboarding-form-confirm-password-input",termsCheckBox:"onboarding-form-terms-of-service-checkbox",continueButton:"button:has-text('Continue')",importWalletButton:"button:has-text('Import Wallet')",getStartedButton:"button:has-text('Get Started')",recoveryPhraseSavedCheckbox:"onboarding-form-saved-secret-recovery-phrase-checkbox",recoveryPhraseInput:"secret-recovery-phrase-word-input"};async function D({page:t,privateKey:e,accountName:o,chain:a}){await t.getByTestId(h.openMenuButton).click(),await t.getByTestId(h.addAccountButton).click(),await t.locator(p.importPrivateKeyButton).click();let i=t.locator("span[id^='button--listbox-input--']"),m=await i.textContent(),u=t.locator("input[name='name']"),f=t.locator("textarea[name='privateKey']");m!==a&&(await i.click(),await t.locator("ul[id^='listbox--listbox-input--']").locator(`li[data-label='${a}']`).click()),await u.fill(o),await f.fill(e),await t.locator("button:has-text('Import')").click()}var v={confirmButton:"primary-button",cancelButton:"secondary-button"};async function nt(t){let e=t.getByTestId(v.confirmButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"});let a=t.getByRole("button",{name:"Confirm anyway",exact:!0});if(await a.isVisible().catch(()=>!1)){await a.click();return}await e.click()}import{expect as Tt}from"@playwright/test";import bt from"zod";async function E(t,e){let o=bt.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(h.openMenuButton).click();let n=null,r=await t.locator("div[data-testid='account-menu'] div[data-testid='tooltip_interactive-wrapper']").all();for(let l of r)if((await l.textContent())?.includes(o)){n=l;break}if(!n)throw new Error(`Account with name "${o}" not found in the account list.`);await n.click()}async function rt(t,e){e&&await E(t,e);let o=t.getByTestId(v.confirmButton);await Tt(o).toBeEnabled({timeout:15e3}),await o.click()}import It from"zod";var St=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function it({page:t,accountName:e,chain:o}){let a=It.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(h.openMenuButton).click(),await t.getByTestId(h.manageAccountsButton).click(),await t.getByTestId(`manage-accounts-sortable-${a}`).click();let i=t.getByRole("button",{name:/Account Address(?:es)?/i});await i.waitFor({state:"visible",timeout:2e4});let u=await i.locator("div[data-name='row.pair'] > div").last().textContent();if(u&&Number(u)===1){await i.locator("> div > div").last().click(),await t.getByTestId("header--back").click();let y=t.getByTestId(B.closeMenuButton);await y.waitFor({state:"visible",timeout:15e3}),await y.click()}else{await i.click();let d=new RegExp(`${St(o.network)}`,"i");await t.getByRole("button",{name:d}).locator("> div").last().locator("> div").last().locator("div > button").last().click(),await t.getByRole("button",{name:"Close",exact:!0}).last().click();let C=t.getByTestId("header--back");await C.waitFor({state:"visible",timeout:15e3}),await C.click();let b=t.getByTestId(B.closeMenuButton);await b.waitFor({state:"visible",timeout:15e3}),await b.click()}return await t.evaluate(async()=>await navigator.clipboard.readText())}async function ct(t){await t.getByTestId(h.openMenuButton).click(),await t.getByTestId(h.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as pt}from"util";import{expect as Nt}from"@playwright/test";function _(t){return new Promise(e=>setTimeout(e,t))}import st from"fs";import vt from"path";async function R(t){let e=x(t),o=vt.resolve(e,"password.txt");try{if(!st.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return st.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Et}from"@playwright/test";async function lt({context:t,path:e,locator:o}){let a;try{await Et.poll(async()=>(a=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).find(n=>n.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).map(r=>r.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(n)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await Wt(a,o),await a.setViewportSize({width:360,height:592}),a}async function Wt(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 ut from"fs";import Ft from"path";async function mt(t){let e=x(t),o=Ft.resolve(e,"extension-id.txt");try{if(!ut.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ut.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var k=class{name="phantom";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await mt(this.name)}async promptPage(e){let o=await this.promptUrl();return await lt({context:e,path:o,locator:"div[id='root']"})}};async function W(t,e){let a=!1;for(;!e();){let n=e();if(n||a||t.isClosed())break;try{let r=t.locator("div[id='modal']").locator("div > svg").first();await r.isVisible().catch(()=>!1)&&(await r.click(),a=!0)}catch(r){if(t.isClosed())break;console.error("[autoClosePhantomNotification]: ",r)}if(n||a||t.isClosed())break;await _(300)}}async function $({page:t,currentAccountName:e,newAccountName:o}){await t.getByTestId(h.openMenuButton).click(),await t.getByTestId(h.manageAccountsButton).click();let l=await t.getByTestId(at.accountProfileContainer).locator("div[data-testid^='manage-accounts-sortable'] div > p").all(),i=null;for(let g of l)if((await g.textContent())?.toLowerCase()===e.toLowerCase()){i=g;break}if(!i)throw new Error(`Account with name "${e}" not found`);await i.click(),await t.locator("button:has-text('Account Name')").click();let u=t.locator("input[name='name']");await u.clear(),await u.fill(o),await t.getByTestId("primary-button").click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function U(t){await t.getByTestId(h.openMenuButton).click(),await t.getByTestId(h.settingsButton).click()}async function H({page:t,...e}){await U(t);let o=t.locator(`button[id='${B.developerSettingsButton}']`);await o.scrollIntoViewIfNeeded(),await o.click();let a=t.getByTestId("toggleTestNetwork"),r=await a.locator("label[data-testid='toggleTestNetwork-switch'] > input[aria-label='Toggle']").isChecked().catch(()=>!1);if(!r&&e.mode==="on"&&await a.click(),r&&e.mode==="off"){await a.click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click();return}if(e.mode==="on"&&e.chain==="Solana"){let{network:m}=e;await t.locator(`button:has-text("${m}")`).click()}if(e.mode==="on"&&e.chain==="Ethereum"){let{network:m}=e;if(!await t.getByText("EVM",{exact:!0}).isVisible().catch(()=>!1))throw new Error(["EVM testnet options are not available. Please ensure Ethereum is enabled in optional chains.","To enable Ethereum, call the 'toggleOptionalChain' action before switching the network.","toggleOptionalChain({ page: page, toggleMode: 'on', supportedChains: ['Ethereum'] })","Tip: For persistence, enable Ethereum in your setup file after the onboarding step completes."].join(`
|
|
1
|
+
import ot from"fs";import Ct from"path";import{chromium as At}from"@playwright/test";import Bt from"path";var Z=".wallet-cache",tt=".wallet-context";var Y="13.22.0",N="https://github.com/amaify/chainwright/releases/download/v0.1.0/",zt=`https://github.com/MetaMask/metamask-extension/releases/download/v${Y}/metamask-chrome-${Y}.zip`,jt=`${N}solflare-wallet-extension-v2.19.1.zip`,Xt=`${N}petra-wallet-extension-v2.4.8.zip`,Jt=`${N}phantom-wallet-extension-v26.10.0.zip`,qt=`${N}meteor-wallet-extension-v0.7.0.zip`,Qt=`${N}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return Bt.resolve(process.cwd(),tt,t)}import Pt from"path";function x(t){return Pt.resolve(process.cwd(),Z,t)}import et from"fs";import kt from"path";async function I(t){try{let e=x(t),o=kt.resolve(e,"extension-path.txt");if(!et.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=et.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 xe({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let n=await T(e.workerIndex.toString()),r=x(t.name),u=Ct.resolve(r,o??"wallet-data");if(!ot.existsSync(u))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);ot.cpSync(u,n,{recursive:!0,force:!0});let i=await I(t.name),m=await At.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:a}),l=await t.indexUrl(),h=m.pages()[0];return h||(h=await m.newPage()),await h.goto(l),{context:m,walletPage:h,contextPath:n}}var g={openMenuButton:"settings-menu-open-button",settingsButton:"sidebar_menu-button-settings",addAccountButton:"sidebar_menu-button-add_account",unlockWalletButton:"unlock-form-submit-button",manageAccountsButton:"sidebar_menu-button-manage_accounts",homeHeaderAccountName:"home-header-account-name"},B={lockWalletButton:"lock-menu-item",closeMenuButton:"settings-menu-close-button",developerSettingsButton:"settings-item-developer-settings",activeNetworksButton:"settings-item-active-networks"},at={accountProfileContainer:"sortable-account-container"};var p={createNewWalletButton:"button:has-text('Create a new wallet')",IAlreadyHaveAWalletButton:"button:has-text('I already have a wallet')",importRecoveryPhraseButton:"button:has-text('Import Recovery Phrase')",importPrivateKeyButton:"button:has-text('Import Private Key')",createSeedPhraseWalletButton:"create-manual-seed-phrase",passwordInput:"onboarding-form-password-input",passwordConfirmInput:"onboarding-form-confirm-password-input",termsCheckBox:"onboarding-form-terms-of-service-checkbox",continueButton:"button:has-text('Continue')",importWalletButton:"button:has-text('Import Wallet')",getStartedButton:"button:has-text('Get Started')",recoveryPhraseSavedCheckbox:"onboarding-form-saved-secret-recovery-phrase-checkbox",recoveryPhraseInput:"secret-recovery-phrase-word-input"};async function D({page:t,privateKey:e,accountName:o,chain:a}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.addAccountButton).click(),await t.locator(p.importPrivateKeyButton).click();let i=t.locator("span[id^='button--listbox-input--']"),m=await i.textContent(),l=t.locator("input[name='name']"),h=t.locator("textarea[name='privateKey']");m!==a&&(await i.click(),await t.locator("ul[id^='listbox--listbox-input--']").locator(`li[data-label='${a}']`).click()),await l.fill(o),await h.fill(e),await t.locator("button:has-text('Import')").click()}var S={confirmButton:"primary-button",cancelButton:"secondary-button"};async function nt(t){let e=t.getByTestId(S.confirmButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"});let a=t.getByRole("button",{name:"Confirm anyway",exact:!0});if(await a.isVisible().catch(()=>!1)){await a.click();return}await e.click()}import{expect as Tt}from"@playwright/test";import bt from"zod";async function v(t,e){let o=bt.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(g.openMenuButton).click();let n=null,r=await t.locator("div[data-testid='account-menu'] div[data-testid='tooltip_interactive-wrapper']").all();for(let u of r)if((await u.textContent())?.includes(o)){n=u;break}if(!n)throw new Error(`Account with name "${o}" not found in the account list.`);await n.click()}async function rt(t,e){e&&await v(t,e);let o=t.getByTestId(S.confirmButton);await Tt(o).toBeEnabled({timeout:15e3}),await o.click()}import It from"zod";var St=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function it({page:t,accountName:e,chain:o}){let a=It.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click(),await t.getByTestId(`manage-accounts-sortable-${a}`).click();let i=t.getByRole("button",{name:/Account Address(?:es)?/i});await i.waitFor({state:"visible",timeout:2e4});let l=await i.locator("div[data-name='row.pair'] > div").last().textContent();if(l&&Number(l)===1){await i.locator("> div > div").last().click(),await t.getByTestId("header--back").click();let y=t.getByTestId(B.closeMenuButton);await y.waitFor({state:"visible",timeout:15e3}),await y.click()}else{await i.click();let w=new RegExp(`${St(o.network)}`,"i");await t.getByRole("button",{name:w}).locator("> div").last().locator("> div").last().locator("div > button").last().click(),await t.getByRole("button",{name:"Close",exact:!0}).last().click();let C=t.getByTestId("header--back");await C.waitFor({state:"visible",timeout:15e3}),await C.click();let b=t.getByTestId(B.closeMenuButton);await b.waitFor({state:"visible",timeout:15e3}),await b.click()}return await t.evaluate(async()=>await navigator.clipboard.readText())}async function ct(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as pt}from"util";import{expect as Nt}from"@playwright/test";function _(t){return new Promise(e=>setTimeout(e,t))}import st from"fs";import vt from"path";async function R(t){let e=x(t),o=vt.resolve(e,"password.txt");try{if(!st.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return st.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Et}from"@playwright/test";async function lt({context:t,path:e,locator:o}){let a;try{await Et.poll(async()=>(a=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).find(n=>n.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).map(r=>r.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(n)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await Wt(a,o),await a.setViewportSize({width:360,height:592}),a}async function Wt(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 ut from"fs";import Ft from"path";async function mt(t){let e=x(t),o=Ft.resolve(e,"extension-id.txt");try{if(!ut.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ut.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var k=class{name="phantom";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await mt(this.name)}async promptPage(e){let o=await this.promptUrl();return await lt({context:e,path:o,locator:"div[id='root']"})}};async function E(t,e){let a=!1;for(;!e();){let n=e();if(n||a||t.isClosed())break;try{let r=t.locator("div[id='modal']").locator("div > svg").first();await r.isVisible().catch(()=>!1)&&(await r.click(),a=!0)}catch(r){if(t.isClosed())break;console.error("[autoClosePhantomNotification]: ",r)}if(n||a||t.isClosed())break;await _(300)}}async function $({page:t,currentAccountName:e,newAccountName:o}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click();let u=await t.getByTestId(at.accountProfileContainer).locator("div[data-testid^='manage-accounts-sortable'] div > p").all(),i=null;for(let f of u)if((await f.textContent())?.toLowerCase()===e.toLowerCase()){i=f;break}if(!i)throw new Error(`Account with name "${e}" not found`);await i.click(),await t.locator("button:has-text('Account Name')").click();let l=t.locator("input[name='name']");await l.clear(),await l.fill(o),await t.getByTestId("primary-button").click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function U(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click()}async function H({page:t,...e}){await U(t);let o=t.locator(`button[id='${B.developerSettingsButton}']`);await o.scrollIntoViewIfNeeded(),await o.click();let a=t.getByTestId("toggleTestNetwork"),r=await a.locator("label[data-testid='toggleTestNetwork-switch'] > input[aria-label='Toggle']").isChecked().catch(()=>!1);if(!r&&e.mode==="on"&&await a.click(),r&&e.mode==="off"){await a.click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click();return}if(e.mode==="on"&&e.chain==="Solana"){let{network:m}=e;await t.locator(`button:has-text("${m}")`).click()}if(e.mode==="on"&&e.chain==="Ethereum"){let{network:m}=e;if(!await t.getByText("EVM",{exact:!0}).isVisible().catch(()=>!1))throw new Error(["EVM testnet options are not available. Please ensure Ethereum is enabled in optional chains.","To enable Ethereum, call the 'toggleOptionalChain' action before switching the network.","toggleOptionalChain({ page: page, toggleMode: 'on', supportedChains: ['Ethereum'] })","Tip: For persistence, enable Ethereum in your setup file after the onboarding step completes."].join(`
|
|
2
2
|
`));await t.locator(`button:has-text("${m}")`).click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function z({page:t,additionalAccounts:e,...o}){console.info(pt("yellowBright",`
|
|
3
|
-
Phantom onboarding started...`,{validateStream:!1}));let a=await R("phantom");if(o.mode==="create"){await t.locator(p.createNewWalletButton).click(),await t.getByTestId(p.createSeedPhraseWalletButton).click();let
|
|
3
|
+
Phantom onboarding started...`,{validateStream:!1}));let a=await R("phantom");if(o.mode==="create"){await t.locator(p.createNewWalletButton).click(),await t.getByTestId(p.createSeedPhraseWalletButton).click();let f=t.getByTestId(p.passwordInput),d=t.getByTestId(p.passwordConfirmInput),A=t.getByTestId(p.termsCheckBox),P=t.locator(p.continueButton);await f.fill(a),await d.fill(a),await A.click(),await P.click(),await P.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByTestId(p.recoveryPhraseSavedCheckbox).click(),await P.click(),await _(1e3),await P.click(),await t.locator(p.getStartedButton).last().click()}if(o.mode==="recovery phrase"){let y=o.secretRecoveryPhrase.split(" ");await t.locator(p.IAlreadyHaveAWalletButton).click(),await t.locator(p.importRecoveryPhraseButton).click();for(let[G,O]of Object.entries(y))await t.getByTestId(`${p.recoveryPhraseInput}-${G}`).fill(O);await t.locator(p.importWalletButton).click(),await t.locator("p:has-text('Finding accounts with activity')").waitFor({state:"detached",timeout:6e4});let C=t.locator(p.continueButton);await C.click();let b=t.getByTestId(p.passwordInput),L=t.getByTestId(p.passwordConfirmInput),J=t.getByTestId(p.termsCheckBox);await b.fill(a),await L.fill(a),await J.click(),await C.click(),await C.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(p.getStartedButton).last().click()}if(o.mode==="private key"){await t.locator(p.IAlreadyHaveAWalletButton).click();let{privateKey:y,chain:f,accountName:d}=o;await t.locator(p.importPrivateKeyButton).click();let P=t.locator("span[id='button--listbox-input--1']"),C=await P.textContent(),b=t.locator("input[name='name']"),L=t.locator("textarea[name='privateKey']");C!==f&&(await P.click(),await t.locator("ul[id='listbox--listbox-input--1']").locator(`li[data-label='${f}']`).click()),await b.fill(d),await L.fill(y),await t.locator("button:has-text('Import')").click();let q=t.getByTestId(p.passwordInput),Q=t.getByTestId(p.passwordConfirmInput),G=t.getByTestId(p.termsCheckBox);await q.fill(a),await Q.fill(a),await G.click();let O=t.locator(p.continueButton);await O.click(),await O.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(p.getStartedButton).last().click()}let n=await t.context().newPage(),r=await new k().indexUrl();await n.goto(r);let i=await t.context().browser()?.newBrowserCDPSession(),m;await Nt.poll(async()=>{if(i){let{targetInfos:w}=await i.send("Target.getTargets"),f=w.filter(d=>d.title==="Phantom Wallet").find(d=>!d.attached&&d.url===r);return m=f,!!f}},{timeout:2e4}).toBe(!0),m&&await i?.send("Target.closeTarget",{targetId:m.targetId});let l=await n.getByTestId("home-header-account-name").textContent();if(o.mode==="create"){let{accountName:w}=o;await $({page:n,newAccountName:w,currentAccountName:"Account 1"})}if(e&&e.length>0){let w=!1;E(n,()=>w).catch(d=>console.error({error:d}));for(let{accountName:d,chain:A,privateKey:P}of e)await D({page:n,privateKey:P,accountName:d,chain:A}),w=!0;let f=o.mode==="create"?o.accountName:l;f&&await v(n,f)}o.toggleNetworkMode&&await H({page:n,...o.toggleNetworkMode}),await _(3e3),console.info(pt("greenBright","\u2728 Phantom onboarding completed successfully",{validateStream:!1}))}async function dt(t){let e=t.getByTestId(S.cancelButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"}),await e.click()}async function wt({page:t,supportedChains:e,toggleMode:o="off"}){if(await U(t),await t.locator("button[id='settings-item-active-networks']").click(),e.length===0)throw Error("Supported chains array cannot be empty for toggle mode other than 'onboard'");for(let u of e){let i=t.locator(`button[id='toggle-${u.toLowerCase()}']`),l=await i.locator(`label[data-testid='toggle-${u.toLowerCase()}-switch'] > input[aria-label='Toggle']`).isChecked().catch(()=>!1);o==="off"&&l&&await i.click(),o==="on"&&!l&&await i.click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function V(t){let e=await R("phantom"),o=t.locator("input[name='password']"),a=t.getByTestId("unlock-form-submit-button");await o.fill(e),await a.click(),await a.waitFor({state:"detached"})}var W=class extends k{page;constructor(e){super(),this.page=e}async onboard({...e}){await z({page:this.page,...e})}async unlock(){await V(this.page)}async lock(){await ct(this.page)}async renameAccount({...e}){await $({page:this.page,...e})}async switchAccount(e){await v(this.page,e)}async getAccountAddress({accountName:e,chain:o}){return await it({page:this.page,accountName:e,chain:o})}async addAccount({...e}){await D({page:this.page,...e})}async toggleOptionalChains({toggleMode:e,supportedChains:o}){await wt({page:this.page,supportedChains:o,toggleMode:e})}async switchNetwork({...e}){await H({page:this.page,...e})}async connectToApp(e){await rt(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await nt(await this.promptPage(this.page.context()))}async rejectTransaction(){await dt(await this.promptPage(this.page.context()))}};import gt from"fs";import Ot from"path";import{test as Dt,chromium as Rt}from"@playwright/test";import{expect as _t}from"@playwright/test";async function j(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function M(t,e){let o=await t.newPage();return await _t(async()=>{await o.goto(e),await j(o)}).toPass(),o}async function X(t,e){let o=await e.newPage();for(let{origin:a,localStorage:n}of t){let r=o.mainFrame();await r.goto(a),await r.evaluate(u=>{u.forEach(({name:i,value:m})=>{window.localStorage.setItem(i,m)})},n)}await o.close()}import Mt from"fs/promises";async function ft(t){await Mt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Lt=35e3;async function K(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Lt))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await ft(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var F,Ua=({slowMo:t=0,profileName:e}={})=>Dt.extend({contextPath:async({browserName:o},a,n)=>{let r=await T(`${o}-${n.testId}`);await a(r)},context:async({context:o,contextPath:a},n)=>{let r=new k,u=x(r.name),i=await I(r.name),m=Ot.resolve(u,e??"wallet-data");if(!gt.existsSync(m))throw new Error("\u274C Cache for Phantom wallet data not found. Create it first");gt.cpSync(m,a,{recursive:!0,force:!0}),process.env.HEADLESS&&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:[`--disable-extensions-except=${i}`,`--load-extension=${i}`],slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:h,origins:w}=await o.storageState();h&&await l.addCookies(h),w&&w.length>0&&await X(w,l);let y=await r.indexUrl();F=l.pages().find(d=>d.url().startsWith(y))||await M(l,y);for(let d of l.pages())d.url().includes("about:blank")&&await d.close();await F.bringToFront(),await V(F),await n(l),await K(l,a)},phantomPage:async({context:o},a)=>{await a(F)},phantom:async({context:o},a)=>{let n=new W(F);await a(n)},autoCloseNotification:[async({context:o},a)=>{let n=!1,u=E(F,()=>n);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});import{test as Ht}from"@playwright/test";import ht from"fs";import $t from"path";import{chromium as Ut}from"@playwright/test";async function yt({workerInfo:t,profileName:e,slowMo:o}){let a=new k,n=await T(t.workerIndex.toString()),r=x(a.name),u=$t.resolve(r,e??"wallet-data");if(!ht.existsSync(u))throw new Error(`Cache for ${a.name} does not exist. Create it first!`);ht.cpSync(u,n,{recursive:!0,force:!0});let i=await I(a.name),m=await Ut.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:o}),l=await a.indexUrl(),h=await M(m,l);return{context:m,walletPage:h,contextPath:n}}var rn=({slowMo:t,profileName:e}={})=>Ht.extend({workerScopeContents:[async({browser:o},a,n)=>{let{context:r,contextPath:u,walletPage:i}=await yt({workerInfo:n,profileName:e,slowMo:t});await r.grantPermissions(["clipboard-read"]);for(let l of r.pages())l.url().includes("about:blank")&&await l.close();let m=new W(i);await m.unlock(),await a({wallet:m,walletPage:i,context:r}),await K(r,u)},{scope:"worker"}],autoCloseNotification:[async({workerScopeContents:o},a)=>{let n=!1,r=()=>n,u=E(o.walletPage,r);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});export{W as Phantom,Ua as phantomFixture,rn as phantomWorkerScopeFixture,xe as workerScopeContext};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WalletProfileFixtureArgs
|
|
2
|
-
import { W as WorkerScopeFixture } from '../../worker-scope-context-
|
|
3
|
-
export { w as workerScopeContext } from '../../worker-scope-context-
|
|
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';
|
|
@@ -138,6 +138,6 @@ declare class Solflare extends SolflareProfile {
|
|
|
138
138
|
|
|
139
139
|
declare const solflareFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & SolflareFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
140
140
|
|
|
141
|
-
declare const solflareWorkerScopeFixture: ({ slowMo, profileName
|
|
141
|
+
declare const solflareWorkerScopeFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & SolflareFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Solflare>>;
|
|
142
142
|
|
|
143
|
-
export { Solflare, WalletProfileFixtureArgs, WorkerScopeFixture,
|
|
143
|
+
export { Solflare, WalletProfileFixtureArgs, WorkerScopeFixture, solflareFixture, solflareWorkerScopeFixture };
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import q from"fs";import dt from"path";import{chromium as gt}from"@playwright/test";import ut from"path";var K=".wallet-cache",j=".wallet-context";var H="13.22.0",k="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Wt=`https://github.com/MetaMask/metamask-extension/releases/download/v${H}/metamask-chrome-${H}.zip`,Nt=`${k}solflare-wallet-extension-v2.19.1.zip`,Ft=`${k}petra-wallet-extension-v2.4.8.zip`,
|
|
2
|
-
|
|
1
|
+
import q from"fs";import dt from"path";import{chromium as gt}from"@playwright/test";import ut from"path";var K=".wallet-cache",j=".wallet-context";var H="13.22.0",k="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Wt=`https://github.com/MetaMask/metamask-extension/releases/download/v${H}/metamask-chrome-${H}.zip`,Nt=`${k}solflare-wallet-extension-v2.19.1.zip`,Ft=`${k}petra-wallet-extension-v2.4.8.zip`,Mt=`${k}phantom-wallet-extension-v26.10.0.zip`,_t=`${k}meteor-wallet-extension-v0.7.0.zip`,Dt=`${k}keplr-wallet-extension-v0.13.3.zip`;async function h(t){return ut.resolve(process.cwd(),j,t)}import wt from"path";function u(t){return wt.resolve(process.cwd(),K,t)}import z from"fs";import ft from"path";async function S(t){try{let e=u(t),r=ft.resolve(e,"extension-path.txt");if(!z.existsSync(r))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let o=z.readFileSync(r,"utf-8").trim();if(!o)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return o}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function ae({wallet:t,workerInfo:e,profileName:r,slowMo:o}){let a=await h(e.workerIndex.toString()),n=u(t.name),p=dt.resolve(n,r??"wallet-data");if(!q.existsSync(p))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);q.cpSync(p,a,{recursive:!0,force:!0});let l=await S(t.name),c=await gt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:o}),f=await t.indexUrl(),m=c.pages()[0];return m||(m=await c.newPage()),await m.goto(f),{context:c,walletPage:m,contextPath:a}}var P={networkSettings:"li-settings-network",selectNetwork:"select-network",confirmModal:"modal-confirm",confifmButton:"btn-confirm",securityAndPrivacyButton:"nav-item-security-and-privacy",lockButton:"btn-lock"},A={portfolioButton:"nav-item-portfolio",walletSelectorButton:"icon-section-wallet-picker-arrow-right"};import b from"zod";var G=b.object({walletName:b.string().min(1,"Wallet name cannot be an empty string"),privateKey:b.array(b.number()).length(64)});async function v({page:t,privateKey:e,walletName:r}){let o=G.parse({privateKey:e,walletName:r});await t.getByTestId(A.walletSelectorButton).click(),await t.getByTestId("icon-btn-add").click(),await t.getByTestId("li-add-wallet-privateKey-add").click();let l=t.getByTestId("input-name"),c=t.getByTestId("input-private-key");await l.fill(o.walletName),await c.fill(`${o.privateKey}`),await t.getByTestId("btn-import").click(),await t.locator("span:has-text('My wallets')").waitFor({state:"attached"}),await t.getByRole("dialog").getByTestId("icon-btn-close").click()}import{expect as yt}from"@playwright/test";var E={approveButton:"btn-approve",rejectButton:"btn-reject"};async function X(t){let e=t.getByTestId(E.approveButton);if(await t.getByTestId("section-network-fee").waitFor({state:"attached",timeout:15e3}).catch(()=>!1),await t.getByTestId("info-box-network-mismatch").isVisible().catch(()=>!1)){console.error(`
|
|
2
|
+
|
|
3
|
+
A 'Network mismatch' error was detected in the transaction confirmation popup. Closing the popup and aborting the transaction confirmation process.`),await t.getByRole("button",{name:"Close",exact:!0}).click();return}let n=t.locator("div[data-id='control-label']");await n.isVisible().catch(()=>!1)&&await n.click(),await yt(e).toBeEnabled(),await e.click()}async function W(t,e){await t.getByTestId("icon-section-wallet-picker-arrow-right").click();let o=t.getByTestId("list-item-m-title").filter({hasText:e}).locator("xpath=../..");if(!await o.isVisible().catch(()=>!1))throw new Error(`Account "${e}" not found. Make sure the account is onboarded or verify the account name.`);await o.click()}async function J(t,e){e&&await W(t,e),await t.getByRole("button",{name:"Connect",exact:!0}).click()}async function Q(t){return await t.getByTestId("icon-section-wallet-picker-copy").click(),await t.evaluate(async()=>await navigator.clipboard.readText())}async function N(t){let e=t.getByRole("button",{name:"settings",exact:!0});await e.waitFor({state:"attached",timeout:3e4}),await e.click()}async function Y(t){await N(t),await t.getByTestId(P.securityAndPrivacyButton).click(),await t.getByTestId("li-settings-lock").getByTestId(P.lockButton).click()}import{styleText as tt}from"util";import Z from"fs";import xt from"path";async function F(t){let e=u(t),r=xt.resolve(e,"password.txt");try{if(!Z.existsSync(r))throw new Error("\u274C password.txt not found. Run setup script first.");return Z.readFileSync(r,"utf-8")}catch(o){throw new Error(`\u274C Failed to get ${t} password from cache: ${o.message}`)}}var y={alreadyHaveAWalletButton:"btn-already-have-wallet",recoveryPhraseInput:"input-recovery-phrase",continueButton:"btn-continue",passwordInput:"input-new-password",repeatPasswordInput:"input-repeat-password",quickSetupButton:"btn-quick-setup",IAgreeButton:"btn-explore"};async function M({page:t,currentAccountName:e,newAccountName:r}){await t.getByTestId(A.walletSelectorButton).click();let a=t.locator(`button[data-testid^='li-wallets']:has-text('${e}')`);if(!await a.isVisible().catch(()=>!1))throw new Error(`Account "${e}" not found. Make sure the account is available.`);await a.hover({timeout:2e4}),await a.getByTestId("icon-btn-three-dots").click({timeout:2e4});let l=t.getByTestId("li-manage-wallet-rename-wallet");await l.click();let c=t.getByTestId("input-name");await c.clear(),await c.fill(r),await t.getByTestId("btn-save").click(),await l.waitFor({state:"attached",timeout:15e3}),await t.getByTestId("icon-btn-close").click()}async function _(t,e){await N(t);let o=t.getByTestId("li-settings-network").getByRole("combobox");await o.locator(" > p").textContent()!==e?(await o.click(),await t.getByTestId(P.selectNetwork).getByRole("option",{name:e,exact:!0}).click(),(e==="Devnet"||e==="Testnet")&&await t.getByTestId(P.confirmModal).getByTestId(P.confifmButton).click()):console.info(`Network is already set to ${e}`),await t.getByTestId(A.portfolioButton).click()}async function et({page:t,recoveryPhrase:e,network:r,walletName:o,additionalAccounts:a}){console.info(tt("yellowBright",`
|
|
4
|
+
Solflare onboarding started...`,{validateStream:!1}));let n=await F("solflare");await t.getByTestId(y.alreadyHaveAWalletButton).click();let l=e.split(" ");for(let[I,g]of Object.entries(l))await t.getByTestId(`${y.recoveryPhraseInput}-${Number(I)+1}`).fill(g);let c=t.getByTestId(y.continueButton);await c.click();let f=t.getByTestId(y.passwordInput),m=t.getByTestId(y.repeatPasswordInput);if(await f.fill(n),await m.fill(n),await c.click(),await t.locator("div",{hasText:"Detecting your existing accounts. This process can take up to a minute."}).waitFor({state:"detached"}),await t.getByTestId(y.quickSetupButton).click(),await t.getByTestId(y.IAgreeButton).click(),await M({page:t,currentAccountName:"Main Wallet",newAccountName:o}),r&&await _(t,r),a&&a.length>0)for(let{privateKey:I,walletName:g}of a)await v({page:t,privateKey:I,walletName:g});console.info(tt("greenBright","\u2728 Solflare onboarding completed successfully",{validateStream:!1}))}async function ot(t){await t.getByTestId(E.rejectButton).click()}async function D(t){let e=await F("solflare");await t.getByTestId("input-password").fill(e),await t.getByTestId("btn-unlock").click(),await t.getByTestId("nav-main").waitFor({state:"attached",timeout:3e4})}import{expect as ht}from"@playwright/test";async function rt({context:t,path:e,locator:r}){let o;try{await ht.poll(async()=>(o=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(e)),!!o),{timeout:3e4}).toBe(!0)}catch{let a=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).map(n=>n.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(a)}`)}if(!o)throw new Error(`Popup page with path ${e} not found in context.`);return await Pt(o,r),await o.setViewportSize({width:360,height:592}),o}async function Pt(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 St from"path";async function nt(t){let e=u(t),r=St.resolve(e,"extension-id.txt");try{if(!at.existsSync(r))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return at.readFileSync(r,"utf-8")}catch(o){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${o.message}`)}}var x=class{name="solflare";onboardingPath="wallet.html#/onboard";async indexUrl(){return`chrome-extension://${await this.extensionId()}/wallet.html#/portfolio`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/confirm_popup.html`}async extensionId(){return await nt(this.name)}async promptPage(e){let r=await this.promptUrl();return await rt({context:e,path:r,locator:"div[data-testid='page-dapp-connect'], div[data-testid='page-tx-sign']"})}};var B=class extends x{page;constructor(e){super(),this.page=e}async onboard({recoveryPhrase:e,network:r,additionalAccounts:o,walletName:a}){await et({page:this.page,recoveryPhrase:e,network:r,additionalAccounts:o,walletName:a})}async unlock(){await D(this.page)}async lock(){await Y(this.page)}async renameAccount({currentAccountName:e,newAccountName:r}){await M({page:this.page,currentAccountName:e,newAccountName:r})}async switchNetwork(e){await _(this.page,e)}async switchAccount(e){await W(this.page,e)}async getAccountAddress(){return await Q(this.page)}async addAccount({privateKey:e,walletName:r}){await v({page:this.page,privateKey:e,walletName:r})}async connectToApp(e){await J(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await X(await this.promptPage(this.page.context()))}async rejectTransaction(){await ot(await this.promptPage(this.page.context()))}};import ct from"fs";import Ct from"path";import{test as Tt,chromium as It}from"@playwright/test";import{expect as At}from"@playwright/test";async function C(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function U(t,e){let r=await t.newPage();return await At(async()=>{await r.goto(e),await C(r)}).toPass(),r}async function V(t,e){let r=await e.newPage();for(let{origin:o,localStorage:a}of t){let n=r.mainFrame();await n.goto(o),await n.evaluate(p=>{p.forEach(({name:l,value:c})=>{window.localStorage.setItem(l,c)})},a)}await r.close()}import Bt from"fs/promises";async function it(t){await Bt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var kt=35e3;async function O(t,e){try{await Promise.race([t.close(),new Promise((r,o)=>setTimeout(()=>o(new Error("Context close timed out")),kt))])}catch(r){console.warn(`Browser context close did not complete cleanly: ${r.message}`)}try{await it(e)}catch(r){console.error(`Failed to remove temporary context directory at ${e}. Error:`,r)}}function st(t){return new Promise(e=>setTimeout(e,t))}async function L(t,e){let o=!1;for(;!e();){let a=e();if(a||o||t.isClosed())break;try{let n=t.locator("div[role='dialog']").locator("button[data-testid='icon-btn-whats-new-modal-close']");await n.isVisible().catch(()=>!1)&&(await n.click(),o=!0)}catch(n){console.error("[autoCloseSolflareNotification]: ",n)}if(a||o||t.isClosed())break;await st(150)}}var T,fr=({slowMo:t=0,profileName:e}={})=>Tt.extend({contextPath:async({browserName:r},o,a)=>{let n=await h(`${r}-${a.testId}`);await o(n)},context:async({context:r,contextPath:o},a)=>{let n=new x,p=u(n.name),l=await S(n.name),c=Ct.resolve(p,e??"wallet-data");if(!ct.existsSync(c))throw new Error("\u274C Cache for Solflare wallet data not found. Create it first");await ct.promises.cp(c,o,{recursive:!0,force:!0});let f=[`--disable-extensions-except=${l}`,`--load-extension=${l}`];process.env.HEADLESS&&(f.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let m=await It.launchPersistentContext(o,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:t});await m.grantPermissions(["clipboard-read"]);let{cookies:d,origins:w}=await r.storageState();d&&await m.addCookies(d),w&&w.length>0&&V(w,m);let R=await n.indexUrl(),$=R.split("#")[0]??"";await m.waitForEvent("page",{predicate:g=>g.url().includes($),timeout:15e3}),T=m.pages().find(g=>g.url().startsWith($))||await U(m,R);for(let g of m.pages())g.url().includes("about:blank")&&await g.close();await D(T),await a(m),await O(m,o)},solflarePage:async({context:r},o)=>{await o(T)},solflare:async({context:r},o)=>{let a=new B(T);await o(a)},autoCloseNotification:[async({context:r},o)=>{let a=!1,p=L(T,()=>a);await o(void 0),a=!0,await p.catch(l=>{console.error(`Auto close notification error: ${l.message}`)})},{auto:!0}]});import{test as Et}from"@playwright/test";import lt from"fs";import bt from"path";import{chromium as vt}from"@playwright/test";async function pt({workerInfo:t,profileName:e,slowMo:r}){let o=new x,a=await h(t.workerIndex.toString()),n=u(o.name),p=bt.resolve(n,e??"wallet-data");if(!lt.existsSync(p))throw new Error(`Cache for ${o.name} does not exist. Create it first!`);lt.cpSync(p,a,{recursive:!0,force:!0});let l=await S(o.name),c=await vt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:r}),f=await o.indexUrl(),m=f.split("#")[0]??"";await c.waitForEvent("page",{predicate:w=>w.url().includes(m),timeout:15e3});let d=c.pages().find(w=>w.url().startsWith(m));d||(d=await c.newPage(),await d.goto(f),await C(d));for(let w of c.pages())w.url().includes("about:blank")&&await w.close();return{context:c,walletPage:d,contextPath:a}}var Nr=({slowMo:t,profileName:e}={})=>Et.extend({workerScopeContents:[async({browser:r},o,a)=>{let{context:n,contextPath:p,walletPage:l}=await pt({workerInfo:a,profileName:e,slowMo:t});await n.grantPermissions(["clipboard-read"]);let c=new B(l);await c.unlock(),await o({wallet:c,walletPage:l,context:n}),await O(n,p)},{scope:"worker"}],autoCloseNotification:[async({workerScopeContents:r},o)=>{let a=!1,n=()=>a,p=L(r.walletPage,n);await o(void 0),a=!0,await p.catch(l=>{console.error(`Auto close notification error: ${l.message}`)})},{auto:!0}]});export{B as Solflare,fr as solflareFixture,Nr as solflareWorkerScopeFixture,ae as workerScopeContext};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chainwright",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.11",
|
|
4
4
|
"description": "Playwright Web3 wallet testing framework for end-to-end dApp automation with MetaMask, Phantom, Solflare, Petra, Meteor, and Keplr",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"changeset": "changeset",
|
|
110
110
|
"version-package": "changeset version",
|
|
111
111
|
"release": "pnpm run build && changeset publish",
|
|
112
|
-
"dev": "tsx src/cli/index.ts ./tests/wallet-setup -f",
|
|
112
|
+
"dev": "tsx src/cli/index.ts ./tests/wallet-setup --all -f",
|
|
113
113
|
"setup-wallets": "tsx src/cli/index.ts ./tests/wallet-setup --all",
|
|
114
114
|
"wallets": "tsx src/cli/index.ts ./tests/wallet-setup --wallets keplr petra",
|
|
115
115
|
"tests": "vitest --exclude '**/*.spec.ts'",
|