chainwright 0.8.15 → 0.8.16
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.
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import Z from"fs";import vt from"path";import{chromium as bt}from"@playwright/test";import yt from"path";var Y=".wallet-cache",q=".wallet-context";var J="13.22.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Kt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ut=`${_}solflare-wallet-extension-v2.19.1.zip`,Mt=`${_}petra-wallet-extension-v2.4.8.zip`,Vt=`${_}phantom-wallet-extension-v26.10.0.zip`,Ht=`${_}meteor-wallet-extension-v0.7.0.zip`,jt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function N(t){return yt.resolve(process.cwd(),q,t)}import At from"path";function w(t){return At.resolve(process.cwd(),Y,t)}import Q from"fs";import Pt from"path";async function R(t){try{let e=w(t),o=Pt.resolve(e,"extension-path.txt");if(!Q.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=Q.readFileSync(o,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function tt({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let r=await N(e.workerIndex.toString()),s=w(t.name),c=vt.resolve(s,o??"wallet-data");if(!Z.existsSync(c))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);Z.cpSync(c,r,{recursive:!0,force:!0});let p=await R(t.name),m=await bt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:a}),u=await t.indexUrl(),l=m.pages()[0];return l||(l=await m.newPage()),await l.goto(u),{context:m,walletPage:l,contextPath:r}}import{expect as nt}from"@playwright/test";function T(t){return new Promise(e=>setTimeout(e,t))}async function C(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import et from"fs";import kt from"path";async function L(t){let e=w(t),o=kt.resolve(e,"password.txt");try{if(!et.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Ct}from"@playwright/test";async function ot({context:t,path:e,locator:o}){let a;try{await Ct.poll(async()=>(a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(s=>s.url().startsWith("chrome-extension://")).map(s=>s.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await St(a,o),await a.setViewportSize({width:360,height:592}),a}async function St(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import at from"fs";import Bt from"path";async function rt(t){let e=w(t),o=Bt.resolve(e,"extension-id.txt");try{if(!at.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return at.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var h=class{name="keplr";onboardingPath="register.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/sidePanel.html`}async onboardingUrl(){return`chrome-extension://${await this.extensionId()}/${this.onboardingPath}`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async extensionId(){return await rt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ot({context:e,path:o,locator:"div[data-simplebar='init']"})}};var g={importExistingWalletButton:"button:has-text('Import an existing wallet')",usePrivateKeyButton:"button:has-text('Use recovery phrase or private key')",privateKeyTabButton:"button:has-text('Private key')",privateKeyInput:"input[type='password']",importButton:"button:has-text('Import')",walletNameInput:"input[name='name']",walletPasswordInput:"input[name='password']",confirmWalletPasswordInput:"input[name='confirmPassword']",nextButton:"button:has-text('Next')",searchNetworkInput:"input[placeholder='Search networks']",saveButton:"button:has-text('Save')",finishButton:"button:has-text('Finish')"};function Tt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function W({page:t,privateKey:e,walletName:o,chains:a,mode:r="onboard"}){let s=await L("keplr"),c=new h;if(await t.locator(g.importExistingWalletButton).click(),await t.locator(g.usePrivateKeyButton).click(),await t.getByRole("button",{name:"Private key",exact:!0}).click(),await t.locator(g.privateKeyInput).fill(e),await t.getByRole("button",{name:"Import",exact:!0}).click(),await t.locator(g.walletNameInput).fill(o),r==="onboard"){let A=t.locator(g.walletPasswordInput),F=t.locator(g.confirmWalletPasswordInput);await A.fill(s),await F.fill(s)}await t.locator(g.nextButton).click();let y=t.locator("div:has-text('All Native Chains')").nth(-4),P=t.locator("div[cursor='pointer']:has-text('Cosmos Hub')"),b=await y.locator("input[type='checkbox']").getAttribute("checked"),k=await P.locator("input[type='checkbox']").getAttribute("checked");b!==null&&await y.click(),k!==null&&await P.click();let D=t.locator(g.searchNetworkInput);for(let A of a){await D.fill(A);let B=t.locator("div[class='simplebar-content']").locator("div[cursor] > div").first().locator("div").filter({hasText:new RegExp(`^${Tt(A)}$`,"i")}).nth(2).locator("../../../../..");await B.waitFor({state:"visible",timeout:2e4}),await B.locator("input[type='checkbox']").getAttribute("checked")===null&&await B.click()}let S=t.locator(g.saveButton);if(await S.scrollIntoViewIfNeeded(),await S.click(),await T(2e3),r==="onboard"){await t.goto(await c.indexUrl());return}if(r==="add-account-single"){let A=t.locator(g.finishButton);await A.waitFor({state:"visible",timeout:2e4}),await nt(A).toBeEnabled({timeout:2e4}),await A.click()}}async function $(t){let o=await new h().onboardingUrl();await t.getByRole("link",{name:"Settings",exact:!0}).click(),await t.locator("div[cursor='pointer']").first().click(),await t.getByRole("button",{name:"Add Wallet",exact:!0}).click();let c;if(await nt.poll(async()=>(c=t.context().pages().find(p=>p.url().match(o)),!!c),{timeout:3e4}).toBe(!0).catch(p=>{console.error(`Failed to find onboarding page with URL matching ${o}. Original error: ${p}`)}),!c)throw new Error(`Onboarding page not found. Expected URL: ${o}`);return await C(c),await c.bringToFront(),c}async function it({page:t,privateKey:e,chains:o,walletName:a,mode:r}){let s=await $(t);await W({page:s,privateKey:e,walletName:a,chains:o,mode:r}),await t.locator("div:has(div:has-text('Select Wallet'))").nth(-4).locator("div:has(> div > svg)").first().click(),await t.getByRole("link",{name:"Home",exact:!0}).click()}import{expect as Wt}from"@playwright/test";var K={approveButton:"button:has-text('Approve')",rejectButton:"button[color='secondary']"};async function ct(t){let e=t.locator(K.approveButton);await Wt(e).toBeEnabled({timeout:2e4}),await e.click()}async function st(t){t.getByRole("button",{name:"Approve",exact:!0}).click(),await T(1e3)}import{expect as Et}from"@playwright/test";import f from"zod";var lt=f.discriminatedUnion("chain",[f.object({chain:f.literal(["Injective","Injective (Testnet)","Polygon"]),walletName:f.string().min(1,"Wallet name cannot be an empty string")}),f.object({chain:f.literal(["Bitcoin","Bitcoin Signet","Bitcoin Testnet"]),chainTag:f.literal(["Taproot","Native Segwit"]),walletName:f.string().min(1,"Wallet name cannot be an empty string")})]),pt=f.object({currentAccountName:f.string().min(1,"Current account name cannot be an empty string"),newAccountName:f.string().min(1,"New account name cannot be an empty string")});async function mt({page:t,...e}){let o=lt.parse({...e});await t.getByRole("textbox",{name:"Search for asset or chain (i.e. ATOM, Cosmos)",exact:!0}).fill(o.chain);let r=t.locator(`div:has-text("${o.chain}")`).nth(-2).filter({hasNot:t.locator("span")});if(await r.waitFor({state:"attached",timeout:2e4}),!await r.isVisible().catch(()=>!1))throw Error(`Make sure "${o.chain}" is activated.`);let c=await r.locator("div").all();Et(c.length).toBeGreaterThan(0),await t.locator(`div:has(div:has-text('${o.walletName}'))`).nth(-3).locator("div:has(> div > svg)").click();let u=t.locator("div:has(> div[data-simplebar='init'])").last(),d=u.locator("div:has(> div > input)").locator("input");await d.fill(o.chain);let v=await u.locator("div[cursor='pointer']",{hasText:o.chain}).all(),y;for(let b of v){let k;"chainTag"in e&&(k=e.chainTag);let D=b.locator("div",{hasText:o.chain}).last(),S=k?b.locator("div",{hasText:k}).last():null,F=(S?await S?.isVisible().catch(()=>!1):!1)?await S?.textContent():null,B=await D.textContent(),X=F?`${B} ${F}`:B,xt=k?`${o.chain} ${k}`:o.chain;if(X===xt){y=D.locator("xpath=../../../.."),await d.clear();break}}if(!y)throw Error(`Address for ${o.walletName} account on "${o.chain}" chain not found.`);return await y.hover(),await y.scrollIntoViewIfNeeded(),await y.click(),await t.evaluate(async()=>await navigator.clipboard.readText())}var O={openSidebarMenuButton:"div[cursor='pointer']:has(> div[cursor='pointer'])",menuPopupContent:"div[id='modal-root-3']",lockWalletButton:"div:has(> div:has-text('Lock Wallet'))",settingsButton:"div:has(a[href='#/settings'])"},H={unlockButton:"button[type='submit']:has-text('Unlock')",passwordInput:"input[placeholder='Type Your Password']"};async function ut(t){await t.locator(O.openSidebarMenuButton).click(),await t.locator(O.lockWalletButton).nth(-1).click(),await t.getByText("Welcome Back").waitFor({state:"visible",timeout:3e4})}import{styleText as dt}from"util";import{expect as It}from"@playwright/test";async function U(t,e){let o=t.locator("div[color]").nth(1);if(await o.textContent()===e){console.info(`
|
|
1
|
+
import Z from"fs";import vt from"path";import{chromium as bt}from"@playwright/test";import yt from"path";var Y=".wallet-cache",q=".wallet-context";var J="13.22.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Kt=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Ut=`${_}solflare-wallet-extension-v2.19.1.zip`,Mt=`${_}petra-wallet-extension-v2.4.8.zip`,Vt=`${_}phantom-wallet-extension-v26.10.0.zip`,Ht=`${_}meteor-wallet-extension-v0.7.0.zip`,jt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function N(t){return yt.resolve(process.cwd(),q,t)}import At from"path";function w(t){return At.resolve(process.cwd(),Y,t)}import Q from"fs";import Pt from"path";async function R(t){try{let e=w(t),o=Pt.resolve(e,"extension-path.txt");if(!Q.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=Q.readFileSync(o,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function tt({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let r=await N(e.workerIndex.toString()),s=w(t.name),c=vt.resolve(s,o??"wallet-data");if(!Z.existsSync(c))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);Z.cpSync(c,r,{recursive:!0,force:!0});let p=await R(t.name),m=await bt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:a}),u=await t.indexUrl(),l=m.pages()[0];return l||(l=await m.newPage()),await l.goto(u),{context:m,walletPage:l,contextPath:r}}import{expect as nt}from"@playwright/test";function T(t){return new Promise(e=>setTimeout(e,t))}async function C(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import et from"fs";import kt from"path";async function L(t){let e=w(t),o=kt.resolve(e,"password.txt");try{if(!et.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Ct}from"@playwright/test";async function ot({context:t,path:e,locator:o}){let a;try{await Ct.poll(async()=>(a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(s=>s.url().startsWith("chrome-extension://")).map(s=>s.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await St(a,o),await a.setViewportSize({width:360,height:592}),a}async function St(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import at from"fs";import Bt from"path";async function rt(t){let e=w(t),o=Bt.resolve(e,"extension-id.txt");try{if(!at.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return at.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var h=class{name="keplr";onboardingPath="register.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/sidePanel.html`}async onboardingUrl(){return`chrome-extension://${await this.extensionId()}/${this.onboardingPath}`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async extensionId(){return await rt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ot({context:e,path:o,locator:"div[data-simplebar='init']"})}};var g={importExistingWalletButton:"button:has-text('Import an existing wallet')",usePrivateKeyButton:"button:has-text('Use recovery phrase or private key')",privateKeyTabButton:"button:has-text('Private key')",privateKeyInput:"input[type='password']",importButton:"button:has-text('Import')",walletNameInput:"input[name='name']",walletPasswordInput:"input[name='password']",confirmWalletPasswordInput:"input[name='confirmPassword']",nextButton:"button:has-text('Next')",searchNetworkInput:"input[placeholder='Search networks']",saveButton:"button:has-text('Save')",finishButton:"button:has-text('Finish')"};function Tt(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async function W({page:t,privateKey:e,walletName:o,chains:a,mode:r="onboard"}){let s=await L("keplr"),c=new h;if(await t.locator(g.importExistingWalletButton).click(),await t.locator(g.usePrivateKeyButton).click(),await t.getByRole("button",{name:"Private key",exact:!0}).click(),await t.locator(g.privateKeyInput).fill(e),await t.getByRole("button",{name:"Import",exact:!0}).click(),await t.locator(g.walletNameInput).fill(o),r==="onboard"){let A=t.locator(g.walletPasswordInput),F=t.locator(g.confirmWalletPasswordInput);await A.fill(s),await F.fill(s)}await t.locator(g.nextButton).click();let 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
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:
|
|
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};
|
|
@@ -384,7 +384,7 @@ declare class Metamask extends MetamaskProfile {
|
|
|
384
384
|
* const metamask = new Metamask(page);
|
|
385
385
|
* await metamask.getAccountAddress()
|
|
386
386
|
*/
|
|
387
|
-
getAccountAddress(network: GetAccountAddressChains): Promise<string
|
|
387
|
+
getAccountAddress(network: GetAccountAddressChains): Promise<string>;
|
|
388
388
|
/**
|
|
389
389
|
* Toggles the visibility of testnet networks in the wallet's network selector.
|
|
390
390
|
* To persists the change, do it at the point of onboarding.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import dt from"fs";import Vt from"path";import{chromium as Ut}from"@playwright/test";import
|
|
2
|
-
\u26A0\uFE0F Skipping test: ${e}`),Ot.skip())}var w={buyButton:"eth-overview-buy",swapButton:"eth-overview-swap",sendButton:"eth-overview-send",receiveButton:"eth-overview-receive",openSettingsButton:"account-options-menu-button",accountMenuButton:"account-menu-icon",accountCell:"multichain-account-cell-entropy",openNetworkSelectorButton:"sort-by-networks",accountAddressesButton:"networks-subtitle-test-id",accountAddressesElements:"multichain-address-row",accountAddressQRCode:"multichain-address-row-qr-button"},_={lockButton:"global-menu-lock",networksButton:"global-menu-networks"},O={passwordInput:"unlock-password",unlockButton:"unlock-submit"},P={accountOptionsMenuButton:"multichain-account-cell-end-accessory",accountDetailsLabel:"Account details",renameAccountLabel:"Rename",addressesLabel:"Addresses",pinToTopLabel:"Pin to top",hideAccountLabel:"Hide account",backButton:"back",addMultichainAccountButton:"add-multichain-account-button",addWalletButton:"account-list-add-wallet-button",importWalletButton:"add-wallet-modal-import-wallet",importAccountButton:"add-wallet-modal-import-account"};var g={createWalletButton:"onboarding-create-wallet",importWalletButton:"onboarding-import-wallet",useSecretRecoveryPhraseButton:"onboarding-create-with-srp-button",createNewPasswordInput:"create-password-new-input",confirmNewPasswordInput:"create-password-confirm-input",confirmPasswordCheckbox:"create-password-terms",createPasswordButton:"create-password-submit",revealSecretRecoveryPhraseButton:"recovery-phrase-reveal",recoveryPhraseRemindMeLaterButton:"recovery-phrase-remind-later",metamaskMetricsIAgreeButton:"metametrics-i-agree",onboardingDoneButton:"onboarding-complete-done",importUsingSecretRecoveryPhraseButton:"onboarding-import-with-srp-button",secretRecoveryPhraseTextAreaInput:"srp-input-import__srp-note",importWalletConfirmButton:"import-srp-confirm",importAccountConfirmButton:"import-account-confirm-button",importSRPError:"bannerAlert"};async function pt({page:t,privateKey:e,accountName:o}){let n=ut.string().min(1,"Account name cannot be an empty string").trim().parse(o),r=ut.string().min(1,"Private key cannot be an empty string").trim().parse(e),a=t.getByTestId(w.accountMenuButton);await x(a).toBeVisible({timeout:3e4}),await a.click(),await x(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let i=t.getByTestId(P.addWalletButton),c=await i.textContent();c?.includes("Syncing")&&await x.poll(async()=>(await i.textContent())?.trim()??"",{timeout:12e4}).not.toBe(c),await x(i).toBeEnabled({timeout:6e4}),await i.click();let s=t.getByRole("dialog");await x(s).toContainText(/add wallet/i),await t.getByTestId(P.importAccountButton).click(),await t.locator("input[id='private-key-box']").fill(r);let u=t.getByTestId(g.importAccountConfirmButton);await x(u).toBeEnabled(),await u.click();let f=t.getByTestId(g.importSRPError),B=await f.isVisible().catch(()=>!1);B&&mt(B,`${(await f.textContent())?.split(".")[0]}`);let S=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])"),A=(await S.textContent())?.split("$")[0];A&&await $t({page:t,accountName:n,activeAccountLocator:S,activeAccountName:A}),await t.locator("button[aria-label='Back']").first().click()}async function $t({page:t,accountName:e,activeAccountLocator:o,activeAccountName:n}){let r=o.locator(`div[aria-label='${n} options']`);await x(r).toBeVisible(),await r.click(),await x(t.getByRole("tooltip")).toBeVisible();let a=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await x(a).toBeVisible(),await a.click();let i=t.getByRole("dialog"),c=i.getByRole("heading",{name:/rename/i});await x(c).toBeVisible();let s=i.getByRole("textbox");await x(s).toBeVisible(),await s.fill(e);let p=i.getByRole("button",{name:/confirm/i});await x(p).toBeEnabled(),await p.click(),await i.waitFor({state:"detached",timeout:2e4});let l=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])");await x(l).toContainText(e)}import{expect as $}from"@playwright/test";import M from"zod";var wt=M.object({networkName:M.string().min(1,"Network name cannot be an empty string"),rpcUrl:M.url(),chainId:M.number().or(M.string().includes("0x")),currencySymbol:M.string().toUpperCase().min(1,"Currency symbol cannot be an empty string")});import{expect as Ht}from"@playwright/test";async function j(t){let e=t.locator("div:has(> p[data-testid='notifications-tag-counter__unread-dot'])"),o=t.getByTestId(w.openSettingsButton);await e.isVisible().catch(()=>!1)?await e.click():(await Ht(o).toBeVisible(),await o.click())}async function ft({page:t,...e}){let{chainId:o,currencySymbol:n,networkName:r,rpcUrl:a}=wt.parse({...e},{reportInput:!0});await j(t),await t.getByTestId(_.networksButton).click();let c=t.locator("section[role='dialog']");await $(c).toContainText(/manage networks/i),await t.getByRole("button",{name:/add a custom network/i}).click(),await $(c).toContainText(/Add a custom network/i);let p=t.getByTestId("network-form-network-name"),l=t.getByTestId("test-add-rpc-drop-down"),u=t.getByTestId("network-form-chain-id"),f=t.getByTestId("network-form-ticker-input");await p.fill(r),await l.click(),await t.getByRole("tooltip").locator("div:has(> button:has-text('Add RPC URL'))").click(),await $(c).toContainText(/Add RPC URL/i);let A=t.getByTestId("rpc-url-input-test"),k=t.getByRole("button",{name:/Add URL/i});await A.fill(a),await k.click(),await $(c).toContainText(/Add a custom network/i);let y=t.getByTestId("network-form-chain-id-error");if(await y.isVisible().catch(()=>!1)){let T=await y.textContent();throw Error(`RPC error: ${T}`)}await u.fill(`${o}`),await f.fill(n);let I=t.getByRole("button",{name:/save/i});await $(I).toBeEnabled(),await I.click()}import{expect as gt}from"@playwright/test";function v(t){return new Promise(e=>setTimeout(e,t))}var q={confirmButton:"confirm-footer-button",cancelButton:"confirm-footer-cancel-button"};async function yt(t,e){let o=t.getByTestId(q.confirmButton);await v(2e3);let r=(await o.textContent())?.includes("Review alert"),a=await o.isDisabled().catch(()=>!1);if(r&&a){await t.getByTestId("edit-gas-fees-row").locator("> div").first().click();let l=t.getByRole("dialog");await gt(l).toBeVisible();let u=l.locator("h4",{hasText:"Insufficient funds"}),f=await l.getByTestId("alert-modal__selected-alert").textContent();if(await u.isVisible().catch(()=>!1))throw Error(`${f}`)}if(e){let s=t.getByTestId("edit-gas-fee-icon");if(await s.scrollIntoViewIfNeeded(),await s.click(),e.feeType!=="advanced"&&await t.getByTestId(`gas-option-${e.feeType}`).click(),e.feeType==="advanced"){await t.getByTestId("gas-option-advanced").click();let l=t.getByRole("textbox",{name:"Max base fee"}),u=t.getByRole("textbox",{name:"Priority fee"}),f=t.getByRole("button",{name:"Save",exact:!0});await l.fill(e.maxBaseFee),await u.fill(e.priorityFee),await f.click()}}await gt(o).toBeEnabled(),await o.click();let i=t.getByRole("dialog");await i.isVisible().catch(()=>!1)&&await i.locator("h4",{hasText:"Your assets may be at risk"}).isVisible().catch(()=>!1)&&(await i.getByTestId("alert-modal-acknowledge-checkbox").click(),await i.getByTestId("confirm-alert-modal-submit-button").click()),await t.waitForEvent("close",{timeout:15e3})}import{expect as Gt}from"@playwright/test";import{expect as Y}from"@playwright/test";async function W({page:t,accountName:e}){let o=t.getByTestId(w.accountMenuButton);if(await o.textContent()===e){console.info(`Can't switch account to "${e}", it is already selected.`);return}await Y(o).toBeVisible({timeout:15e3}),await o.click(),await Y(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let r=t.getByTestId(P.addMultichainAccountButton),a=await r.textContent();a?.includes("Syncing")&&await Y.poll(async()=>(await r.textContent())?.trim()??"",{timeout:6e4}).not.toBe(a);let i=await t.getByTestId(/^multichain-account-cell-(?:entropy|keyring):/).all(),c=null;for(let s of i)if(await s.scrollIntoViewIfNeeded(),(await s.textContent())?.includes(e)){c=s;break}if(!c)throw Error(`Account with name "${e}" not found.`);await c?.click()}async function ht(t,e){e&&await W({page:t,accountName:e});let o=t.getByRole("button",{name:"Connect",exact:!0});await o.waitFor({state:"visible",timeout:25e3}),await o.click(),await t.getByRole("heading",{name:"Connecting",exact:!0}).waitFor({state:"detached",timeout:3e4});let r;await Gt.poll(async()=>(r=await t.locator("div[class='permissions-connect']").isVisible().catch(()=>!1),r),{timeout:25e3}).toBe(!0).catch(()=>console.error("Notice dialog did not appear within the timeout period."));let a=t.getByTestId("page-container-footer-next");await a.waitFor({state:"visible",timeout:25e3}),await a.click(),await t.waitForEvent("close",{predicate:()=>!0,timeout:25e3}).catch(()=>console.error("Extension popup did not close within the timeout period when connecting to the DApp."))}async function xt(t,e){return await t.getByTestId(w.accountAddressesButton).hover(),await t.getByTestId("multichain-address-rows-list").getByRole("button",{name:/view all/i}).click(),await t.getByRole("searchbox",{name:/search networks/i}).fill(e),await t.locator(`div[data-testid='${w.accountAddressesElements}']:has-text('${e}')`).getByTestId(w.accountAddressQRCode).click(),await t.getByRole("dialog").locator("div > p[data-testid='account-address']").textContent()}import{expect as qt}from"@playwright/test";import{errors as zt}from"@playwright/test";async function E(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}var Bt={loadingOverlay:"loading-overlay",loadingSpinner:"spinner loading-overlay__spinner"};var Kt=6e4,jt=async(t,e,o)=>{await E(e);try{await e.locator(`div[class="${t}"]`).waitFor({state:"detached",timeout:o})}catch(n){if(n instanceof zt.TimeoutError)console.info(`Loading indicator '${t}' not found - continuing.`);else throw console.error(`Error while waiting for loading indicator '${t}' to disappear`),n}},X=async t=>{try{await jt(Bt.loadingSpinner,t,Kt)}catch(e){console.warn("Warning during MetaMask load:",e)}return await v(300),t};async function kt(t){if(await t.getByTestId(O.unlockButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already locked");return}await j(t);let n=t.getByTestId(_.lockButton);await qt(n).toBeVisible(),await n.click(),await X(t)}import{styleText as Pt}from"util";import{expect as D}from"@playwright/test";import bt from"fs";import Xt from"path";async function Q(t){let e=h(t),o=Xt.resolve(e,"password.txt");try{if(!bt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return bt.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} password from cache: ${n.message}`)}}import{expect as Qt}from"@playwright/test";async function At({context:t,path:e,locator:o}){let n;try{await Qt.poll(async()=>(n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!n),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).map(a=>a.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!n)throw new Error(`Popup page with path ${e} not found in context.`);return await Jt(n,o),await n.setViewportSize({width:360,height:592}),n}async function Jt(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import Ct from"fs";import Yt from"path";async function Tt(t){let e=h(t),o=Yt.resolve(e,"extension-id.txt");try{if(!Ct.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return Ct.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${n.message}`)}}var C=class{name="metamask";onboardingPath="/home.html#onboarding";async indexUrl(){return`chrome-extension://${await this.extensionId()}/home.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await Tt(this.name)}async promptPage(e){let o=await this.promptUrl();return await At({context:e,path:o,locator:"div[data-testid='multichain-page']"})}};import{expect as Z}from"@playwright/test";async function L({page:t}){await t.locator(`div:has(> button[data-testid='${w.openSettingsButton}'])`).click(),await t.getByTestId(_.networksButton).click();let n=t.locator("section[role='dialog']");await Z(n).toBeVisible(),await Z(n).toContainText(/manage networks/i);let r="div:has(> p:has-text('Show test networks'))";await n.locator(r).scrollIntoViewIfNeeded();let a=n.locator(r);if(!await a.locator("label[class='toggle-button toggle-button--off']").isVisible().catch(()=>!1)){await n.getByRole("button",{name:/close/i}).click(),console.info("Testnet networks are already visible.");return}await a.locator("label[class='toggle-button toggle-button--off']").click(),await t.getByTestId("Sepolia").scrollIntoViewIfNeeded(),await Z(t.getByTestId("Sepolia")).toBeVisible(),await n.getByRole("button",{name:/close/i}).click()}async function tt({page:t,mainAccountName:e,...o}){console.info(Pt("yellowBright",`
|
|
3
|
-
\u{1F98A} MetaMask onboarding started...`,{validateStream:!1}));let n=await Q("metamask"),r=new C,a=t.locator("img[class='loading-spinner']"),i=t.getByTestId(g.createWalletButton),c=t.getByTestId(g.importWalletButton),s=t.getByTestId(g.createNewPasswordInput),p=t.getByTestId(g.confirmNewPasswordInput),l=t.getByTestId(g.confirmPasswordCheckbox),u=t.getByTestId(g.createPasswordButton),f=t.getByTestId(g.metamaskMetricsIAgreeButton),B=t.getByTestId(g.onboardingDoneButton);if(await a.waitFor({state:"detached",timeout:3e4}),o.mode==="create"){let I=t.getByTestId(g.useSecretRecoveryPhraseButton);await i.click(),await I.click(),await s.fill(n),await p.fill(n),await l.click(),await u.click(),await t.getByTestId(g.revealSecretRecoveryPhraseButton).click(),await t.getByTestId(g.recoveryPhraseRemindMeLaterButton).click(),await f.click()}if(o.mode==="import"){let{secretRecoveryPhrase:I}=o,T=I.split(" "),z=t.getByTestId(g.importUsingSecretRecoveryPhraseButton);await c.click(),await z.click();let at=t.getByTestId(g.secretRecoveryPhraseTextAreaInput);await at.fill(T[0]),await at.press("Space");for(let K=1;K<T.length;K++){let rt=t.getByTestId(`import-srp__srp-word-${K}`);await rt.fill(T[K]),await rt.press("Space")}await t.getByTestId(g.importWalletConfirmButton).click(),await s.fill(n),await p.fill(n),await l.click(),await u.click(),await f.click();let Mt=t.getByTestId("wallet-ready");await D(Mt).toContainText(/your wallet is ready/i)}await B.click();let A=`chrome-extension://${await r.extensionId()}/sidepanel.html`,y=await t.context().browser()?.newBrowserCDPSession(),N;await D.poll(async()=>{if(y){let{targetInfos:I}=await y.send("Target.getTargets"),T=I.find(z=>z.url===A);return N=T,!!T}},{timeout:15e3}).toBe(!0),N&&await y?.send("Target.closeTarget",{targetId:N.targetId}),await t.goto(await r.indexUrl()),await a.waitFor({state:"detached",timeout:3e4}),await D(t.getByTestId(w.buyButton)).toBeVisible(),await D(t.getByTestId(w.swapButton)).toBeVisible(),await D(t.getByTestId(w.sendButton)).toBeVisible(),await D(t.getByTestId(w.receiveButton)).toBeVisible(),await L({page:t}),e&&await W({page:t,accountName:e}),await v(5e3),console.info(Pt("greenBright","\u2728 MetaMask onboarding completed successfully",{validateStream:!1}))}import{expect as Zt}from"@playwright/test";async function St(t){let e=t.getByTestId(q.cancelButton);await v(1e3),await Zt(e).toBeEnabled(),await e.click()}import{expect as b}from"@playwright/test";async function It({page:t,currentAccountName:e,newAccountName:o}){let n=t.getByTestId(w.accountMenuButton);if(await n.textContent()===o)throw Error(`The account to be renamed "${o}" already exists.`);await b(n).toBeVisible({timeout:15e3}),await n.click(),await b(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let a=t.getByTestId(P.addMultichainAccountButton),i=await a.textContent();i?.includes("Syncing")&&await b.poll(async()=>(await a.textContent())?.trim()??"",{timeout:6e4}).not.toBe(i);let c=await t.getByTestId(/^multichain-account-cell/).all(),s=null;for(let y of c)if((await y.textContent())?.includes(e)){s=y;break}if(!s)throw Error(`Account with name "${e}" not found.`);if((await s.textContent())?.split("$")[0]===o)throw Error(`The new account name "${o}" is the same as the old account name "${e}".`);let l=t.locator(`div[aria-label='${e} options']`);await b(l).toBeVisible(),await l.click(),await b(t.getByRole("tooltip")).toBeVisible();let u=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await b(u).toBeVisible(),await u.click();let f=t.getByRole("dialog"),B=f.getByRole("heading",{name:/rename/i});await b(B).toBeVisible();let S=f.getByRole("textbox");await b(S).toBeVisible(),await S.fill(o);let A=f.getByRole("button",{name:/confirm/i});await b(A).toBeEnabled(),await A.click();for(let y of c)if((await y.textContent())?.includes(o)){await b(y).toBeVisible(),await b(y).toContainText(o);break}await t.locator("button[aria-label='Back']").first().click()}import{expect as et}from"@playwright/test";async function vt({page:t,networkType:e,chainName:o}){let n=t.getByTestId(w.openNetworkSelectorButton);await n.click();let r=t.getByTestId("modal-header-close-button");if(e==="testnet"||e==="custom"){let s=t.getByRole("tab",{name:"Custom"});await s.click(),await t.locator("p:has-text('Testnets')").isVisible().catch(()=>!1)||(await r.click(),await L({page:t}),await n.click(),await s.click());let u=t.locator(`div div[data-testid='${o}']`),f=await u.textContent();et(f).toBe(o),await u.click();return}await t.getByRole("tab",{name:"Popular"}).click();let i=t.locator(`div div[data-testid='${o}']`),c=await i.textContent();et(c).toBe(o),await i.click(),await et(n).toContainText(o,{timeout:3e4})}import{expect as Rt}from"@playwright/test";async function H(t){let e=await Q("metamask"),o=t.getByTestId(O.passwordInput);if(await t.getByTestId(w.openNetworkSelectorButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await o.fill(e);let a=t.getByTestId(O.unlockButton);await Rt(a).toBeVisible(),await a.click(),await X(t),await Rt(t.getByTestId(w.buyButton)).toBeVisible({timeout:3e4})}var V=class extends C{page;constructor(e){super(),this.page=e}async onboard(e){await tt({page:this.page,...e})}async unlock(){await H(this.page)}async lock(){await kt(this.page)}async renameAccount({newAccountName:e,currentAccountName:o}){await It({page:this.page,newAccountName:e,currentAccountName:o})}async addAccount({privateKey:e,accountName:o}){await pt({page:this.page,privateKey:e,accountName:o})}async switchAccount({accountName:e}){await W({page:this.page,accountName:e})}async switchNetwork({...e}){await vt({page:this.page,...e})}async getAccountAddress(e){return await xt(this.page,e)}async toggleShowTestnetNetwork(){await L({page:this.page})}async addCustomNetwork({chainId:e,currencySymbol:o,networkName:n,rpcUrl:r}){await ft({page:this.page,chainId:e,currencySymbol:o,networkName:n,rpcUrl:r})}async connectToApp(e){await ht(await this.promptPage(this.page.context()),e)}async confirmTransaction(e){await yt(await this.promptPage(this.page.context()),e)}async rejectTransaction(){await St(await this.promptPage(this.page.context()))}};import Nt from"fs";import ne from"path";import{test as ae,chromium as re}from"@playwright/test";import{Instance as ie,Pool as se}from"prool";import{expect as te}from"@playwright/test";async function ot(t,e){let o=await t.newPage();return await te(async()=>{await o.goto(e),await E(o)}).toPass(),o}async function nt(t,e){let o=await e.newPage();for(let{origin:n,localStorage:r}of t){let a=o.mainFrame();await a.goto(n),await a.evaluate(i=>{i.forEach(({name:c,value:s})=>{window.localStorage.setItem(c,s)})},r)}await o.close()}import ee from"fs/promises";async function Et(t){await ee.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var oe=35e3;async function J(t,e){try{await Promise.race([t.close(),new Promise((o,n)=>setTimeout(()=>n(new Error("Context close timed out")),oe))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await Et(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var G,Ua=({slowMo:t=0,profileName:e}={})=>ae.extend({contextPath:async({browserName:o},n,r)=>{let a=await R(`${o}-${r.testId}`);await n(a)},context:async({context:o,contextPath:n},r)=>{let a=new C,i=h(a.name),c=await F(a.name),s=ne.resolve(i,e??"wallet-data");if(!Nt.existsSync(s))throw new Error("\u274C Cache for MetaMask wallet data not found. Create it first");Nt.cpSync(s,n,{recursive:!0,force:!0});let p=[`--disable-extensions-except=${c}`,`--load-extension=${c}`];process.env.HEADLESS&&(p.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await re.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:t}),{cookies:u,origins:f}=await o.storageState();u&&await l.addCookies(u),f&&f.length>0&&nt(f,l);let B=await a.indexUrl();await l.waitForEvent("page",{predicate:k=>k.url().startsWith(B),timeout:3e4}),G=l.pages().find(k=>k.url().startsWith(B))??await ot(l,B);for(let k of l.pages())k.url().includes("about:blank")&&await k.close();await G.locator("img[class='loading-spinner']").waitFor({state:"detached"}),await H(G),await r(l),await J(l,n)},metamaskPage:async({context:o},n)=>{await n(G)},metamask:async({context:o},n)=>{let r=new V(G);await n(r)},createAnvilNode:async({context:o},n,r)=>{let a=r.workerIndex,i;await n(async c=>{i=se.define({instance:ie.anvil(c)});let s=await i.start(a),p=`http://${s.host}:${s.port}`,u=c?.chainId??31337;return{rpcUrl:p,anvil:s,chainId:u}}),i&&await i.stop(a)},connectToAnvil:async({context:o,metamask:n,createAnvilNode:r},a)=>{await a(async()=>{let{chainId:i,rpcUrl:c}=await r({chainId:2251});await n.addCustomNetwork({chainId:i,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:c})})}});import{test as de}from"@playwright/test";import{Instance as me,Pool as ue}from"prool";import Ft from"fs";import ce from"path";import{chromium as le}from"@playwright/test";async function _t({workerInfo:t,profileName:e,slowMo:o}){let n=new C,r=await R(t.workerIndex.toString()),a=h(n.name),i=ce.resolve(a,e??"wallet-data");if(!Ft.existsSync(i))throw new Error(`Cache for ${n.name} does not exist. Create it first!`);Ft.cpSync(i,r,{recursive:!0,force:!0});let c=await F(n.name),s=await le.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:o}),p=await n.indexUrl();await s.waitForEvent("page",{predicate:u=>u.url().startsWith(p),timeout:4e4});let l=s.pages().find(u=>u.url().startsWith(p));l||(l=await s.newPage(),await l.goto(p),await E(l));for(let u of s.pages())u.url().includes("about:blank")&&await u.close();return{context:s,walletPage:l,contextPath:r}}var ar=({profileName:t,slowMo:e}={})=>de.extend({workerScopeContents:[async({browser:o},n,r)=>{let{context:a,contextPath:i,walletPage:c}=await _t({workerInfo:r,profileName:t,slowMo:e});await a.grantPermissions(["clipboard-read"]);let s=new V(c);await s.unlock(),await n({wallet:s,walletPage:c,context:a}),await J(a,i)},{scope:"worker"}],createAnvilNode:async({context:o},n,r)=>{let a=r.workerIndex,i;await n(async c=>{i=ue.define({instance:me.anvil(c)});let s=await i.start(a),p=`http://${s.host}:${s.port}`,u=c?.chainId??31337;return{rpcUrl:p,anvil:s,chainId:u}}),i&&await i.stop(a)},connectToAnvil:async({context:o,createAnvilNode:n,workerScopeContents:r},a)=>{await a(async()=>{let{wallet:i}=r,{chainId:c,rpcUrl:s}=await n({chainId:2251});await i.addCustomNetwork({chainId:c,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:s})})}});export{V as Metamask,Ua as metamaskFixture,ar as metamaskWorkerScopeFixture,Oe as workerScopeContext};
|
|
1
|
+
import dt from"fs";import Vt from"path";import{chromium as Ut}from"@playwright/test";import Lt from"path";var st=".wallet-cache",ct=".wallet-context";var it="13.22.0",U="https://github.com/amaify/chainwright/releases/download/v0.1.0/",we=`https://github.com/MetaMask/metamask-extension/releases/download/v${it}/metamask-chrome-${it}.zip`,fe=`${U}solflare-wallet-extension-v2.19.1.zip`,ge=`${U}petra-wallet-extension-v2.4.8.zip`,ye=`${U}phantom-wallet-extension-v26.10.0.zip`,he=`${U}meteor-wallet-extension-v0.7.0.zip`,xe=`${U}keplr-wallet-extension-v0.13.3.zip`;async function R(t){return Lt.resolve(process.cwd(),ct,t)}import Wt from"path";function h(t){return Wt.resolve(process.cwd(),st,t)}import lt from"fs";import Dt from"path";async function F(t){try{let e=h(t),o=Dt.resolve(e,"extension-path.txt");if(!lt.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let n=lt.readFileSync(o,"utf-8").trim();if(!n)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return n}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function Oe({wallet:t,workerInfo:e,profileName:o,slowMo:n}){let r=await R(e.workerIndex.toString()),a=h(t.name),i=Vt.resolve(a,o??"wallet-data");if(!dt.existsSync(i))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);dt.cpSync(i,r,{recursive:!0,force:!0});let c=await F(t.name),s=await Ut.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:n}),p=await t.indexUrl(),l=s.pages()[0];return l||(l=await s.newPage()),await l.goto(p),{context:s,walletPage:l,contextPath:r}}import{expect as x}from"@playwright/test";import ut from"zod";import{test as Ot}from"@playwright/test";function mt(t,e){t&&(console.warn(`
|
|
2
|
+
\u26A0\uFE0F Skipping test: ${e}`),Ot.skip())}var w={buyButton:"eth-overview-buy",swapButton:"eth-overview-swap",sendButton:"eth-overview-send",receiveButton:"eth-overview-receive",openSettingsButton:"account-options-menu-button",accountMenuButton:"account-menu-icon",accountCell:"multichain-account-cell-entropy",openNetworkSelectorButton:"sort-by-networks",accountAddressesButton:"networks-subtitle-test-id",accountAddressesElements:"multichain-address-row",accountAddressQRCode:"multichain-address-row-qr-button"},_={lockButton:"global-menu-lock",networksButton:"global-menu-networks"},O={passwordInput:"unlock-password",unlockButton:"unlock-submit"},P={accountOptionsMenuButton:"multichain-account-cell-end-accessory",accountDetailsLabel:"Account details",renameAccountLabel:"Rename",addressesLabel:"Addresses",pinToTopLabel:"Pin to top",hideAccountLabel:"Hide account",backButton:"back",addMultichainAccountButton:"add-multichain-account-button",addWalletButton:"account-list-add-wallet-button",importWalletButton:"add-wallet-modal-import-wallet",importAccountButton:"add-wallet-modal-import-account"};var g={createWalletButton:"onboarding-create-wallet",importWalletButton:"onboarding-import-wallet",useSecretRecoveryPhraseButton:"onboarding-create-with-srp-button",createNewPasswordInput:"create-password-new-input",confirmNewPasswordInput:"create-password-confirm-input",confirmPasswordCheckbox:"create-password-terms",createPasswordButton:"create-password-submit",revealSecretRecoveryPhraseButton:"recovery-phrase-reveal",recoveryPhraseRemindMeLaterButton:"recovery-phrase-remind-later",metamaskMetricsIAgreeButton:"metametrics-i-agree",onboardingDoneButton:"onboarding-complete-done",importUsingSecretRecoveryPhraseButton:"onboarding-import-with-srp-button",secretRecoveryPhraseTextAreaInput:"srp-input-import__srp-note",importWalletConfirmButton:"import-srp-confirm",importAccountConfirmButton:"import-account-confirm-button",importSRPError:"bannerAlert"};async function pt({page:t,privateKey:e,accountName:o}){let n=ut.string().min(1,"Account name cannot be an empty string").trim().parse(o),r=ut.string().min(1,"Private key cannot be an empty string").trim().parse(e),a=t.getByTestId(w.accountMenuButton);await x(a).toBeVisible({timeout:3e4}),await a.click(),await x(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let i=t.getByTestId(P.addWalletButton),c=await i.textContent();c?.includes("Syncing")&&await x.poll(async()=>(await i.textContent())?.trim()??"",{timeout:12e4}).not.toBe(c),await x(i).toBeEnabled({timeout:6e4}),await i.click();let s=t.getByRole("dialog");await x(s).toContainText(/add wallet/i),await t.getByTestId(P.importAccountButton).click(),await t.locator("input[id='private-key-box']").fill(r);let u=t.getByTestId(g.importAccountConfirmButton);await x(u).toBeEnabled(),await u.click();let f=t.getByTestId(g.importSRPError),B=await f.isVisible().catch(()=>!1);B&&mt(B,`${(await f.textContent())?.split(".")[0]}`);let S=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])"),A=(await S.textContent())?.split("$")[0];A&&await $t({page:t,accountName:n,activeAccountLocator:S,activeAccountName:A}),await t.locator("button[aria-label='Back']").first().click()}async function $t({page:t,accountName:e,activeAccountLocator:o,activeAccountName:n}){let r=o.locator(`div[aria-label='${n} options']`);await x(r).toBeVisible(),await r.click(),await x(t.getByRole("tooltip")).toBeVisible();let a=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await x(a).toBeVisible(),await a.click();let i=t.getByRole("dialog"),c=i.getByRole("heading",{name:/rename/i});await x(c).toBeVisible();let s=i.getByRole("textbox");await x(s).toBeVisible(),await s.fill(e);let p=i.getByRole("button",{name:/confirm/i});await x(p).toBeEnabled(),await p.click(),await i.waitFor({state:"detached",timeout:2e4});let l=t.locator("div:has(> div[data-testid^='multichain-account-cell-keyring'][class*='mm-box--background-color-background-muted'])");await x(l).toContainText(e)}import{expect as $}from"@playwright/test";import M from"zod";var wt=M.object({networkName:M.string().min(1,"Network name cannot be an empty string"),rpcUrl:M.url(),chainId:M.number().or(M.string().includes("0x")),currencySymbol:M.string().toUpperCase().min(1,"Currency symbol cannot be an empty string")});import{expect as Ht}from"@playwright/test";async function j(t){let e=t.locator("div:has(> p[data-testid='notifications-tag-counter__unread-dot'])"),o=t.getByTestId(w.openSettingsButton);await e.isVisible().catch(()=>!1)?await e.click():(await Ht(o).toBeVisible(),await o.click())}async function ft({page:t,...e}){let{chainId:o,currencySymbol:n,networkName:r,rpcUrl:a}=wt.parse({...e},{reportInput:!0});await j(t),await t.getByTestId(_.networksButton).click();let c=t.locator("section[role='dialog']");await $(c).toContainText(/manage networks/i),await t.getByRole("button",{name:/add a custom network/i}).click(),await $(c).toContainText(/Add a custom network/i);let p=t.getByTestId("network-form-network-name"),l=t.getByTestId("test-add-rpc-drop-down"),u=t.getByTestId("network-form-chain-id"),f=t.getByTestId("network-form-ticker-input");await p.fill(r),await l.click(),await t.getByRole("tooltip").locator("div:has(> button:has-text('Add RPC URL'))").click(),await $(c).toContainText(/Add RPC URL/i);let A=t.getByTestId("rpc-url-input-test"),k=t.getByRole("button",{name:/Add URL/i});await A.fill(a),await k.click(),await $(c).toContainText(/Add a custom network/i);let y=t.getByTestId("network-form-chain-id-error");if(await y.isVisible().catch(()=>!1)){let T=await y.textContent();throw Error(`RPC error: ${T}`)}await u.fill(`${o}`),await f.fill(n);let I=t.getByRole("button",{name:/save/i});await $(I).toBeEnabled(),await I.click()}import{expect as gt}from"@playwright/test";function v(t){return new Promise(e=>setTimeout(e,t))}var q={confirmButton:"confirm-footer-button",cancelButton:"confirm-footer-cancel-button"};async function yt(t,e){let o=t.getByTestId(q.confirmButton);await v(2e3);let r=(await o.textContent())?.includes("Review alert"),a=await o.isDisabled().catch(()=>!1);if(r&&a){await t.getByTestId("edit-gas-fees-row").locator("> div").first().click();let l=t.getByRole("dialog");await gt(l).toBeVisible();let u=l.locator("h4",{hasText:"Insufficient funds"}),f=await l.getByTestId("alert-modal__selected-alert").textContent();if(await u.isVisible().catch(()=>!1))throw Error(`${f}`)}if(e){let s=t.getByTestId("edit-gas-fee-icon");if(await s.scrollIntoViewIfNeeded(),await s.click(),e.feeType!=="advanced"&&await t.getByTestId(`gas-option-${e.feeType}`).click(),e.feeType==="advanced"){await t.getByTestId("gas-option-advanced").click();let l=t.getByRole("textbox",{name:"Max base fee"}),u=t.getByRole("textbox",{name:"Priority fee"}),f=t.getByRole("button",{name:"Save",exact:!0});await l.fill(e.maxBaseFee),await u.fill(e.priorityFee),await f.click()}}await gt(o).toBeEnabled(),await o.click();let i=t.getByRole("dialog");await i.isVisible().catch(()=>!1)&&await i.locator("h4",{hasText:"Your assets may be at risk"}).isVisible().catch(()=>!1)&&(await i.getByTestId("alert-modal-acknowledge-checkbox").click(),await i.getByTestId("confirm-alert-modal-submit-button").click()),await t.waitForEvent("close",{timeout:15e3})}import{expect as Gt}from"@playwright/test";import{expect as Y}from"@playwright/test";async function L({page:t,accountName:e}){let o=t.getByTestId(w.accountMenuButton);if(await o.textContent()===e){console.info(`Can't switch account to "${e}", it is already selected.`);return}await Y(o).toBeVisible({timeout:15e3}),await o.click(),await Y(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let r=t.getByTestId(P.addMultichainAccountButton),a=await r.textContent();a?.includes("Syncing")&&await Y.poll(async()=>(await r.textContent())?.trim()??"",{timeout:6e4}).not.toBe(a);let i=await t.getByTestId(/^multichain-account-cell-(?:entropy|keyring):/).all(),c=null;for(let s of i)if(await s.scrollIntoViewIfNeeded(),(await s.textContent())?.includes(e)){c=s;break}if(!c)throw Error(`Account with name "${e}" not found.`);await c?.click()}async function ht(t,e){e&&await L({page:t,accountName:e});let o=t.getByRole("button",{name:"Connect",exact:!0});await o.waitFor({state:"visible",timeout:25e3}),await o.click(),await t.getByRole("heading",{name:"Connecting",exact:!0}).waitFor({state:"detached",timeout:3e4});let r;await Gt.poll(async()=>(r=await t.locator("div[class='permissions-connect']").isVisible().catch(()=>!1),r),{timeout:25e3}).toBe(!0).catch(()=>console.error("Notice dialog did not appear within the timeout period."));let a=t.getByTestId("page-container-footer-next");await a.waitFor({state:"visible",timeout:25e3}),await a.click(),await t.waitForEvent("close",{predicate:()=>!0,timeout:25e3}).catch(()=>console.error("Extension popup did not close within the timeout period when connecting to the DApp."))}async function xt(t,e){await t.getByTestId(w.accountAddressesButton).hover(),await t.getByTestId("multichain-address-rows-list").getByRole("button",{name:/view all/i}).click(),await t.getByRole("searchbox",{name:/search networks/i}).fill(e),await t.locator(`div[data-testid='${w.accountAddressesElements}']:has-text('${e}')`).getByTestId(w.accountAddressQRCode).click();let l=await t.getByRole("dialog").locator("div > p[data-testid='account-address']").textContent();if(!l)throw new Error("Account address not found");return await t.getByLabel("Close",{exact:!0}).click(),await t.getByTestId("multichain-account-address-list-page-back-button").click(),l}import{expect as qt}from"@playwright/test";import{errors as zt}from"@playwright/test";async function E(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}var Bt={loadingOverlay:"loading-overlay",loadingSpinner:"spinner loading-overlay__spinner"};var Kt=6e4,jt=async(t,e,o)=>{await E(e);try{await e.locator(`div[class="${t}"]`).waitFor({state:"detached",timeout:o})}catch(n){if(n instanceof zt.TimeoutError)console.info(`Loading indicator '${t}' not found - continuing.`);else throw console.error(`Error while waiting for loading indicator '${t}' to disappear`),n}},X=async t=>{try{await jt(Bt.loadingSpinner,t,Kt)}catch(e){console.warn("Warning during MetaMask load:",e)}return await v(300),t};async function kt(t){if(await t.getByTestId(O.unlockButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already locked");return}await j(t);let n=t.getByTestId(_.lockButton);await qt(n).toBeVisible(),await n.click(),await X(t)}import{styleText as Pt}from"util";import{expect as D}from"@playwright/test";import bt from"fs";import Xt from"path";async function Q(t){let e=h(t),o=Xt.resolve(e,"password.txt");try{if(!bt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return bt.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} password from cache: ${n.message}`)}}import{expect as Qt}from"@playwright/test";async function At({context:t,path:e,locator:o}){let n;try{await Qt.poll(async()=>(n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(e)),!!n),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).map(a=>a.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!n)throw new Error(`Popup page with path ${e} not found in context.`);return await Jt(n,o),await n.setViewportSize({width:360,height:592}),n}async function Jt(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import Ct from"fs";import Yt from"path";async function Tt(t){let e=h(t),o=Yt.resolve(e,"extension-id.txt");try{if(!Ct.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return Ct.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${n.message}`)}}var C=class{name="metamask";onboardingPath="/home.html#onboarding";async indexUrl(){return`chrome-extension://${await this.extensionId()}/home.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await Tt(this.name)}async promptPage(e){let o=await this.promptUrl();return await At({context:e,path:o,locator:"div[data-testid='multichain-page']"})}};import{expect as Z}from"@playwright/test";async function W({page:t}){await t.locator(`div:has(> button[data-testid='${w.openSettingsButton}'])`).click(),await t.getByTestId(_.networksButton).click();let n=t.locator("section[role='dialog']");await Z(n).toBeVisible(),await Z(n).toContainText(/manage networks/i);let r="div:has(> p:has-text('Show test networks'))";await n.locator(r).scrollIntoViewIfNeeded();let a=n.locator(r);if(!await a.locator("label[class='toggle-button toggle-button--off']").isVisible().catch(()=>!1)){await n.getByRole("button",{name:/close/i}).click(),console.info("Testnet networks are already visible.");return}await a.locator("label[class='toggle-button toggle-button--off']").click(),await t.getByTestId("Sepolia").scrollIntoViewIfNeeded(),await Z(t.getByTestId("Sepolia")).toBeVisible(),await n.getByRole("button",{name:/close/i}).click()}async function tt({page:t,mainAccountName:e,...o}){console.info(Pt("yellowBright",`
|
|
3
|
+
\u{1F98A} MetaMask onboarding started...`,{validateStream:!1}));let n=await Q("metamask"),r=new C,a=t.locator("img[class='loading-spinner']"),i=t.getByTestId(g.createWalletButton),c=t.getByTestId(g.importWalletButton),s=t.getByTestId(g.createNewPasswordInput),p=t.getByTestId(g.confirmNewPasswordInput),l=t.getByTestId(g.confirmPasswordCheckbox),u=t.getByTestId(g.createPasswordButton),f=t.getByTestId(g.metamaskMetricsIAgreeButton),B=t.getByTestId(g.onboardingDoneButton);if(await a.waitFor({state:"detached",timeout:3e4}),o.mode==="create"){let I=t.getByTestId(g.useSecretRecoveryPhraseButton);await i.click(),await I.click(),await s.fill(n),await p.fill(n),await l.click(),await u.click(),await t.getByTestId(g.revealSecretRecoveryPhraseButton).click(),await t.getByTestId(g.recoveryPhraseRemindMeLaterButton).click(),await f.click()}if(o.mode==="import"){let{secretRecoveryPhrase:I}=o,T=I.split(" "),z=t.getByTestId(g.importUsingSecretRecoveryPhraseButton);await c.click(),await z.click();let at=t.getByTestId(g.secretRecoveryPhraseTextAreaInput);await at.fill(T[0]),await at.press("Space");for(let K=1;K<T.length;K++){let rt=t.getByTestId(`import-srp__srp-word-${K}`);await rt.fill(T[K]),await rt.press("Space")}await t.getByTestId(g.importWalletConfirmButton).click(),await s.fill(n),await p.fill(n),await l.click(),await u.click(),await f.click();let Mt=t.getByTestId("wallet-ready");await D(Mt).toContainText(/your wallet is ready/i)}await B.click();let A=`chrome-extension://${await r.extensionId()}/sidepanel.html`,y=await t.context().browser()?.newBrowserCDPSession(),N;await D.poll(async()=>{if(y){let{targetInfos:I}=await y.send("Target.getTargets"),T=I.find(z=>z.url===A);return N=T,!!T}},{timeout:15e3}).toBe(!0),N&&await y?.send("Target.closeTarget",{targetId:N.targetId}),await t.goto(await r.indexUrl()),await a.waitFor({state:"detached",timeout:3e4}),await D(t.getByTestId(w.buyButton)).toBeVisible(),await D(t.getByTestId(w.swapButton)).toBeVisible(),await D(t.getByTestId(w.sendButton)).toBeVisible(),await D(t.getByTestId(w.receiveButton)).toBeVisible(),await W({page:t}),e&&await L({page:t,accountName:e}),await v(5e3),console.info(Pt("greenBright","\u2728 MetaMask onboarding completed successfully",{validateStream:!1}))}import{expect as Zt}from"@playwright/test";async function St(t){let e=t.getByTestId(q.cancelButton);await v(1e3),await Zt(e).toBeEnabled(),await e.click()}import{expect as b}from"@playwright/test";async function It({page:t,currentAccountName:e,newAccountName:o}){let n=t.getByTestId(w.accountMenuButton);if(await n.textContent()===o)throw Error(`The account to be renamed "${o}" already exists.`);await b(n).toBeVisible({timeout:15e3}),await n.click(),await b(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let a=t.getByTestId(P.addMultichainAccountButton),i=await a.textContent();i?.includes("Syncing")&&await b.poll(async()=>(await a.textContent())?.trim()??"",{timeout:6e4}).not.toBe(i);let c=await t.getByTestId(/^multichain-account-cell/).all(),s=null;for(let y of c)if((await y.textContent())?.includes(e)){s=y;break}if(!s)throw Error(`Account with name "${e}" not found.`);if((await s.textContent())?.split("$")[0]===o)throw Error(`The new account name "${o}" is the same as the old account name "${e}".`);let l=t.locator(`div[aria-label='${e} options']`);await b(l).toBeVisible(),await l.click(),await b(t.getByRole("tooltip")).toBeVisible();let u=t.locator(`div[aria-label='${P.renameAccountLabel}']`);await b(u).toBeVisible(),await u.click();let f=t.getByRole("dialog"),B=f.getByRole("heading",{name:/rename/i});await b(B).toBeVisible();let S=f.getByRole("textbox");await b(S).toBeVisible(),await S.fill(o);let A=f.getByRole("button",{name:/confirm/i});await b(A).toBeEnabled(),await A.click();for(let y of c)if((await y.textContent())?.includes(o)){await b(y).toBeVisible(),await b(y).toContainText(o);break}await t.locator("button[aria-label='Back']").first().click()}import{expect as et}from"@playwright/test";async function vt({page:t,networkType:e,chainName:o}){let n=t.getByTestId(w.openNetworkSelectorButton);await n.click();let r=t.getByTestId("modal-header-close-button");if(e==="testnet"||e==="custom"){let s=t.getByRole("tab",{name:"Custom"});await s.click(),await t.locator("p:has-text('Testnets')").isVisible().catch(()=>!1)||(await r.click(),await W({page:t}),await n.click(),await s.click());let u=t.locator(`div div[data-testid='${o}']`),f=await u.textContent();et(f).toBe(o),await u.click();return}await t.getByRole("tab",{name:"Popular"}).click();let i=t.locator(`div div[data-testid='${o}']`),c=await i.textContent();et(c).toBe(o),await i.click(),await et(n).toContainText(o,{timeout:3e4})}import{expect as Rt}from"@playwright/test";async function H(t){let e=await Q("metamask"),o=t.getByTestId(O.passwordInput);if(await t.getByTestId(w.openNetworkSelectorButton).isVisible().catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await o.fill(e);let a=t.getByTestId(O.unlockButton);await Rt(a).toBeVisible(),await a.click(),await X(t),await Rt(t.getByTestId(w.buyButton)).toBeVisible({timeout:3e4})}var V=class extends C{page;constructor(e){super(),this.page=e}async onboard(e){await tt({page:this.page,...e})}async unlock(){await H(this.page)}async lock(){await kt(this.page)}async renameAccount({newAccountName:e,currentAccountName:o}){await It({page:this.page,newAccountName:e,currentAccountName:o})}async addAccount({privateKey:e,accountName:o}){await pt({page:this.page,privateKey:e,accountName:o})}async switchAccount({accountName:e}){await L({page:this.page,accountName:e})}async switchNetwork({...e}){await vt({page:this.page,...e})}async getAccountAddress(e){return await xt(this.page,e)}async toggleShowTestnetNetwork(){await W({page:this.page})}async addCustomNetwork({chainId:e,currencySymbol:o,networkName:n,rpcUrl:r}){await ft({page:this.page,chainId:e,currencySymbol:o,networkName:n,rpcUrl:r})}async connectToApp(e){await ht(await this.promptPage(this.page.context()),e)}async confirmTransaction(e){await yt(await this.promptPage(this.page.context()),e)}async rejectTransaction(){await St(await this.promptPage(this.page.context()))}};import Nt from"fs";import ne from"path";import{test as ae,chromium as re}from"@playwright/test";import{Instance as ie,Pool as se}from"prool";import{expect as te}from"@playwright/test";async function ot(t,e){let o=await t.newPage();return await te(async()=>{await o.goto(e),await E(o)}).toPass(),o}async function nt(t,e){let o=await e.newPage();for(let{origin:n,localStorage:r}of t){let a=o.mainFrame();await a.goto(n),await a.evaluate(i=>{i.forEach(({name:c,value:s})=>{window.localStorage.setItem(c,s)})},r)}await o.close()}import ee from"fs/promises";async function Et(t){await ee.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var oe=35e3;async function J(t,e){try{await Promise.race([t.close(),new Promise((o,n)=>setTimeout(()=>n(new Error("Context close timed out")),oe))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await Et(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var G,Ua=({slowMo:t=0,profileName:e}={})=>ae.extend({contextPath:async({browserName:o},n,r)=>{let a=await R(`${o}-${r.testId}`);await n(a)},context:async({context:o,contextPath:n},r)=>{let a=new C,i=h(a.name),c=await F(a.name),s=ne.resolve(i,e??"wallet-data");if(!Nt.existsSync(s))throw new Error("\u274C Cache for MetaMask wallet data not found. Create it first");Nt.cpSync(s,n,{recursive:!0,force:!0});let p=[`--disable-extensions-except=${c}`,`--load-extension=${c}`];process.env.HEADLESS&&(p.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await re.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:t}),{cookies:u,origins:f}=await o.storageState();u&&await l.addCookies(u),f&&f.length>0&&nt(f,l);let B=await a.indexUrl();await l.waitForEvent("page",{predicate:k=>k.url().startsWith(B),timeout:3e4}),G=l.pages().find(k=>k.url().startsWith(B))??await ot(l,B);for(let k of l.pages())k.url().includes("about:blank")&&await k.close();await G.locator("img[class='loading-spinner']").waitFor({state:"detached"}),await H(G),await r(l),await J(l,n)},metamaskPage:async({context:o},n)=>{await n(G)},metamask:async({context:o},n)=>{let r=new V(G);await n(r)},createAnvilNode:async({context:o},n,r)=>{let a=r.workerIndex,i;await n(async c=>{i=se.define({instance:ie.anvil(c)});let s=await i.start(a),p=`http://${s.host}:${s.port}`,u=c?.chainId??31337;return{rpcUrl:p,anvil:s,chainId:u}}),i&&await i.stop(a)},connectToAnvil:async({context:o,metamask:n,createAnvilNode:r},a)=>{await a(async()=>{let{chainId:i,rpcUrl:c}=await r({chainId:2251});await n.addCustomNetwork({chainId:i,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:c})})}});import{test as de}from"@playwright/test";import{Instance as me,Pool as ue}from"prool";import Ft from"fs";import ce from"path";import{chromium as le}from"@playwright/test";async function _t({workerInfo:t,profileName:e,slowMo:o}){let n=new C,r=await R(t.workerIndex.toString()),a=h(n.name),i=ce.resolve(a,e??"wallet-data");if(!Ft.existsSync(i))throw new Error(`Cache for ${n.name} does not exist. Create it first!`);Ft.cpSync(i,r,{recursive:!0,force:!0});let c=await F(n.name),s=await le.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:o}),p=await n.indexUrl();await s.waitForEvent("page",{predicate:u=>u.url().startsWith(p),timeout:4e4});let l=s.pages().find(u=>u.url().startsWith(p));l||(l=await s.newPage(),await l.goto(p),await E(l));for(let u of s.pages())u.url().includes("about:blank")&&await u.close();return{context:s,walletPage:l,contextPath:r}}var ar=({profileName:t,slowMo:e}={})=>de.extend({workerScopeContents:[async({browser:o},n,r)=>{let{context:a,contextPath:i,walletPage:c}=await _t({workerInfo:r,profileName:t,slowMo:e});await a.grantPermissions(["clipboard-read"]);let s=new V(c);await s.unlock(),await n({wallet:s,walletPage:c,context:a}),await J(a,i)},{scope:"worker"}],createAnvilNode:async({context:o},n,r)=>{let a=r.workerIndex,i;await n(async c=>{i=ue.define({instance:me.anvil(c)});let s=await i.start(a),p=`http://${s.host}:${s.port}`,u=c?.chainId??31337;return{rpcUrl:p,anvil:s,chainId:u}}),i&&await i.stop(a)},connectToAnvil:async({context:o,createAnvilNode:n,workerScopeContents:r},a)=>{await a(async()=>{let{wallet:i}=r,{chainId:c,rpcUrl:s}=await n({chainId:2251});await i.addCustomNetwork({chainId:c,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:s})})}});export{V as Metamask,Ua as metamaskFixture,ar as metamaskWorkerScopeFixture,Oe as workerScopeContext};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chainwright",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.16",
|
|
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",
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"version-package": "changeset version",
|
|
111
111
|
"release": "pnpm run build && changeset publish",
|
|
112
112
|
"dev": "tsx src/cli/index.ts ./tests/wallet-setup",
|
|
113
|
+
"setup-wallets": "tsx src/cli/index.ts ./tests/wallet-setup --all",
|
|
113
114
|
"tests": "vitest --exclude '**/*.spec.ts'",
|
|
114
115
|
"lint": "biome check ./src",
|
|
115
116
|
"format": "biome format --write ./src",
|