chainwright 0.9.5 → 0.9.7

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.
@@ -18,14 +18,29 @@ declare class KeplrProfile {
18
18
  declare const keplrChains: readonly ["Injective", "Injective (Testnet)", "Polygon", "Bitcoin", "Bitcoin Signet", "Bitcoin Testnet"];
19
19
  type KeplrChains = (typeof keplrChains)[number];
20
20
  type AddAndOnboardingArgs = {
21
+ walletName: string;
22
+ seedPhrase: string;
23
+ mode: "seedPhrase";
24
+ chains: Array<KeplrChains>;
25
+ } | {
21
26
  walletName: string;
22
27
  privateKey: string;
28
+ mode: "privateKey";
23
29
  chains: Array<KeplrChains>;
24
30
  };
25
31
  type OnboardingArgs = Array<AddAndOnboardingArgs>;
26
- type AddAccountArgs = AddAndOnboardingArgs & {
32
+ type AddAccount = {
33
+ walletName: string;
34
+ chains: Array<KeplrChains>;
35
+ } & {
27
36
  mode: "add-account-multiple" | "add-account-single" | "onboard";
28
37
  };
38
+ interface AddAccountViaPrivateKey extends AddAccount {
39
+ privateKey: string;
40
+ }
41
+ interface AddAccountViaSeedPhrase extends AddAccount {
42
+ seedPhrase: string;
43
+ }
29
44
  declare const getAccountAddressSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
30
45
  chain: z.ZodLiteral<"Injective" | "Injective (Testnet)" | "Polygon">;
31
46
  walletName: z.ZodString;
@@ -113,13 +128,14 @@ declare class Keplr extends KeplrProfile {
113
128
  * @param {AddAccountArgs} args - The arguments to add the account.
114
129
  * @param args.chains - The chains of the account to add.
115
130
  * @param args.privateKey - The private key of the account to add, if the mode is "privateKey".
131
+ * @param args.seedPhrase - The seed phrase of the account to add, if the mode is "seedPhrase".
116
132
  * @param args.walletName - The name of the wallet to add the account to.
117
133
  * @param args.mode - The mode of adding the account (default: "add-account-multiple").
118
134
  * @example
119
135
  * const keplr = new Keplr(page);
120
136
  * await keplr.addAccount({ chains: ["Testnet"], privateKey: "private key", walletName: "Keplr Wallet", mode: "add-account-multiple" });
121
137
  */
122
- addAccount({ chains, privateKey, walletName, mode }: AddAccountArgs): Promise<void>;
138
+ addAccount({ chains, walletName, mode, ...args }: AddAccountViaPrivateKey | AddAccountViaSeedPhrase): Promise<void>;
123
139
  /**
124
140
  * Connects to the wallet.
125
141
  * This function connects to the wallet by opening the connect page and then clicking on the connect button.
@@ -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.33.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 x=t.locator("div:has-text('All Native Chains')").nth(-4),P=t.locator("div[cursor='pointer']:has-text('Cosmos Hub')"),b=await x.locator("input[type='checkbox']").getAttribute("checked"),k=await P.locator("input[type='checkbox']").getAttribute("checked");b!==null&&await x.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(),x;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){x=D.locator("xpath=../../../.."),await d.clear();break}}if(!x)throw Error(`Address for ${o.walletName} account on "${o.chain}" chain not found.`);return await x.hover(),await x.scrollIntoViewIfNeeded(),await x.click(),await x.waitFor({state:"detached",timeout:7e3}),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:y}=o;await W({page:t,privateKey:l,walletName:d,chains:y,mode:"onboard"})}let a=e.slice(1);for(let{privateKey:l,walletName:d,chains:y}of a){let v=await $(t);await W({page:v,privateKey:l,walletName:d,chains:y,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:y}=await o.storageState();d&&await l.addCookies(d),y&&y.length>0&&G(y,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
+ import Q from"fs";import bt from"path";import{chromium as kt}from"@playwright/test";import At from"path";var J=".wallet-cache",Y=".wallet-context";var X="13.33.0",E="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Ut=`https://github.com/MetaMask/metamask-extension/releases/download/v${X}/metamask-chrome-${X}.zip`,Mt=`${E}solflare-wallet-extension-v2.19.1.zip`,Ht=`${E}petra-wallet-extension-v2.4.8.zip`,jt=`${E}phantom-wallet-extension-v26.10.0.zip`,zt=`${E}meteor-wallet-extension-v0.7.0.zip`,Gt=`${E}keplr-wallet-extension-v0.13.3.zip`;async function I(t){return At.resolve(process.cwd(),Y,t)}import Pt from"path";function x(t){return Pt.resolve(process.cwd(),J,t)}import q from"fs";import vt from"path";async function N(t){try{let o=x(t),e=vt.resolve(o,"extension-path.txt");if(!q.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=q.readFileSync(e,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function Z({wallet:t,workerInfo:o,profileName:e,slowMo:a}){let r=await I(o.workerIndex.toString()),i=x(t.name),l=bt.resolve(i,e??"wallet-data");if(!Q.existsSync(l))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);Q.cpSync(l,r,{recursive:!0,force:!0});let d=await N(t.name),p=await kt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${d}`],slowMo:process.env.HEADLESS?0:a}),m=await t.indexUrl(),n=p.pages()[0];return n||(n=await p.newPage()),await n.goto(m),{context:p,walletPage:n,contextPath:r}}import{expect as rt}from"@playwright/test";function S(t){return new Promise(o=>setTimeout(o,t))}async function k(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import tt from"fs";import St from"path";async function _(t){let o=x(t),e=St.resolve(o,"password.txt");try{if(!tt.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return tt.readFileSync(e,"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 et({context:t,path:o,locator:e}){let a;try{await Ct.poll(async()=>(a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(o)),!!a),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(i=>i.url().startsWith("chrome-extension://")).map(i=>i.url());throw new Error(`Popup page with path "${o}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!a)throw new Error(`Popup page with path ${o} not found in context.`);return await Bt(a,e),await a.setViewportSize({width:360,height:592}),a}async function Bt(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 ot from"fs";import Tt from"path";async function at(t){let o=x(t),e=Tt.resolve(o,"extension-id.txt");try{if(!ot.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ot.readFileSync(e,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var y=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 at(this.name)}async promptPage(o){let e=await this.promptUrl();return await et({context:o,path:e,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 Wt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function C({page:t,privateKey:o,walletName:e,chains:a,mode:r="onboard"}){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(o),await nt({page:t,walletName:e,mode:r,chains:a})}async function B({page:t,seedPhrase:o,walletName:e,chains:a,mode:r="onboard"}){await t.locator(g.importExistingWalletButton).click(),await t.locator(g.usePrivateKeyButton).click(),await t.getByRole("button",{name:"12 words",exact:!0}).click();let p=t.locator("input[type='password']"),m=o.split(" ");await p.first().fill(m[0]),await p.first().press("Tab");for(let n=1;n<m.length;n++){let u=p.nth(n),w=m[n];await u.fill(w),n<m.length-1&&await u.press("Tab")}await nt({page:t,walletName:e,mode:r,chains:a})}async function nt({page:t,walletName:o,mode:e,chains:a}){let r=new y,i=await _("keplr");if(await t.getByRole("button",{name:"Import",exact:!0}).click(),await t.locator(g.walletNameInput).fill(o),e==="onboard"){let f=t.locator(g.walletPasswordInput),b=t.locator(g.confirmWalletPasswordInput);await f.fill(i),await b.fill(i)}await t.locator(g.nextButton).click();let m=t.locator("div:has-text('All Native Chains')").nth(-4),n=t.locator("div[cursor='pointer']:has-text('Cosmos Hub')"),u=await m.locator("input[type='checkbox']").getAttribute("checked"),w=await n.locator("input[type='checkbox']").getAttribute("checked");u!==null&&await m.click(),w!==null&&await n.click();let P=t.locator(g.searchNetworkInput);for(let f of a){await P.fill(f);let v=t.locator("div[class='simplebar-content']").locator("div[cursor] > div").first().locator("div").filter({hasText:new RegExp(`^${Wt(f)}$`,"i")}).nth(2).locator("../../../../..");await v.waitFor({state:"visible",timeout:2e4}),await v.locator("input[type='checkbox']").getAttribute("checked")===null&&await v.click()}let h=t.locator(g.saveButton);if(await h.scrollIntoViewIfNeeded(),await h.click(),await S(2e3),e==="onboard"){await t.goto(await r.indexUrl());return}if(e==="add-account-single"){let f=t.locator(g.finishButton);await f.waitFor({state:"visible",timeout:2e4}),await rt(f).toBeEnabled({timeout:2e4}),await f.click()}}async function K(t){let e=await new y().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 l;if(await rt.poll(async()=>(l=t.context().pages().find(d=>d.url().match(e)),!!l),{timeout:3e4}).toBe(!0).catch(d=>{console.error(`Failed to find onboarding page with URL matching ${e}. Original error: ${d}`)}),!l)throw new Error(`Onboarding page not found. Expected URL: ${e}`);return await k(l),await l.bringToFront(),l}async function it({page:t,chains:o,walletName:e,mode:a,...r}){let i=await K(t);"privateKey"in r&&await C({page:i,privateKey:r.privateKey,walletName:e,chains:o,mode:a}),"seedPhrase"in r&&await B({page:i,seedPhrase:r.seedPhrase,walletName:e,chains:o,mode:a}),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 Et}from"@playwright/test";var O={approveButton:"button:has-text('Approve')",rejectButton:"button[color='secondary']"};async function st(t){let o=t.locator(O.approveButton);await Et(o).toBeEnabled({timeout:2e4}),await o.click()}async function ct(t){t.getByRole("button",{name:"Approve",exact:!0}).click(),await S(1e3)}import{expect as It}from"@playwright/test";import A from"zod";var lt=A.discriminatedUnion("chain",[A.object({chain:A.literal(["Injective","Injective (Testnet)","Polygon"]),walletName:A.string().min(1,"Wallet name cannot be an empty string")}),A.object({chain:A.literal(["Bitcoin","Bitcoin Signet","Bitcoin Testnet"]),chainTag:A.literal(["Taproot","Native Segwit"]),walletName:A.string().min(1,"Wallet name cannot be an empty string")})]),pt=A.object({currentAccountName:A.string().min(1,"Current account name cannot be an empty string"),newAccountName:A.string().min(1,"New account name cannot be an empty string")});async function mt({page:t,...o}){let e=lt.parse({...o});await t.getByRole("textbox",{name:"Search for asset or chain (i.e. ATOM, Cosmos)",exact:!0}).fill(e.chain);let r=t.locator(`div:has-text("${e.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 "${e.chain}" is activated.`);let l=await r.locator("div").all();It(l.length).toBeGreaterThan(0),await t.locator(`div:has(div:has-text('${e.walletName}'))`).nth(-3).locator("div:has(> div > svg)").click();let m=t.locator("div:has(> div[data-simplebar='init'])").last(),u=m.locator("div:has(> div > input)").locator("input");await u.fill(e.chain);let P=await m.locator("div[cursor='pointer']",{hasText:e.chain}).all(),h;for(let b of P){let v;"chainTag"in o&&(v=o.chainTag);let $=b.locator("div",{hasText:e.chain}).last(),V=v?b.locator("div",{hasText:v}).last():null,z=(V?await V?.isVisible().catch(()=>!1):!1)?await V?.textContent():null,G=await $.textContent(),xt=z?`${G} ${z}`:G,yt=v?`${e.chain} ${v}`:e.chain;if(xt===yt){h=$.locator("xpath=../../../.."),await u.clear();break}}if(!h)throw Error(`Address for ${e.walletName} account on "${e.chain}" chain not found.`);return await h.hover(),await h.scrollIntoViewIfNeeded(),await h.click(),await h.waitFor({state:"detached",timeout:7e3}),await t.evaluate(async()=>await navigator.clipboard.readText())}var F={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'])"},U={unlockButton:"button[type='submit']:has-text('Unlock')",passwordInput:"input[placeholder='Type Your Password']"};async function dt(t){await t.locator(F.openSidebarMenuButton).click(),await t.locator(F.lockWalletButton).nth(-1).click(),await t.getByText("Welcome Back").waitFor({state:"visible",timeout:3e4})}import{styleText as ut}from"util";import{expect as Ft}from"@playwright/test";async function R(t,o){let e=t.locator("div[color]").nth(1);if(await e.textContent()===o){console.info(`
2
+ Already on ${o} account. No need to switch.`);return}await e.click();let r=t.locator("div[class='simplebar-content'] > div").locator("> div",{hasText:o});if(!await r.isVisible().catch(()=>!1))throw new Error(`Account "${o}" not found. Make sure the account is onboarded or verify the account name.`);let l=t.locator("div:has-text('Select Wallet')").last();await r.click(),await l.waitFor({state:"detached",timeout:3e4})}async function M({page:t,onboard:o}){if(console.info(ut("yellowBright",`
3
+ Keplr onboarding started...`,{validateStream:!1})),o.length===1){let e=o[0];if(e&&e.mode==="privateKey"){let{privateKey:a,walletName:r,chains:i}=e;await C({page:t,privateKey:a,walletName:r,chains:i,mode:"onboard"})}if(e&&e.mode==="seedPhrase"){let{seedPhrase:a,walletName:r,chains:i}=e;await B({page:t,seedPhrase:a,walletName:r,chains:i,mode:"onboard"})}}if(o.length>1){let e=o[0];if(e&&e.mode==="privateKey"){let{privateKey:n,walletName:u,chains:w}=e;await C({page:t,privateKey:n,walletName:u,chains:w,mode:"onboard"})}if(e&&e.mode==="seedPhrase"){let{seedPhrase:n,walletName:u,chains:w}=e;await B({page:t,seedPhrase:n,walletName:u,chains:w,mode:"onboard"})}let a=o.slice(1);for(let{...n}of a){let u=await K(t);if(n.mode==="privateKey"){let{privateKey:w,walletName:P,chains:h}=n;await C({page:u,privateKey:w,walletName:P,chains:h,mode:"add-account-single"})}if(n.mode==="seedPhrase"){let{seedPhrase:w,walletName:P,chains:h}=n;await B({page:u,seedPhrase:w,walletName:P,chains:h,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 d=t.locator(F.openSidebarMenuButton);await Ft(d).toBeVisible({timeout:3e4});let p=o.at(-1)?.walletName,m=o[0]?.walletName;p&&m&&await R(t,m)}await S(3e3),console.info(ut("greenBright","\u2728 Keplr onboarding completed successfully",{validateStream:!1}))}async function wt(t){let o=t.locator(O.rejectButton);await o.waitFor({state:"visible",timeout:2e4}),await o.click()}async function ht({page:t,currentAccountName:o,newAccountName:e}){let a=pt.parse({currentAccountName:o,newAccountName:e});await t.getByRole("link",{name:"Settings",exact:!0}).click(),await t.locator("div[cursor='pointer']").first().click();let l=t.locator("div",{hasText:a.currentAccountName}).nth(-4);if(!await l.isVisible().catch(()=>!1))throw Error(`Account with name "${a.currentAccountName}" not found`);await l.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 D(t){let o=await _("keplr");await t.locator(U.passwordInput).fill(o),await t.locator(U.unlockButton).click(),await t.locator("div:has-text('Deposit')").last().waitFor({state:"visible",timeout:3e4})}var T=class extends y{page;constructor(o){super(),this.page=o}async onboard(o){await M({page:this.page,onboard:o})}async unlock(){await D(this.page)}async lock(){await dt(this.page)}async renameAccount({currentAccountName:o,newAccountName:e}){await ht({page:this.page,currentAccountName:o,newAccountName:e})}async switchAccount(o){await R(this.page,o)}async getAccountAddress({...o}){return await mt({page:this.page,...o})}async addAccount({chains:o,walletName:e,mode:a="add-account-multiple",...r}){await it({page:this.page,walletName:e,chains:o,mode:a,...r})}async connectToApp(){await ct(await this.promptPage(this.page.context()))}async confirmTransaction(){await st(await this.promptPage(this.page.context()))}async rejectTransaction(){await wt(await this.promptPage(this.page.context()))}};import gt from"fs";import Ot from"path";import{test as Rt,chromium as Dt}from"@playwright/test";import{expect as Nt}from"@playwright/test";async function H(t,o){let e=await t.newPage();return await Nt(async()=>{await e.goto(o),await k(e)}).toPass(),e}async function j(t,o){let e=await o.newPage();for(let{origin:a,localStorage:r}of t){let i=e.mainFrame();await i.goto(a),await i.evaluate(l=>{l.forEach(({name:d,value:p})=>{window.localStorage.setItem(d,p)})},r)}await e.close()}import _t from"fs/promises";async function ft(t){await _t.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Kt=35e3;async function L(t,o){try{await Promise.race([t.close(),new Promise((e,a)=>setTimeout(()=>a(new Error("Context close timed out")),Kt))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await ft(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}var W,ba=({slowMo:t=0,profileName:o}={})=>Rt.extend({contextPath:async({browserName:e},a,r)=>{let i=await I(`${e}-${r.testId}`);await a(i)},context:async({context:e,contextPath:a},r)=>{let i=new y,l=x(i.name),d=await N(i.name),p=Ot.resolve(l,o??"wallet-data");if(!gt.existsSync(p))throw new Error("\u274C Cache for Keplr wallet data not found. Create it first");gt.cpSync(p,a,{recursive:!0,force:!0});let m=[`--disable-extensions-except=${d}`,`--load-extension=${d}`];process.env.HEADLESS&&(m.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let n=await Dt.launchPersistentContext(a,{headless:!1,args:m,slowMo:process.env.HEADLESS?0:t});await n.grantPermissions(["clipboard-read"]);let{cookies:u,origins:w}=await e.storageState();u&&await n.addCookies(u),w&&w.length>0&&j(w,n);let P=await i.indexUrl();W=n.pages().find(f=>f.url().startsWith(P))||await H(n,P),await k(W);for(let f of n.pages()){let b=f.url();(b.includes("about:blank")||b.includes(i.onboardingPath))&&await f.close()}await W.bringToFront(),await D(W),await r(n),await L(n,a)},keplrPage:async({context:e},a)=>{await a(W)},keplr:async({context:e},a)=>{let r=new T(W);await a(r)}});import{test as Lt}from"@playwright/test";var Ia=({slowMo:t,profileName:o}={})=>Lt.extend({workerScopeContents:[async({browser:e},a,r)=>{let i=new y,{context:l,contextPath:d,walletPage:p}=await Z({wallet:i,workerInfo:r,profileName:o,slowMo:t});await l.grantPermissions(["clipboard-read"]);for(let n of l.pages())n.url().includes("about:blank")&&await n.close();let m=new T(p);await m.unlock(),await a({wallet:m,walletPage:p,context:l}),await L(l,d)},{scope:"worker"}]});export{T as Keplr,ba as keplrFixture,Ia as keplrWorkerScopeFixture,Z as workerScopeContext};
@@ -14,20 +14,32 @@ declare class MeteorProfile {
14
14
  }
15
15
 
16
16
  type MeteorNetwork = "Mainnet" | "Testnet";
17
- type OnboardingArgs = {
17
+ type SharedOnboardArgs = {
18
18
  network: MeteorNetwork;
19
- privateKey: string;
20
19
  accountName: string;
21
20
  additionalAccounts?: Array<AddAccountArgs>;
22
21
  };
22
+ type OnboardingArgs = ({
23
+ mode: "privateKey";
24
+ privateKey: string;
25
+ } | {
26
+ mode: "secretPhrase";
27
+ secretPhrase: string;
28
+ }) & SharedOnboardArgs;
23
29
  type RenameAccountArgs = {
24
30
  newAccountName: string;
25
31
  };
26
- type AddAccountArgs = {
27
- privateKey: string;
32
+ type AddAccountCommonArgs = {
28
33
  accountName: string;
29
34
  network: MeteorNetwork;
30
35
  };
36
+ type AddAccountArgs = ({
37
+ privateKey: string;
38
+ mode: "privateKey";
39
+ } | {
40
+ secretPhrase: string;
41
+ mode: "secretPhrase";
42
+ }) & AddAccountCommonArgs;
31
43
  type MeteorFixture = {
32
44
  contextPath: string;
33
45
  meteor: Meteor;
@@ -47,7 +59,7 @@ declare class Meteor extends MeteorProfile {
47
59
  * const meteor = new Meteor(page);
48
60
  * await meteor.onboard({ mode: "importPrivateKey", password: "password", privateKey: "private key" });
49
61
  */
50
- onboard({ network, privateKey, accountName, additionalAccounts }: OnboardingArgs): Promise<void>;
62
+ onboard({ network, accountName, additionalAccounts, ...args }: OnboardingArgs): Promise<void>;
51
63
  /**
52
64
  * Unlocks the wallet by entering the password.
53
65
  * @example
@@ -107,7 +119,7 @@ declare class Meteor extends MeteorProfile {
107
119
  * const meteor = new Meteor(page);
108
120
  * await meteor.addAccount(TBD);
109
121
  */
110
- addAccount({ accountName, network, privateKey }: AddAccountArgs): Promise<void>;
122
+ addAccount({ accountName, network, ...args }: AddAccountArgs): Promise<void>;
111
123
  /**
112
124
  * Opens the settings page for the wallet.
113
125
  * @example
@@ -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.33.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
+ 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.33.0",M="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Dt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ot=`${M}solflare-wallet-extension-v2.19.1.zip`,Rt=`${M}petra-wallet-extension-v2.4.8.zip`,Ut=`${M}phantom-wallet-extension-v26.10.0.zip`,$t=`${M}meteor-wallet-extension-v0.7.0.zip`,Vt=`${M}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),r=xt.resolve(o,"extension-path.txt");if(!Z.existsSync(r))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let e=Z.readFileSync(r,"utf-8").trim();if(!e)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return e}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function ot({wallet:t,workerInfo:o,profileName:r,slowMo:e}){let a=await E(o.workerIndex.toString()),c=f(t.name),w=gt.resolve(c,r??"wallet-data");if(!tt.existsSync(w))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);tt.cpSync(w,a,{recursive:!0,force:!0});let l=await N(t.name),p=await yt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:e}),s=await t.indexUrl(),d=p.pages()[0];return d||(d=await p.newPage()),await d.goto(s),{context:p,walletPage:d,contextPath:a}}var u={openSidebarMenuButton:"button[aria-label='open sidebar']",addWalletButton:"button:has-text('Add Wallet')",settingsButton:"button:has-text('Settings')",settingsMenuBackButton:"button[aria-label='Back']"};var m={importExistingWalletButton:'button:has-text("Import an existing wallet")',switchNetworkButton:"button[id^='menu-button']",privateKeyButton:"button:has-text('Private Key')",secretPhraseButton:"button:has-text('Secret Phrase')",findMyAccountButton:"button:has-text('Find my account')"};async function I(t,o,r){let e=t.locator(m.switchNetworkButton).last();await e.scrollIntoViewIfNeeded();let a=await e.textContent(),c=o.split("net")[0]?.toLowerCase()??"";if(a?.toLowerCase()===c)return console.info(`
2
+ Already on ${o}, no need to switch network.`),"Exit";await e.click();let l=t.locator(r).last().locator(`> button:has-text('${o}')`);await l.click(),await t.locator("div > h2:has-text('Meteor Community')").isVisible().catch(()=>!1)&&await l.click()}async function k({page:t,newAccountName:o}){await t.locator(u.openSidebarMenuButton).click(),await t.locator("div:has(> h2):has(> svg)").click();let a=t.locator("input[placeholder='Ex. My Meteor Wallet']"),c=t.locator("button[type='submit']:has-text('Update')");await a.fill(o),await c.click(),await t.locator("div[id='root'] button[aria-label='Close']").click()}async function _({page:t,accountName:o,network:r,...e}){await t.locator(u.openSidebarMenuButton).click(),await t.locator(u.addWalletButton).click(),await I(t,r,"section[role='dialog'] div[role='menu']"),await t.locator(m.importExistingWalletButton).click();let l=t.locator('button:has-text("Continue")');e.mode==="secretPhrase"&&(await t.locator(m.secretPhraseButton).click(),await l.scrollIntoViewIfNeeded(),await l.click(),await t.locator("textarea:not([disabled])").fill(e.secretPhrase)),e.mode==="privateKey"&&(await t.locator(m.privateKeyButton).click(),await l.scrollIntoViewIfNeeded(),await l.click(),await t.locator("textarea:not([disabled])").fill(e.privateKey)),await t.locator(m.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 k({page:t,newAccountName:o})}var B={approveButton:"button:has-text('Approve')",connectButton:"button:has-text('Connect')",cancelButton:"button:has-text('Cancel')"};import et from"fs";import Pt from"path";async function F(t){let o=f(t),r=Pt.resolve(o,"password.txt");try{if(!et.existsSync(r))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(r,"utf-8")}catch(e){throw new Error(`\u274C Failed to get ${t} password from cache: ${e.message}`)}}async function x(t){let o=await F("meteor"),r=t.locator("input[placeholder='Enter Password']"),e=t.locator('button:has-text("Unlock")');await r.fill(o),await e.click()}async function rt(t){await x(t),await t.locator(B.approveButton).click(),await t.locator("h2:has-text('Executing Transaction')").waitFor({state:"attached"}),await t.waitForEvent("close",{timeout:15e3})}async function C(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(u.openSidebarMenuButton).click();let p=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 d of p)if((await d.textContent())?.toLowerCase()===o.toLowerCase()){s=d;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function at(t,o){await x(t),o&&await C(t,o);let r=t.getByRole("heading",{name:"Connect Request",exact:!0});await Promise.all([t.locator(B.connectButton).click(),r.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(u.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 bt}from"@playwright/test";async function ct({context:t,path:o,locator:r}){let e;try{await bt.poll(async()=>(e=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(o)),!!e),{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(!e)throw new Error(`Popup page with path ${o} not found in context.`);return await At(e,r),await e.setViewportSize({width:360,height:592}),e}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),r=kt.resolve(o,"extension-id.txt");try{if(!st.existsSync(r))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return st.readFileSync(r,"utf-8")}catch(e){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${e.message}`)}}var g=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 r=await this.promptUrl();return await ct({context:o,path:r,locator:"div[id='root']"})}};async function S(t){await t.locator(u.openSidebarMenuButton).click(),await t.locator(u.settingsButton).click()}async function L(t,o){if(await S(t),await I(t,o,"div[role='menu']")==="Exit"){let c=t.locator(u.settingsMenuBackButton);await c.scrollIntoViewIfNeeded(),await c.click();return}let e=t.locator("p:has-text('Available Balance')");if(await T(1e3),!await e.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,network:o,accountName:r,additionalAccounts:e,...a}){console.info(O("yellowBright",`
5
+ Meteor onboarding started...`,{validateStream:!1}));let c=await F("meteor"),l=await new g().indexUrl();await t.goto(l);let p=t.locator(m.switchNetworkButton),s=await p.textContent(),d=o.split("net")[0]?.toLowerCase()??"";s?.toLowerCase().includes(d)||(await p.click(),await t.locator("div[role='menu']").locator(`> button:has-text('${o}')`).click());let P=t.locator("input[placeholder='Enter Password']"),A=t.locator("input[placeholder='Confirm Password']"),K=t.locator("label.chakra-checkbox .chakra-checkbox__control"),h=t.locator('button:has-text("Continue")');await P.fill(c),await A.fill(c),await K.click(),await h.click(),await t.locator(m.importExistingWalletButton).click(),a.mode==="secretPhrase"&&(await t.locator(m.secretPhraseButton).click(),await h.scrollIntoViewIfNeeded(),await h.click(),await t.locator("textarea:not([disabled])").fill(a.secretPhrase)),a.mode==="privateKey"&&(await t.locator(m.privateKeyButton).click(),await h.scrollIntoViewIfNeeded(),await h.click(),await t.locator("textarea:not([disabled])").fill(a.privateKey));let H=t.locator(m.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 y=5,b=!1;for(;y>0;){if(console.info(`
6
+ Retrying search for account. ${y} 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)){b=!0;break}y-=1}if(!b)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 k({page:t,newAccountName:r}),e&&e.length>0){for(let{accountName:q,network:G,...dt}of e)await _({page:t,accountName:q,network:G,...dt});await S(t);let y=t.locator(m.switchNetworkButton).last();await y.scrollIntoViewIfNeeded();let b=await y.textContent(),X=o.split("net")[0]?.toLowerCase()??"";b?.toLowerCase()!==X&&await L(t,o),await C(t,r)}await T(3e3),console.info(O("greenBright","\u2728 Meteor onboarding completed successfully"))}async function pt(t){await x(t),await t.locator(B.cancelButton).click()}var v=class extends g{page;constructor(o){super(),this.page=o}async onboard({network:o,accountName:r,additionalAccounts:e,...a}){await R({page:this.page,network:o,accountName:r,additionalAccounts:e,...a})}async unlock(){await x(this.page)}async lock(){await it(this.page)}async renameAccount({newAccountName:o}){await k({page:this.page,newAccountName:o})}async switchNetwork(o){await L(this.page,o)}async switchAccount(o){await C(this.page,o)}async getAccountAddress(){return await nt(this.page)}async addAccount({accountName:o,network:r,...e}){await _({page:this.page,accountName:o,network:r,...e})}async openSettings(){await S(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 mt from"fs";import vt from"path";import{test as Mt,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 r=await t.newPage();return await Bt(async()=>{await r.goto(o),await U(r)}).toPass(),r}async function V(t,o){let r=await o.newPage();for(let{origin:e,localStorage:a}of t){let c=r.mainFrame();await c.goto(e),await c.evaluate(w=>{w.forEach(({name:l,value:p})=>{window.localStorage.setItem(l,p)})},a)}await r.close()}import Ct from"fs/promises";async function ut(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((r,e)=>setTimeout(()=>e(new Error("Context close timed out")),St))])}catch(r){console.warn(`Browser context close did not complete cleanly: ${r.message}`)}try{await ut(o)}catch(r){console.error(`Failed to remove temporary context directory at ${o}. Error:`,r)}}var W,Mr=({slowMo:t=0,profileName:o}={})=>Mt.extend({contextPath:async({browserName:r},e,a)=>{let c=await E(`${r}-${a.testId}`);await e(c)},context:async({context:r,contextPath:e},a)=>{let c=new g,w=f(c.name),l=await N(c.name),p=vt.resolve(w,o??"wallet-data");if(!mt.existsSync(p))throw new Error("\u274C Cache for Meteor wallet data not found. Create it first");mt.cpSync(p,e,{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(e,{headless:!1,args:[`--disable-extensions-except=${l}`,`--load-extension=${l}`],slowMo:process.env.HEADLESS?0:t});await s.grantPermissions(["clipboard-read"]);let{cookies:d,origins:P}=await r.storageState();d&&await s.addCookies(d),P&&P.length>0&&V(P,s);let A=await c.indexUrl();W=s.pages().find(h=>h.url().startsWith(A))||await $(s,A);for(let h of s.pages())h.url().includes("about:blank")&&await h.close();await W.bringToFront(),await x(W),await a(s),await D(s,e)},meteorPage:async({context:r},e)=>{await e(W)},meteor:async({context:r},e)=>{let a=new v(W);await e(a)}});import{test as Tt}from"@playwright/test";var Lr=({slowMo:t,profileName:o}={})=>Tt.extend({workerScopeContents:[async({browser:r},e,a)=>{let{context:c,contextPath:w,walletPage:l}=await ot({workerInfo:a,profileName:o,slowMo:t,wallet:new g});await c.grantPermissions(["clipboard-read"]);for(let s of c.pages())s.url().includes("about:blank")&&await s.close();let p=new v(l);await p.unlock(),await e({wallet:p,walletPage:l,context:c}),await D(c,w)},{scope:"worker"}]});export{v as Meteor,Mr as meteorFixture,Lr as meteorWorkerScopeFixture,ot as workerScopeContext};
@@ -20,6 +20,7 @@ type OnboardingArgs = {
20
20
  additionalAccounts?: Array<AddAccountArgs>;
21
21
  } | {
22
22
  mode: "recovery phrase";
23
+ accountName: string;
23
24
  secretRecoveryPhrase: string;
24
25
  toggleNetworkMode?: SwitchNetwork;
25
26
  additionalAccounts?: Array<AddAccountArgs>;
@@ -1,3 +1,3 @@
1
- import at from"fs";import At from"path";import{chromium as bt}from"@playwright/test";import Pt from"path";var tt=".wallet-cache",et=".wallet-context";var Z="13.33.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",zt=`https://github.com/MetaMask/metamask-extension/releases/download/v${Z}/metamask-chrome-${Z}.zip`,jt=`${_}solflare-wallet-extension-v2.19.1.zip`,Xt=`${_}petra-wallet-extension-v2.4.8.zip`,Jt=`${_}phantom-wallet-extension-v26.10.0.zip`,qt=`${_}meteor-wallet-extension-v0.7.0.zip`,Qt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return Pt.resolve(process.cwd(),et,t)}import kt from"path";function x(t){return kt.resolve(process.cwd(),tt,t)}import ot from"fs";import Ct from"path";async function I(t){try{let e=x(t),o=Ct.resolve(e,"extension-path.txt");if(!ot.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=ot.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=At.resolve(r,o??"wallet-data");if(!at.existsSync(u))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);at.cpSync(u,n,{recursive:!0,force:!0});let i=await I(t.name),m=await bt.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"},nt={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 R({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 rt(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 It}from"@playwright/test";import Tt from"zod";async function v(t,e){let o=Tt.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 it(t,e){e&&await v(t,e);let o=t.getByTestId(S.confirmButton);await It(o).toBeEnabled({timeout:15e3}),await o.click()}import St from"zod";var vt=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function ct({page:t,accountName:e,chain:o}){let a=St.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 d=new RegExp(`${vt(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 st(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as wt}from"util";import{expect as _t}from"@playwright/test";function M(t){return new Promise(e=>setTimeout(e,t))}import lt from"fs";import Et from"path";async function D(t){let e=x(t),o=Et.resolve(e,"password.txt");try{if(!lt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return lt.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Wt}from"@playwright/test";async function ut({context:t,path:e,locator:o}){let a;try{await Wt.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 Nt(a,o),await a.setViewportSize({width:360,height:592}),a}async function Nt(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 mt from"fs";import Ft from"path";async function pt(t){let e=x(t),o=Ft.resolve(e,"extension-id.txt");try{if(!mt.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return mt.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 pt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ut({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 M(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(nt.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(`
1
+ import at from"fs";import At from"path";import{chromium as bt}from"@playwright/test";import Pt from"path";var tt=".wallet-cache",et=".wallet-context";var Z="13.33.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",zt=`https://github.com/MetaMask/metamask-extension/releases/download/v${Z}/metamask-chrome-${Z}.zip`,jt=`${_}solflare-wallet-extension-v2.19.1.zip`,Xt=`${_}petra-wallet-extension-v2.4.8.zip`,Jt=`${_}phantom-wallet-extension-v26.10.0.zip`,qt=`${_}meteor-wallet-extension-v0.7.0.zip`,Qt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return Pt.resolve(process.cwd(),et,t)}import kt from"path";function x(t){return kt.resolve(process.cwd(),tt,t)}import ot from"fs";import Ct from"path";async function I(t){try{let e=x(t),o=Ct.resolve(e,"extension-path.txt");if(!ot.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=ot.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=At.resolve(r,o??"wallet-data");if(!at.existsSync(u))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);at.cpSync(u,n,{recursive:!0,force:!0});let i=await I(t.name),m=await bt.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"},nt={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 R({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 rt(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 It}from"@playwright/test";import Tt from"zod";async function v(t,e){let o=Tt.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 it(t,e){e&&await v(t,e);let o=t.getByTestId(S.confirmButton);await It(o).toBeEnabled({timeout:15e3}),await o.click()}import St from"zod";var vt=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function ct({page:t,accountName:e,chain:o}){let a=St.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(`${vt(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 st(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as wt}from"util";import{expect as _t}from"@playwright/test";function M(t){return new Promise(e=>setTimeout(e,t))}import lt from"fs";import Et from"path";async function D(t){let e=x(t),o=Et.resolve(e,"password.txt");try{if(!lt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return lt.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Wt}from"@playwright/test";async function ut({context:t,path:e,locator:o}){let a;try{await Wt.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 Ft(a,o),await a.setViewportSize({width:360,height:592}),a}async function Ft(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 mt from"fs";import Nt from"path";async function pt(t){let e=x(t),o=Nt.resolve(e,"extension-id.txt");try{if(!mt.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return mt.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var P=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 pt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ut({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 M(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(nt.accountProfileContainer).locator("div[data-testid^='manage-accounts-sortable'] div > p").all(),i=null;for(let d of u)if((await d.textContent())?.toLowerCase()===e.toLowerCase()){i=d;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 G({page:t,additionalAccounts:e,...o}){console.info(wt("yellowBright",`
3
- Phantom onboarding started...`,{validateStream:!1}));let a=await D("phantom");if(o.mode==="create"){await t.locator(p.createNewWalletButton).click(),await t.getByTestId(p.createSeedPhraseWalletButton).click();let f=t.getByTestId(p.passwordInput),w=t.getByTestId(p.passwordConfirmInput),A=t.getByTestId(p.termsCheckBox),P=t.locator(p.continueButton);await f.fill(a),await w.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 M(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[F,Y]of Object.entries(y))await t.getByTestId(`${p.recoveryPhraseInput}-${F}`).fill(Y);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),O=t.getByTestId(p.passwordConfirmInput),X=t.getByTestId(p.termsCheckBox);await b.fill(a),await O.fill(a),await X.click(),await C.click(),await C.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByRole("textbox",{name:"Username @ Clear",exact:!0}).waitFor({state:"attached",timeout:5e3}).then(async()=>{await t.getByRole("button",{name:"Continue",exact:!0}).click()}).catch(()=>{}),await t.locator(p.getStartedButton).last().click()}if(o.mode==="private key"){await t.locator(p.IAlreadyHaveAWalletButton).click();let{privateKey:y,chain:f,accountName:w}=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']"),O=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(w),await O.fill(y),await t.locator("button:has-text('Import')").click();let J=t.getByTestId(p.passwordInput),q=t.getByTestId(p.passwordConfirmInput),Q=t.getByTestId(p.termsCheckBox);await J.fill(a),await q.fill(a),await Q.click();let F=t.locator(p.continueButton);await F.click(),await F.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 _t.poll(async()=>{if(i){let{targetInfos:d}=await i.send("Target.getTargets"),f=d.filter(w=>w.title==="Phantom Wallet").find(w=>!w.attached&&w.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:d}=o;await $({page:n,newAccountName:d,currentAccountName:"Account 1"})}if(e&&e.length>0){let d=!1;E(n,()=>d).catch(w=>console.error({error:w}));for(let{accountName:w,chain:A,privateKey:P}of e)await R({page:n,privateKey:P,accountName:w,chain:A}),d=!0;let f=o.mode==="create"?o.accountName:l;f&&await v(n,f)}o.toggleNetworkMode&&await H({page:n,...o.toggleNetworkMode}),await M(3e3),console.info(wt("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 ft({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 D("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 G({page:this.page,...e})}async unlock(){await V(this.page)}async lock(){await st(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 ct({page:this.page,accountName:e,chain:o})}async addAccount({...e}){await R({page:this.page,...e})}async toggleOptionalChains({toggleMode:e,supportedChains:o}){await ft({page:this.page,supportedChains:o,toggleMode:e})}async switchNetwork({...e}){await H({page:this.page,...e})}async connectToApp(e){await it(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await rt(await this.promptPage(this.page.context()))}async rejectTransaction(){await dt(await this.promptPage(this.page.context()))}};import ht from"fs";import Rt from"path";import{test as Dt,chromium as $t}from"@playwright/test";import{expect as Mt}from"@playwright/test";async function z(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function L(t,e){let o=await t.newPage();return await Mt(async()=>{await o.goto(e),await z(o)}).toPass(),o}async function j(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 Lt from"fs/promises";async function gt(t){await Lt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Ot=35e3;async function K(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Ot))])}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 N,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=Rt.resolve(u,e??"wallet-data");if(!ht.existsSync(m))throw new Error("\u274C Cache for Phantom wallet data not found. Create it first");ht.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 $t.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:d}=await o.storageState();h&&await l.addCookies(h),d&&d.length>0&&await j(d,l);let y=await r.indexUrl();N=l.pages().find(w=>w.url().startsWith(y))||await L(l,y);for(let w of l.pages())w.url().includes("about:blank")&&await w.close();await N.bringToFront(),await V(N),await n(l),await K(l,a)},phantomPage:async({context:o},a)=>{await a(N)},phantom:async({context:o},a)=>{let n=new W(N);await a(n)},autoCloseNotification:[async({context:o},a)=>{let n=!1,u=E(N,()=>n);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});import{test as Vt}from"@playwright/test";import yt from"fs";import Ut from"path";import{chromium as Ht}from"@playwright/test";async function xt({workerInfo:t,profileName:e,slowMo:o}){let a=new k,n=await T(t.workerIndex.toString()),r=x(a.name),u=Ut.resolve(r,e??"wallet-data");if(!yt.existsSync(u))throw new Error(`Cache for ${a.name} does not exist. Create it first!`);yt.cpSync(u,n,{recursive:!0,force:!0});let i=await I(a.name),m=await Ht.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:o}),l=await a.indexUrl(),h=await L(m,l);return{context:m,walletPage:h,contextPath:n}}var rn=({slowMo:t,profileName:e}={})=>Vt.extend({workerScopeContents:[async({browser:o},a,n)=>{let{context:r,contextPath:u,walletPage:i}=await xt({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};
3
+ Phantom onboarding started...`,{validateStream:!1}));let a=await D("phantom");if(o.mode==="create"){await t.locator(p.createNewWalletButton).click(),await t.getByTestId(p.createSeedPhraseWalletButton).click();let d=t.getByTestId(p.passwordInput),f=t.getByTestId(p.passwordConfirmInput),A=t.getByTestId(p.termsCheckBox),k=t.locator(p.continueButton);await d.fill(a),await f.fill(a),await A.click(),await k.click(),await k.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByTestId(p.recoveryPhraseSavedCheckbox).click(),await k.click(),await M(1e3),await k.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[N,Y]of Object.entries(y))await t.getByTestId(`${p.recoveryPhraseInput}-${N}`).fill(Y);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),O=t.getByTestId(p.passwordConfirmInput),X=t.getByTestId(p.termsCheckBox);await b.fill(a),await O.fill(a),await X.click(),await C.click(),await C.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByRole("textbox",{name:"Username @ Clear",exact:!0}).waitFor({state:"attached",timeout:5e3}).then(async()=>{await t.getByRole("button",{name:"Continue",exact:!0}).click()}).catch(()=>{}),await t.locator(p.getStartedButton).last().click()}if(o.mode==="private key"){await t.locator(p.IAlreadyHaveAWalletButton).click();let{privateKey:y,chain:d,accountName:f}=o;await t.locator(p.importPrivateKeyButton).click();let k=t.locator("span[id='button--listbox-input--1']"),C=await k.textContent(),b=t.locator("input[name='name']"),O=t.locator("textarea[name='privateKey']");C!==d&&(await k.click(),await t.locator("ul[id='listbox--listbox-input--1']").locator(`li[data-label='${d}']`).click()),await b.fill(f),await O.fill(y),await t.locator("button:has-text('Import')").click();let J=t.getByTestId(p.passwordInput),q=t.getByTestId(p.passwordConfirmInput),Q=t.getByTestId(p.termsCheckBox);await J.fill(a),await q.fill(a),await Q.click();let N=t.locator(p.continueButton);await N.click(),await N.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(p.getStartedButton).last().click()}let n=await t.context().newPage(),r=await new P().indexUrl();await n.goto(r);let i=await t.context().browser()?.newBrowserCDPSession(),m;await _t.poll(async()=>{if(i){let{targetInfos:w}=await i.send("Target.getTargets"),d=w.filter(f=>f.title==="Phantom Wallet").find(f=>!f.attached&&f.url===r);return m=d,!!d}},{timeout:2e4}).toBe(!0),m&&await i?.send("Target.closeTarget",{targetId:m.targetId});let l=await n.getByTestId("home-header-account-name").textContent();if(!l)throw new Error("Cannot find initial account name");if(o.mode==="create"||o.mode==="recovery phrase"){let{accountName:w}=o;await $({page:n,newAccountName:w,currentAccountName:l})}if(e&&e.length>0){let w=!1;E(n,()=>w).catch(d=>console.error({error:d}));for(let{accountName:d,chain:f,privateKey:A}of e)await R({page:n,privateKey:A,accountName:d,chain:f}),w=!0;await v(n,o.accountName)}o.toggleNetworkMode&&await H({page:n,...o.toggleNetworkMode}),await M(3e3),console.info(wt("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 ft({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 D("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 P{page;constructor(e){super(),this.page=e}async onboard({...e}){await G({page:this.page,...e})}async unlock(){await V(this.page)}async lock(){await st(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 ct({page:this.page,accountName:e,chain:o})}async addAccount({...e}){await R({page:this.page,...e})}async toggleOptionalChains({toggleMode:e,supportedChains:o}){await ft({page:this.page,supportedChains:o,toggleMode:e})}async switchNetwork({...e}){await H({page:this.page,...e})}async connectToApp(e){await it(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await rt(await this.promptPage(this.page.context()))}async rejectTransaction(){await dt(await this.promptPage(this.page.context()))}};import ht from"fs";import Rt from"path";import{test as Dt,chromium as $t}from"@playwright/test";import{expect as Mt}from"@playwright/test";async function z(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function L(t,e){let o=await t.newPage();return await Mt(async()=>{await o.goto(e),await z(o)}).toPass(),o}async function j(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 Lt from"fs/promises";async function gt(t){await Lt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Ot=35e3;async function K(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Ot))])}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 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 P,u=x(r.name),i=await I(r.name),m=Rt.resolve(u,e??"wallet-data");if(!ht.existsSync(m))throw new Error("\u274C Cache for Phantom wallet data not found. Create it first");ht.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 $t.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 j(w,l);let y=await r.indexUrl();F=l.pages().find(f=>f.url().startsWith(y))||await L(l,y);for(let f of l.pages())f.url().includes("about:blank")&&await f.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 Vt}from"@playwright/test";import yt from"fs";import Ut from"path";import{chromium as Ht}from"@playwright/test";async function xt({workerInfo:t,profileName:e,slowMo:o}){let a=new P,n=await T(t.workerIndex.toString()),r=x(a.name),u=Ut.resolve(r,e??"wallet-data");if(!yt.existsSync(u))throw new Error(`Cache for ${a.name} does not exist. Create it first!`);yt.cpSync(u,n,{recursive:!0,force:!0});let i=await I(a.name),m=await Ht.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:o}),l=await a.indexUrl(),h=await L(m,l);return{context:m,walletPage:h,contextPath:n}}var rn=({slowMo:t,profileName:e}={})=>Vt.extend({workerScopeContents:[async({browser:o},a,n)=>{let{context:r,contextPath:u,walletPage:i}=await xt({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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chainwright",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
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",