@tobelabs/chainwright 0.3.2 → 0.4.0
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/dist/cli/index.js +8 -8
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
${a}`,{encoding:"utf-8"})}catch(l){console.error("Error appending new profile name type: ",l)}}else try{L.writeFileSync($,a)}catch(r){console.error("Error writing new profile name type: ",r)}}import ne from"path";import{pathToFileURL as se}from"url";import{styleText as ie}from"util";import{glob as le}from"glob";import ae from"path";function I(e){let
|
|
4
|
-
`));return await Promise.all(s.map(async({filePath:i,walletName:p})=>{let f=await
|
|
5
|
-
`));return await
|
|
2
|
+
import j from"path";import{styleText as g}from"util";import Re from"@inquirer/checkbox";import{Command as Ie}from"commander";import L from"fs";import z from"path";import{fileURLToPath as ee}from"url";var te=ee(import.meta.url),oe=z.dirname(te),$=z.resolve(oe,"..","..","generated-profile-name.types.ts"),re=e=>e.replace(/(^\w)/g,t=>t.toUpperCase());async function H({walletName:e,profileName:t}){let o=re(e),a=`export type ${o}Profiles = "${t}";`;if(L.existsSync($)){let r=L.readFileSync($,"utf-8"),n=r.match(new RegExp(`export type ${o}Profiles = ("[^"]+"(?:\\s*\\|\\s*"[^"]+")*)`));if(n){let l=n[0];if(!l.includes(`"${t}"`)){let s=l.concat(` | "${t}"`),c=r.replace(l,s);try{L.writeFileSync($,c)}catch(i){console.error("Error updating existing profile name type: ",i)}}}else try{L.appendFileSync($,`
|
|
3
|
+
${a}`,{encoding:"utf-8"})}catch(l){console.error("Error appending new profile name type: ",l)}}else try{L.writeFileSync($,a)}catch(r){console.error("Error writing new profile name type: ",r)}}import ne from"path";import{pathToFileURL as se}from"url";import{styleText as ie}from"util";import{glob as le}from"glob";import{tsImport as pe}from"tsx/esm/api";import ae from"path";function I(e){let t=ae.basename(e),o=t.match(/^([a-z0-9_]+)(?:-[a-z0-9_]+)*\.setup\.(?:ts|js|mjs)$/i);if(!o)throw new Error(`Invalid wallet setup filename: ${t} (expected "<name>[ -variant].setup.{ts,js,mjs}")`);return o[1]}var ce=e=>e.replace(/\\/g,"/"),me=e=>`${ce(ne.resolve(e))}/**/*.setup.{ts,js,}`,fe=e=>{let t=new URL(se(e)).href;return e.endsWith(".ts")?pe(t,import.meta.url):import(t)};async function O({walletSetupDir:e,selectedWallets:t}){let o=me(e),a=(await le(o,{dot:!0,absolute:!0,nodir:!0,windowsPathsNoEscape:!0})).sort(),r=t.length===1?t[0]:t,n=["metamask","solflare","petra","meteor","keplr","phantom"];Array.isArray(r)&&r.forEach(i=>{n.includes(i)||console.warn(ie("magenta",`Unsupported wallet: "${i}". Supported wallets are: ${n.join(", ")}`,{validateStream:!1}))});let s=(r==="all"?a:Array.isArray(r)?a.filter(i=>r.some(p=>i.includes(p))):a.filter(i=>i.includes(r))).map(i=>({filePath:i,walletName:I(i)}));if(!s.length||s.length===0)throw new Error([`No wallet setup file found at ${e} for wallet: "${t}".`,'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:u}=f;return{walletName:p,fileList:s,config:h,walletPassword:u,setupFunction:E}}))}import x from"fs";import R from"path";import{styleText as S}from"util";import{chromium as De}from"@playwright/test";import N from"path";import{fileURLToPath as ue}from"url";var de=ue(import.meta.url),we=N.dirname(de),ge=N.resolve(we,"..","../.wallet-cache");function P(e){return N.resolve(`${ge}/${e}`)}import{z as F}from"zod";var he=F.object({id:F.string(),name:F.string()}),ye=F.array(he);async function G(e,t){let o=await e.newPage();await o.goto("chrome://extensions");let a=await o.evaluate("chrome.management.getAll()"),r=ye.parse(a),n=r.find(l=>l.name.toLowerCase()===t.toLowerCase());if(!n)throw new Error([`[GetExtensionId] Extension with name ${t} not found.`,`Available extensions: ${r.map(l=>l.name).join(", ")}`].join(`
|
|
5
|
+
`));return await o.close(),n.id}var V="wallet-setup",K="13.22.0",C="https://github.com/amaify/chainwright/releases/download/v0.1.0/",xe=`https://github.com/MetaMask/metamask-extension/releases/download/v${K}/metamask-chrome-${K}.zip`,Se=`${C}solflare-wallet-extension-v2.19.1.zip`,Ee=`${C}petra-wallet-extension-v2.4.8.zip`,ve=`${C}phantom-wallet-extension-v26.10.0.zip`,Ae=`${C}meteor-wallet-extension-v0.7.0.zip`,Le=`${C}keplr-wallet-extension-v0.13.3.zip`,T={metamask:{downloadUrl:xe,extensionName:"MetaMask"},solflare:{downloadUrl:Se,extensionName:"Solflare Wallet"},petra:{downloadUrl:Ee,extensionName:"Petra Aptos Wallet"},phantom:{downloadUrl:ve,extensionName:"Phantom"},meteor:{downloadUrl:Ae,extensionName:"Meteor Wallet"},keplr:{downloadUrl:Le,extensionName:"Keplr"}};import A from"fs";import U from"path";import{styleText as W}from"util";import _e from"adm-zip";import{createWriteStream as $e}from"fs";import{Readable as Pe}from"stream";import{styleText as k}from"util";import Ce from"cli-progress";var We=12e4;async function Y({url:e,destination:t}){let o=new AbortController,a=setTimeout(()=>o.abort(),We),r=await fetch(e,{redirect:"follow",signal:o.signal});r.ok||(console.error(k("redBright",`\u274C Download failed: HTTP ${r.status}`,{validateStream:!1})),o.abort(),process.exit(1));let n=parseInt(r.headers.get("content-length")||"0",10),l=0,s=Pe.fromWeb(r.body);try{let c=new Ce.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=$e(t);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 X=!1;async function q({downloadUrl:e,name:t,force:o}){let a=P(t),n=T[t].extensionName,l=U.join(a,`${t}-extension.zip`),s=U.join(a,`${t}-extension`);if(o&&A.existsSync(a)&&(A.rmSync(a,{recursive:!0}),console.info(W("magenta",`\u{1F9F9} Removed ${n} because of the force flag`,{validateStream:!1}))),A.existsSync(a)||A.mkdir(a,{recursive:!0},i=>{if(i)throw Error("Failed to create cache directory");console.info(`\u2705 ${n} Cache directory created successfully.`)}),A.existsSync(s)?console.info(`\u2705 ${n} Version is downloaded already.`):(console.info(W("cyanBright",`\u{1F4E5} Downloading ${n} extension...`,{validateStream:!1})),await Y({url:e,destination:l}),console.info(W("green",`\u2705 ${t.toUpperCase()} Extension downloaded successfully.`,{validateStream:!1}))),!A.existsSync(s))console.info("\u{1F4E6} Extracting extension..."),new _e(l).extractAllTo(s,!0),console.info(`\u2705 ${n} Extension extracted successfully.`);else{if(X)return console.info(W("magentaBright",`Using the cached ${n} extension for profile creation.`,{validateStream:!1})),s;console.info(W("yellow",`\u26A0\uFE0F Skipping ${n} cache creation: Cache already exists at ${s}. Use --force to overwrite.`,{validateStream:!1}))}let c=U.join(s,"manifest.json");if(!A.existsSync(c))throw new Error(`\u274C (${n}) Invalid extension: manifest.json not found`);return X=!0,s}import{styleText as be}from"util";function B(e){return new Promise(t=>setTimeout(t,e))}var D=20,Fe=6e3,Z=4e3;async function Te(e){return e.pages().find(a=>{try{return a.url().startsWith("chrome-extension://")}catch(r){return console.error("[WaitForExtensionOnLoadPage] Error checking page URL:",r),!1}})}async function J(e,t){let o=0,a=null;if(t==="meteor")return await e.newPage();for(console.info(`Waiting ${Z}ms for browser to initialize...`),await B(Z);o<=D;)try{if(console.info(`Looking for extension page (attempt ${o+1}/${D})...`),o===D)throw new Error("Extension page not found after maximum retries");let r=await Te(e);if(r){console.info(`Found extension page after ${o+1} polling attempts`),a=r;break}r||(o++,console.info(`Extension page not found, retrying (${o}/${D})...`),await B(Fe))}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 ${o} attempts!`)}return console.info(be("greenBright","\u2705 Extension page is properly loaded and ready",{validateStream:!1})),a}async function M({walletName:e,force:t,config:o,fileList:a,walletPassword:r,setupFunction:n}){let{downloadUrl:l,extensionName:s}=T[e],c=P(e),i=o?.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),u=await q({downloadUrl:l,name:e,force:t}),y=[`--disable-extensions-except=${u}`,`--load-extension=${u}`];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(
|
|
9
|
-
Setting up cache for ${p}...`,{validateStream:!1})),await
|
|
10
|
-
\u274C Failed to setup cache for ${p}: ${u.message}`,{validateStream:!1}));let
|
|
11
|
-
\u274C Attempt ${
|
|
8
|
+
`));if(x.existsSync(h))return;let v=await De.launchPersistentContext(h,{headless:!1,args:y,slowMo:o?.slowMo??0});console.info(S("magentaBright",`\u{1F9E9}\u{1F680} Starting Chrome extension for ${e.toUpperCase()}`,{validateStream:!1}));let Q=await J(v,e);if(!x.existsSync(m)&&!x.existsSync(f)){let b=await G(v,s);console.info(S("magentaBright",`\u{1F194} ${s} extension ID: ${b}`,{validateStream:!1})),x.writeFileSync(m,b,"utf-8"),console.info(S("cyanBright",`\u{1F4BE} Saved extension ID to: ${m}`,{validateStream:!1})),x.writeFileSync(f,u,"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:v,walletPage:Q})}catch(b){await v.close(),x.rmSync(c,{force:!0,recursive:!0}),console.error("Error setting up wallet: ",b.message)}await v.close()}var Oe=j.resolve(process.cwd(),"tests",V),_=2;async function Ne(){let e=new Ie;e.name(g("yellow","Chainwright")).description(g("green","A CLI tool for setting up wallet cache for E2E testing of web3 applications")).version(g("blue","0.0.0")),e.argument("[dir]","Directory containing the wallet setup functions",j.resolve(Oe)).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(t,o)=>{let a=["all","metamask","solflare","petra","meteor","keplr","phantom"],r=Object.keys(o).filter(p=>a.includes(p)?o[p]===!0:!1),n=r.length>0,l=o.wallets,s=l||(n?r:await Re({message:"Select the wallet you want to setup",choices:[{name:"All",value:"all"},{name:"Keplr",value:"keplr"},{name:"MetaMask",value:"metamask"},{name:"Meteor",value:"meteor"},{name:"Petra",value:"petra"},{name:"Phantom",value:"phantom"},{name:"Solflare",value:"solflare"}],validate:p=>{let m=p.map(f=>f.value);return m.includes("all")&&m.length>1?'Select either "All" or specific wallets, not both.':!0},pageSize:10})),c=j.resolve(process.cwd(),t);o.headless&&(process.env.HEADLESS=!0);let i=await O({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 M({walletName:p,config:m,setupFunction:E,fileList:h,force:o.force,walletPassword:f}),m.profileName&&await H({walletName:p,profileName:m.profileName})}catch(u){if(u.message.includes("directory already exists")&&console.warn(u.message),!u.message.includes("directory already exists")){console.error(g("redBright",`
|
|
10
|
+
\u274C Failed to setup cache for ${p}: ${u.message}`,{validateStream:!1}));let y=0;for(;_>y;){console.info(`${g("yellow",`Retry Attempt ${y+1} of ${_} for ${p}...`,{validateStream:!1})}`),console.info(g("yellow","Retrying wallet setup...",{validateStream:!1}));try{await M({walletName:p,config:m,setupFunction:E,fileList:h,force:o.force,walletPassword:f});break}catch(v){y+1<_&&console.error(g("redBright",`
|
|
11
|
+
\u274C Attempt ${y+1} failed! Retrying...`,{validateStream:!1})),y++,y===_&&console.error(g("redBright",`\u274C Failed to setup cache after ${_} attempts for ${p}: ${v.message}`,{validateStream:!1}))}}}}}),await e.parseAsync(process.argv)}Ne().catch(e=>console.error(g("redBright",`Failed to run the CLI: ${e.message})`,{validateStream:!1})));export{Ne as clientEntry};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tobelabs/chainwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "An end-to-end testing framework built on top of Playwright for Web3 wallet interactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,7 +57,6 @@
|
|
|
57
57
|
"@types/cli-progress": "^3.11.6",
|
|
58
58
|
"@types/node": "^25.8.0",
|
|
59
59
|
"tsup": "^8.5.1",
|
|
60
|
-
"tsx": "^4.22.0",
|
|
61
60
|
"typescript": "^6.0.3",
|
|
62
61
|
"vitest": "^4.1.6"
|
|
63
62
|
},
|
|
@@ -68,6 +67,7 @@
|
|
|
68
67
|
"commander": "^14.0.3",
|
|
69
68
|
"glob": "^13.0.6",
|
|
70
69
|
"prool": "^0.2.4",
|
|
70
|
+
"tsx": "^4.22.0",
|
|
71
71
|
"zod": "^4.4.3"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|