chainwright 0.9.10 → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js
CHANGED
|
@@ -5,7 +5,7 @@ ${a}`,{encoding:"utf-8"})}catch(l){console.error("Error appending new profile na
|
|
|
5
5
|
`));return await t.close(),n.id}import v from"fs";import U from"path";import{styleText as _}from"util";import Pe from"adm-zip";import{createWriteStream as Ae}from"fs";import{Readable as ve}from"stream";import{styleText as k}from"util";import Le from"cli-progress";var $e=12e4;async function q({url:e,destination:o}){let t=new AbortController,a=setTimeout(()=>t.abort(),$e),r=await fetch(e,{redirect:"follow",signal:t.signal});r.ok||(console.error(k("redBright",`\u274C Download failed: HTTP ${r.status}`,{validateStream:!1})),t.abort(),process.exit(1));let n=parseInt(r.headers.get("content-length")||"0",10),l=0,s=ve.fromWeb(r.body);try{let c=new Le.SingleBar({format:`Downloading ${k("cyan","{bar}",{validateStream:!1})} {percentage}%`,clearOnComplete:!0,barCompleteChar:"\u2588",barIncompleteChar:"\u2591",hideCursor:!0});c.start(n,0,{speed:"N/A"}),await new Promise((i,p)=>{let m=Ae(o);s.pipe(m),s.on("data",f=>{l+=f.length,c.update(l)}),m.on("error",p),m.on("finish",()=>{c.stop(),i(void 0)})})}catch(c){console.error(k("redBright",`\u274C Download failed: ${c}`,{validateStream:!1})),process.exit(1)}finally{clearTimeout(a)}}var L=!1,M="";async function Z({downloadUrl:e,name:o,force:t}){let a=W(o),n=T[o].extensionName,l=U.join(a,`${o}-extension.zip`),s=U.join(a,`${o}-extension`);if(L?M!==n&&L&&(L=!1,M=n):M=n,t&&v.existsSync(a)&&!L&&(v.rmSync(a,{recursive:!0}),console.info(_("magenta",`\u{1F9F9} Removed ${n} because of the force flag`,{validateStream:!1}))),v.existsSync(a)||v.mkdir(a,{recursive:!0},i=>{if(i)throw Error("Failed to create cache directory");console.info(`\u2705 ${n} Cache directory created successfully.`)}),v.existsSync(s)?console.info(`\u2705 ${n} Version is downloaded already.`):(console.info(_("cyanBright",`\u{1F4E5} Downloading ${n} extension...`,{validateStream:!1})),await q({url:e,destination:l}),console.info(_("green",`\u2705 ${o.toUpperCase()} Extension downloaded successfully.`,{validateStream:!1}))),!v.existsSync(s))console.info("\u{1F4E6} Extracting extension..."),new Pe(l).extractAllTo(s,!0),console.info(`\u2705 ${n} Extension extracted successfully.`);else{if(L)return console.info(_("magentaBright",`Using the cached ${n} extension for profile creation.`,{validateStream:!1})),s;console.info(_("yellow",`\u26A0\uFE0F Skipping ${n} cache creation: Cache already exists at ${s}. Use --force to overwrite.`,{validateStream:!1}))}let c=U.join(s,"manifest.json");if(!v.existsSync(c))throw new Error(`\u274C (${n}) Invalid extension: manifest.json not found`);return L=!0,s}import{styleText as Ce}from"util";function B(e){return new Promise(o=>setTimeout(o,e))}var O=20,We=6e3,J=4e3;async function _e(e){return e.pages().find(a=>{try{return a.url().startsWith("chrome-extension://")}catch(r){return console.error("[WaitForExtensionOnLoadPage] Error checking page URL:",r),!1}})}async function Q(e,o){let t=0,a=null;if(o==="meteor")return await e.newPage();for(console.info(`Waiting ${J}ms for browser to initialize...`),await B(J);t<=O;)try{if(console.info(`Looking for extension page (attempt ${t+1}/${O})...`),t===O)throw new Error("Extension page not found after maximum retries");let r=await _e(e);if(r){console.info(`Found extension page after ${t+1} polling attempts`),a=r;break}r||(t++,console.info(`Extension page not found, retrying (${t}/${O})...`),await B(We))}catch(r){throw console.error("Error waiting for extension page:",r instanceof Error?r.message:"Unknown error"),new Error(`Extension failed to load properly after ${t} attempts!`)}return console.info(Ce("greenBright","\u2705 Extension page is properly loaded and ready",{validateStream:!1})),a}async function j({walletName:e,force:o,config:t,fileList:a,walletPassword:r,setupFunction:n}){let{downloadUrl:l,extensionName:s}=T[e],c=W(e),i=t?.profileName,p=i?`${i}`:"wallet-data",m=R.resolve(c,"extension-id.txt"),f=R.resolve(c,"extension-path.txt"),E=R.resolve(c,"password.txt"),h=R.resolve(c,p),w=await Z({downloadUrl:l,name:e,force:o}),y=[`--disable-extensions-except=${w}`,`--load-extension=${w}`];if(x.existsSync(h)&&a.length>1)throw Error([S("yellowBright",[`\u274C ${p} directory already exists for ${s}.`,`
|
|
6
6
|
To setup another wallet profile, add a profile name to the wallet setup function.`,S(["blueBright","italic"],'Example: defineWalletSetup(async ({ context, walletPage }) => { ... }, { profileName: "profile-name" });'),S("italic","You can also use the --force flag to overwrite the existing cache.")].join(`
|
|
7
7
|
`),{validateStream:!1})].join(`
|
|
8
|
-
`));if(x.existsSync(h))return;let A=await be.launchPersistentContext(h,{headless:!1,args:y,slowMo:t?.slowMo??0});console.info(S("magentaBright",`\u{1F9E9}\u{1F680} Starting Chrome extension for ${e.toUpperCase()}`,{validateStream:!1}));let ee=await Q(A,e);if(!x.existsSync(m)&&!x.existsSync(f)){let F=await Y(A,s);console.info(S("magentaBright",`\u{1F194} ${s} extension ID: ${F}`,{validateStream:!1})),x.writeFileSync(m,F,"utf-8"),console.info(S("cyanBright",`\u{1F4BE} Saved extension ID to: ${m}`,{validateStream:!1})),x.writeFileSync(f,w,"utf-8"),console.info(S("blueBright",`\u{1F4C1} Saved extension Path to: ${f}`,{validateStream:!1})),x.writeFileSync(E,r,"utf-8"),console.info(S("yellowBright",`\u{1F511} Saved ${e} password to: ${E}`,{validateStream:!1}))}try{await n({context:A,walletPage:ee})}catch(F){await A.close(),x.rmSync(c,{force:!0,recursive:!0}),
|
|
8
|
+
`));if(x.existsSync(h))return;let A=await be.launchPersistentContext(h,{headless:!1,args:y,slowMo:t?.slowMo??0});console.info(S("magentaBright",`\u{1F9E9}\u{1F680} Starting Chrome extension for ${e.toUpperCase()}`,{validateStream:!1}));let ee=await Q(A,e);if(!x.existsSync(m)&&!x.existsSync(f)){let F=await Y(A,s);console.info(S("magentaBright",`\u{1F194} ${s} extension ID: ${F}`,{validateStream:!1})),x.writeFileSync(m,F,"utf-8"),console.info(S("cyanBright",`\u{1F4BE} Saved extension ID to: ${m}`,{validateStream:!1})),x.writeFileSync(f,w,"utf-8"),console.info(S("blueBright",`\u{1F4C1} Saved extension Path to: ${f}`,{validateStream:!1})),x.writeFileSync(E,r,"utf-8"),console.info(S("yellowBright",`\u{1F511} Saved ${e} password to: ${E}`,{validateStream:!1}))}try{await n({context:A,walletPage:ee})}catch(F){throw await A.close(),x.rmSync(c,{force:!0,recursive:!0}),Error(`Error setting up wallet: ${F.message}`)}await A.close()}var De=z.resolve(process.cwd(),"tests",X),b=1;async function Oe(){let e=new Te;e.name(g("yellow","Chainwright")).description(g("green","A CLI tool for setting up wallet cache for E2E testing of web3 applications")).version(g("blue","0.0.0")),e.argument("[dir]","Directory containing the wallet setup functions",z.resolve(De)).option("--headless","Build cache in the headless browser mode. Alternatively, set the `HEADLESS` env variable to `true`",!1).option("-f, --force","Force the creation of cache even if it already exists",!1).option("-a, --all","Setup all wallets","all").option("--kp, --keplr","Setup Keplr","keplr").option("-m, --metamask","Setup MetaMask","metamask").option("--mt, --meteor","Setup Meteor","meteor").option("--pt, --petra","Setup Petra","petra").option("--ph, --phantom","Setup Phantom","phantom").option("-s, --solflare","Setup Solflare","solflare").option("--wls, --wallets <wallets...>","Specify wallets to setup (e.g., --wallets keplr metamask)").action(async(o,t)=>{let a=["all","metamask","solflare","petra","meteor","keplr","phantom"],r=Object.keys(t).filter(p=>a.includes(p)?t[p]===!0:!1),n=r.length>0,l=t.wallets,s=l||(n?r:await Fe({message:"Select the wallet you want to setup",choices:[{name:"All",value:"all"},{name:"Keplr",value:"keplr"},{name:"MetaMask",value:"metamask"},{name:"Meteor",value:"meteor"},{name:"Petra",value:"petra"},{name:"Phantom",value:"phantom"},{name:"Solflare",value:"solflare"}],validate:p=>{let m=p.map(f=>f.value);return m.includes("all")&&m.length>1?'Select either "All" or specific wallets, not both.':!0},pageSize:10})),c=z.resolve(process.cwd(),o);t.headless&&(process.env.HEADLESS=!0);let i=await N({walletSetupDir:c,selectedWallets:s});for(let{walletName:p,config:m,walletPassword:f,setupFunction:E,fileList:h}of i)try{console.info(g("cyanBright",`
|
|
9
9
|
Setting up cache for ${p}...`,{validateStream:!1})),await j({walletName:p,config:m,setupFunction:E,fileList:h,force:t.force,walletPassword:f}),m.profileName&&await G({walletName:p,profileName:m.profileName})}catch(w){if(w.message.includes("directory already exists")&&console.warn(w.message),!w.message.includes("directory already exists")){console.error(g("redBright",`
|
|
10
10
|
\u274C Failed to setup cache for ${p}: ${w.message}`,{validateStream:!1}));let y=0;for(;b>y;){console.info(`${g("yellow",`Retry Attempt ${y+1} of ${b} for ${p}...`,{validateStream:!1})}`),console.info(g("yellow","Retrying wallet setup...",{validateStream:!1}));try{await j({walletName:p,config:m,setupFunction:E,fileList:h,force:t.force,walletPassword:f});break}catch(A){y+1<b&&console.error(g("redBright",`
|
|
11
11
|
\u274C Attempt ${y+1} failed! Retrying...`,{validateStream:!1})),y++,y===b&&console.error(g("redBright",`\u274C Failed to setup cache after ${b} attempts for ${p}: ${A.message}`,{validateStream:!1}))}}}}}),await e.parseAsync(process.argv)}Oe().catch(e=>console.error(g("redBright",`Failed to run the CLI: ${e.message})`,{validateStream:!1})));export{Oe as clientEntry};
|
|
@@ -11,11 +11,6 @@ type RenameAccount = {
|
|
|
11
11
|
newAccountName: string;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
type SwitchAccount = {
|
|
15
|
-
page: Page;
|
|
16
|
-
accountName: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
14
|
declare class MetamaskProfile {
|
|
20
15
|
readonly name: "metamask";
|
|
21
16
|
readonly onboardingPath = "/home.html#onboarding";
|
|
@@ -359,13 +354,12 @@ declare class Metamask extends MetamaskProfile {
|
|
|
359
354
|
addAccount({ privateKey, accountName }: AddAccountArgs): Promise<void>;
|
|
360
355
|
/**
|
|
361
356
|
* Switches the current account to the given account.
|
|
362
|
-
* @param
|
|
363
|
-
* @param args.accountName - The name of the account to switch to.
|
|
357
|
+
* @param accountName - The name of the account to switch to.
|
|
364
358
|
* @example
|
|
365
359
|
* const metamask = new Metamask(page);
|
|
366
|
-
* await metamask.switchAccount(
|
|
360
|
+
* await metamask.switchAccount("Account 1");
|
|
367
361
|
*/
|
|
368
|
-
switchAccount(
|
|
362
|
+
switchAccount(accountName: string): Promise<void>;
|
|
369
363
|
/**
|
|
370
364
|
* Switches the current network to the given network.
|
|
371
365
|
* @param {SwitchNetwork} args - The arguments to switch the network.
|
|
@@ -380,9 +374,10 @@ declare class Metamask extends MetamaskProfile {
|
|
|
380
374
|
/**
|
|
381
375
|
* Gets the current account's address.
|
|
382
376
|
* @returns The current account's address as a string.
|
|
377
|
+
* @param netowrk - Get the address based on the network ("Bitcoin", "Ethereum", "Solana", "Tron")
|
|
383
378
|
* @example
|
|
384
379
|
* const metamask = new Metamask(page);
|
|
385
|
-
* await metamask.getAccountAddress()
|
|
380
|
+
* await metamask.getAccountAddress("Ethereum")
|
|
386
381
|
*/
|
|
387
382
|
getAccountAddress(network: GetAccountAddressChains): Promise<string>;
|
|
388
383
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import lt from"fs";import Vt from"path";import{chromium as Ut}from"@playwright/test";import Lt from"path";var it=".wallet-cache",st=".wallet-context";var rt="13.33.0",U="https://github.com/amaify/chainwright/releases/download/v0.1.0/",fe=`https://github.com/MetaMask/metamask-extension/releases/download/v${rt}/metamask-chrome-${rt}.zip`,ge=`${U}solflare-wallet-extension-v2.19.1.zip`,ye=`${U}petra-wallet-extension-v2.4.8.zip`,he=`${U}phantom-wallet-extension-v26.10.0.zip`,xe=`${U}meteor-wallet-extension-v0.7.0.zip`,Be=`${U}keplr-wallet-extension-v0.13.3.zip`;async function v(t){return Lt.resolve(process.cwd(),st,t)}import Wt from"path";function x(t){return Wt.resolve(process.cwd(),it,t)}import ct from"fs";import Dt from"path";async function F(t){try{let e=x(t),o=Dt.resolve(e,"extension-path.txt");if(!ct.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let n=ct.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 $e({wallet:t,workerInfo:e,profileName:o,slowMo:n}){let r=await v(e.workerIndex.toString()),i=x(t.name),s=Vt.resolve(i,o??"wallet-data");if(!lt.existsSync(s))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);lt.cpSync(s,r,{recursive:!0,force:!0});let c=await F(t.name),a=await Ut.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:n}),p=await t.indexUrl(),l=a.pages()[0];return l||(l=await a.newPage()),await l.goto(p),{context:a,walletPage:l,contextPath:r}}import{expect as B}from"@playwright/test";import ut from"zod";import{test as Ot}from"@playwright/test";function dt(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"},S={lockButton:"global-menu-lock",networksButton:"global-menu-networks",networksPageList:"networks-page-list",headerBackButton:"settings-header-back-button"},O={passwordInput:"unlock-password",unlockButton:"unlock-submit"},C={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:"choose-wallet-type-import-wallet",importAccountButton:"choose-wallet-type-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 mt({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),i=t.getByTestId(w.accountMenuButton);await B(i).toBeVisible({timeout:3e4}),await i.click(),await B(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let s=t.getByTestId(C.addWalletButton),c=await s.textContent();c?.includes("Syncing")&&await B.poll(async()=>(await s.textContent())?.trim()??"",{timeout:12e4}).not.toBe(c),await B(s).toBeEnabled({timeout:6e4}),await s.click();let a=t.getByTestId("multichain-page");await B(a).toContainText(/add a wallet/i),await t.getByTestId(C.importAccountButton).click(),await t.locator("input[id='private-key-box']").fill(r);let m=t.getByTestId(g.importAccountConfirmButton);await B(m).toBeEnabled(),await m.click();let f=t.getByTestId(g.importSRPError),k=await f.isVisible().catch(()=>!1);k&&dt(k,`${(await f.textContent())?.split(".")[0]}`),await t.locator("button[aria-label='Back']").first().click();let A=t.locator("div[data-testid^='multichain-account-cell-keyring'][class*='is-selected']"),h=await A.locator("p[class*='multichain-account-cell__account-name']").textContent();h&&await $t({page:t,accountName:n,activeAccountLocator:A,activeAccountName:h}),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 B(r).toBeVisible(),await r.click(),await B(t.getByRole("tooltip")).toBeVisible();let i=t.locator(`div[aria-label='${C.renameAccountLabel}']`);await B(i).toBeVisible(),await i.click();let s=t.getByRole("dialog"),c=s.getByRole("heading",{name:/rename/i});await B(c).toBeVisible();let a=s.getByRole("textbox");await B(a).toBeVisible(),await a.fill(e);let p=s.getByRole("button",{name:/confirm/i});await B(p).toBeEnabled(),await p.click(),await s.waitFor({state:"detached",timeout:2e4});let l=t.locator("div[data-testid^='multichain-account-cell-keyring'][class*='is-selected']");await B(l).toContainText(e)}import{expect as wt}from"@playwright/test";import M from"zod";var pt=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 z(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:i}=pt.parse({...e},{reportInput:!0});await z(t),await t.getByTestId(S.networksButton).click();let c=t.getByTestId(S.networksPageList);await wt(c).toContainText(/networks/i),await t.getByRole("button",{name:/add a custom network/i}).click();let p=t.getByTestId("network-form-network-name"),l=t.getByTestId("test-add-rpc-drop-down"),m=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();let A=t.getByTestId("rpc-url-input-test"),h=t.getByRole("button",{name:/Add URL/i});await A.fill(i),await h.click();let y=t.getByTestId("network-form-chain-id-error");if(await y.isVisible().catch(()=>!1)){let _=await y.textContent();throw Error(`RPC error: ${_}`)}await m.fill(`${o}`),await f.fill(n);let P=t.getByRole("button",{name:/save/i});await wt(P).toBeEnabled(),await P.click(),await t.getByTestId(S.headerBackButton).click()}import{expect as gt}from"@playwright/test";function L(t){return new Promise(e=>setTimeout(e,t))}var K={confirmButton:"confirm-footer-button",cancelButton:"confirm-footer-cancel-button"};async function yt(t,e){let o=t.getByTestId(K.confirmButton);await L(2e3);let r=(await o.textContent())?.includes("Review alert"),i=await o.isDisabled().catch(()=>!1);if(r&&i){await t.getByTestId("edit-gas-fees-row").locator("> div").first().click();let l=t.getByRole("dialog");await gt(l).toBeVisible();let m=l.locator("h4",{hasText:"Insufficient funds"}),f=await l.getByTestId("alert-modal__selected-alert").textContent();if(await m.isVisible().catch(()=>!1))throw Error(`${f}`)}if(e){let a=t.getByTestId("edit-gas-fee-icon");if(await a.scrollIntoViewIfNeeded(),await a.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"}),m=t.getByRole("textbox",{name:"Priority fee"}),f=t.getByRole("button",{name:"Save",exact:!0});await l.fill(e.maxBaseFee),await m.fill(e.priorityFee),await f.click()}}await gt(o).toBeEnabled(),await o.click();let s=t.getByRole("dialog");await s.isVisible().catch(()=>!1)&&await s.locator("h4",{hasText:"Your assets may be at risk"}).isVisible().catch(()=>!1)&&(await s.getByTestId("alert-modal-acknowledge-checkbox").click(),await s.getByTestId("confirm-alert-modal-submit-button").click()),await t.waitForEvent("close",{timeout:15e3})}import{expect as Gt}from"@playwright/test";async function ht(t){let e=t.getByRole("button",{name:"Connect",exact:!0});await e.waitFor({state:"visible",timeout:25e3}),await t.getByRole("button",{name:"Edit accounts"}).click();let n=t.getByTestId("modal-page");await n.waitFor({state:"visible",timeout:25e3});let r=await t.locator("[data-testid^='multichain-account-cell-entropy:']").all();for(let p of r)await p.locator("input[type='checkbox']").isChecked().catch(()=>!1)||await p.click();await n.getByTestId("connect-more-accounts-button").click(),await e.click(),await t.getByRole("heading",{name:"Connecting",exact:!0}).waitFor({state:"detached",timeout:3e4});let c;await Gt.poll(async()=>(c=await t.locator("div[class='permissions-connect']").isVisible().catch(()=>!1),c),{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 R(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 R(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}},j=async t=>{try{await jt(Bt.loadingSpinner,t,Kt)}catch(e){console.warn("Warning during MetaMask load:",e)}return await L(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 z(t);let n=t.getByTestId(S.lockButton);await qt(n).toBeVisible(),await n.click(),await j(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=x(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(i=>i.url().startsWith("chrome-extension://")).map(i=>i.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 Tt from"fs";import Yt from"path";async function Ct(t){let e=x(t),o=Yt.resolve(e,"extension-id.txt");try{if(!Tt.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return Tt.readFileSync(o,"utf-8")}catch(n){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${n.message}`)}}var T=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 Ct(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 J}from"@playwright/test";async function X({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 J(o).toBeVisible({timeout:15e3}),await o.click(),await J(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let r=t.getByTestId(C.addMultichainAccountButton),i=await r.textContent();i?.includes("Syncing")&&await J.poll(async()=>(await r.textContent())?.trim()??"",{timeout:6e4}).not.toBe(i);let s=await t.getByTestId(/^multichain-account-cell-(?:entropy|keyring):/).all(),c=null;for(let a of s)if(await a.scrollIntoViewIfNeeded(),(await a.textContent())?.includes(e)){c=a;break}if(!c)throw Error(`Account with name "${e}" not found.`);await c?.click()}import{expect as Y}from"@playwright/test";async function W({page:t}){await t.locator(`div:has(> button[data-testid='${w.openSettingsButton}'])`).click(),await t.getByTestId(S.networksButton).click();let n=t.getByTestId("networks-page-list");await Y(n).toBeVisible(),await Y(n).toContainText(/networks/i);let r="div:has(> p:has-text('Show test networks'))";await n.locator(r).scrollIntoViewIfNeeded();let i=n.locator(r),s=await i.locator("label[class='toggle-button toggle-button--off']").isVisible().catch(()=>!1),c=t.getByTestId("settings-header-back-button"),a=t.getByTestId("drawer-close-button");if(!s){await c.click(),await a.click(),console.info("Testnet networks are already visible.");return}await i.locator("label[class='toggle-button toggle-button--off']").click(),await t.getByTestId("Sepolia").scrollIntoViewIfNeeded(),await Y(t.getByTestId("Sepolia")).toBeVisible(),await c.click(),await a.click()}async function Z({page:t,mainAccountName:e,...o}){console.info(Pt("yellowBright",`
|
|
3
|
-
\u{1F98A} MetaMask onboarding started...`,{validateStream:!1}));let n=await q("metamask"),r=new T,i=t.locator("img[class='loading-spinner']"),s=t.getByTestId(g.createWalletButton),c=t.getByTestId(g.importWalletButton),a=t.getByTestId(g.createNewPasswordInput),p=t.getByTestId(g.confirmNewPasswordInput),l=t.getByTestId(g.confirmPasswordCheckbox),m=t.getByTestId(g.createPasswordButton),f=t.getByTestId(g.metamaskMetricsIAgreeButton),k=t.getByTestId(g.onboardingDoneButton);if(await i.waitFor({state:"detached",timeout:3e4}),o.mode==="create"){let P=t.getByTestId(g.useSecretRecoveryPhraseButton);await s.click(),await P.click(),await a.fill(n),await p.fill(n),await l.click(),await m.click(),await t.getByTestId(g.revealSecretRecoveryPhraseButton).click(),await t.getByTestId(g.recoveryPhraseRemindMeLaterButton).click(),await f.click()}if(o.mode==="import"){let{secretRecoveryPhrase:P}=o,I=P.split(" "),_=t.getByTestId(g.importUsingSecretRecoveryPhraseButton);await c.click(),await _.click();let nt=t.getByTestId(g.secretRecoveryPhraseTextAreaInput);await nt.fill(I[0]),await nt.press("Space");for(let G=1;G<I.length;G++){let at=t.getByTestId(`import-srp__srp-word-${G}`);await at.fill(I[G]),await at.press("Space")}await t.getByTestId(g.importWalletConfirmButton).click(),await a.fill(n),await p.fill(n),await l.click(),await m.click(),await t.getByTestId("passkey-maybe-later-button").click(),await f.click();let Mt=t.getByTestId("wallet-ready");await D(Mt).toContainText(/your wallet is ready/i)}await k.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:P}=await y.send("Target.getTargets"),I=P.find(_=>_.url===A);return N=I,!!I}},{timeout:15e3}).toBe(!0),N&&await y?.send("Target.closeTarget",{targetId:N.targetId}),await t.goto(await r.indexUrl()),await i.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 X({page:t,accountName:e}),await L(5e3),console.info(Pt("greenBright","\u2728 MetaMask onboarding completed successfully",{validateStream:!1}))}import{expect as Zt}from"@playwright/test";async function It(t){let e=t.getByTestId(K.cancelButton);await Zt(e).toBeEnabled(),await e.click()}import{expect as b}from"@playwright/test";async function St({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 i=t.getByTestId(C.addMultichainAccountButton),s=await i.textContent();s?.includes("Syncing")&&await b.poll(async()=>(await i.textContent())?.trim()??"",{timeout:6e4}).not.toBe(s);let c=await t.getByTestId(/^multichain-account-cell/).all(),a=null;for(let y of c)if((await y.textContent())?.includes(e)){a=y;break}if(!a)throw Error(`Account with name "${e}" not found.`);if((await a.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 m=t.locator(`div[aria-label='${C.renameAccountLabel}']`);await b(m).toBeVisible(),await m.click();let f=t.getByRole("dialog"),k=f.getByRole("heading",{name:/rename/i});await b(k).toBeVisible();let E=f.getByRole("textbox");await b(E).toBeVisible(),await E.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 tt}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 a=t.getByRole("tab",{name:"Custom"});await a.click(),await t.locator("p:has-text('Testnets')").isVisible().catch(()=>!1)||(await r.click(),await W({page:t}),await n.click(),await a.click());let m=t.locator(`div div[data-testid='${o}']`),f=await m.textContent();tt(f).toBe(o),await m.click();return}await t.getByRole("tab",{name:"Popular"}).click();let s=t.locator(`div div[data-testid='${o}']`),c=await s.textContent();tt(c).toBe(o),await s.click(),await tt(n).toContainText(o,{timeout:3e4})}import{expect as Rt}from"@playwright/test";async function $(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 i=t.getByTestId(O.unlockButton);await Rt(i).toBeVisible(),await i.click(),await j(t),await Rt(t.getByTestId(w.buyButton)).toBeVisible({timeout:3e4})}var V=class extends T{page;constructor(e){super(),this.page=e}async onboard(e){await Z({page:this.page,...e})}async unlock(){await $(this.page)}async lock(){await kt(this.page)}async renameAccount({newAccountName:e,currentAccountName:o}){await St({page:this.page,newAccountName:e,currentAccountName:o})}async addAccount({privateKey:e,accountName:o}){await mt({page:this.page,privateKey:e,accountName:o})}async switchAccount({accountName:e}){await X({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(){await ht(await this.promptPage(this.page.context()))}async confirmTransaction(e){await yt(await this.promptPage(this.page.context()),e)}async rejectTransaction(){await It(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 et(t,e){let o=await t.newPage();return await te(async()=>{await o.goto(e),await R(o)}).toPass(),o}async function ot(t,e){let o=await e.newPage();for(let{origin:n,localStorage:r}of t){let i=o.mainFrame();await i.goto(n),await i.evaluate(s=>{s.forEach(({name:c,value:a})=>{window.localStorage.setItem(c,a)})},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 Q(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 H,Va=({slowMo:t=0,profileName:e}={})=>ae.extend({contextPath:async({browserName:o},n,r)=>{let i=await v(`${o}-${r.testId}`);await n(i)},context:async({context:o,contextPath:n},r)=>{let i=new T,s=x(i.name),c=await F(i.name),a=ne.resolve(s,e??"wallet-data");if(!Nt.existsSync(a))throw new Error("\u274C Cache for MetaMask wallet data not found. Create it first");Nt.cpSync(a,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:m,origins:f}=await o.storageState();m&&await l.addCookies(m),f&&f.length>0&&ot(f,l);let k=await i.indexUrl();await l.waitForEvent("page",{predicate:h=>h.url().startsWith(k),timeout:3e4}),H=l.pages().find(h=>h.url().startsWith(k))??await et(l,k);for(let h of l.pages())h.url().includes("about:blank")&&await h.close();await H.locator("img[class='loading-spinner']").waitFor({state:"detached"}),await $(H),await r(l),await Q(l,n)},metamaskPage:async({context:o},n)=>{await n(H)},metamask:async({context:o},n)=>{let r=new V(H);await n(r)},createAnvilNode:async({context:o},n,r)=>{let i=r.workerIndex,s;await n(async c=>{s=se.define({instance:ie.anvil(c)});let a=await s.start(i),p=`http://${a.host}:${a.port}`,m=c?.chainId??31337;return{rpcUrl:p,anvil:a,chainId:m}}),s&&await s.stop(i)},connectToAnvil:async({context:o,metamask:n,createAnvilNode:r},i)=>{await i(async()=>{let{chainId:s,rpcUrl:c}=await r({chainId:2251});await n.addCustomNetwork({chainId:s,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:c})})}});import{test as de}from"@playwright/test";import{Instance as ue,Pool as me}from"prool";import _t from"fs";import ce from"path";import{chromium as le}from"@playwright/test";async function Ft({workerInfo:t,profileName:e,slowMo:o}){let n=new T,r=await v(t.workerIndex.toString()),i=x(n.name),s=ce.resolve(i,e??"wallet-data");if(!_t.existsSync(s))throw new Error(`Cache for ${n.name} does not exist. Create it first!`);_t.cpSync(s,r,{recursive:!0,force:!0});let c=await F(n.name),a=await le.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:o}),p=await n.indexUrl();await a.waitForEvent("page",{predicate:m=>m.url().startsWith(p),timeout:4e4});let l=a.pages().find(m=>m.url().startsWith(p));l||(l=await a.newPage(),await l.goto(p),await R(l));for(let m of a.pages())m.url().includes("about:blank")&&await m.close();return{context:a,walletPage:l,contextPath:r}}var nr=({profileName:t,slowMo:e}={})=>de.extend({workerScopeContents:[async({browser:o},n,r)=>{let{context:i,contextPath:s,walletPage:c}=await Ft({workerInfo:r,profileName:t,slowMo:e});await i.grantPermissions(["clipboard-read"]);let a=new V(c);await a.unlock(),await n({wallet:a,walletPage:c,context:i}),await Q(i,s)},{scope:"worker"}],createAnvilNode:async({context:o},n,r)=>{let i=r.workerIndex,s;await n(async c=>{s=me.define({instance:ue.anvil(c)});let a=await s.start(i),p=`http://${a.host}:${a.port}`,m=c?.chainId??31337;return{rpcUrl:p,anvil:a,chainId:m}}),s&&await s.stop(i)},connectToAnvil:async({context:o,createAnvilNode:n,workerScopeContents:r},i)=>{await i(async()=>{let{wallet:s}=r,{chainId:c,rpcUrl:a}=await n({chainId:2251});await s.addCustomNetwork({chainId:c,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:a})})}});export{V as Metamask,Va as metamaskFixture,nr as metamaskWorkerScopeFixture,$e as workerScopeContext};
|
|
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"},S={lockButton:"global-menu-lock",networksButton:"global-menu-networks",networksPageList:"networks-page-list",headerBackButton:"settings-header-back-button"},O={passwordInput:"unlock-password",unlockButton:"unlock-submit"},C={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:"choose-wallet-type-import-wallet",importAccountButton:"choose-wallet-type-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 mt({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),i=t.getByTestId(w.accountMenuButton);await B(i).toBeVisible({timeout:3e4}),await i.click(),await B(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let s=t.getByTestId(C.addWalletButton),c=await s.textContent();c?.includes("Syncing")&&await B.poll(async()=>(await s.textContent())?.trim()??"",{timeout:12e4}).not.toBe(c),await B(s).toBeEnabled({timeout:6e4}),await s.click();let a=t.getByTestId("multichain-page");await B(a).toContainText(/add a wallet/i),await t.getByTestId(C.importAccountButton).click(),await t.locator("input[id='private-key-box']").fill(r);let m=t.getByTestId(g.importAccountConfirmButton);await B(m).toBeEnabled(),await m.click();let f=t.getByTestId(g.importSRPError),k=await f.isVisible().catch(()=>!1);k&&dt(k,`${(await f.textContent())?.split(".")[0]}`),await t.locator("button[aria-label='Back']").first().click();let T=t.locator("div[data-testid^='multichain-account-cell-keyring'][class*='is-selected']"),h=await T.locator("p[class*='multichain-account-cell__account-name']").textContent();h&&await $t({page:t,accountName:n,activeAccountLocator:T,activeAccountName:h}),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 B(r).toBeVisible(),await r.click(),await B(t.getByRole("tooltip")).toBeVisible();let i=t.locator(`div[aria-label='${C.renameAccountLabel}']`);await B(i).toBeVisible(),await i.click();let s=t.getByRole("dialog"),c=s.getByRole("heading",{name:/rename/i});await B(c).toBeVisible();let a=s.getByRole("textbox");await B(a).toBeVisible(),await a.fill(e);let p=s.getByRole("button",{name:/confirm/i});await B(p).toBeEnabled(),await p.click(),await s.waitFor({state:"detached",timeout:2e4});let l=t.locator("div[data-testid^='multichain-account-cell-keyring'][class*='is-selected']");await B(l).toContainText(e)}import{expect as wt}from"@playwright/test";import M from"zod";var pt=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 z(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:i}=pt.parse({...e},{reportInput:!0});await z(t),await t.getByTestId(S.networksButton).click();let c=t.getByTestId(S.networksPageList);await wt(c).toContainText(/networks/i),await t.getByRole("button",{name:/add a custom network/i}).click();let p=t.getByTestId("network-form-network-name"),l=t.getByTestId("test-add-rpc-drop-down"),m=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();let T=t.getByTestId("rpc-url-input-test"),h=t.getByRole("button",{name:/Add URL/i});await T.fill(i),await h.click();let y=t.getByTestId("network-form-chain-id-error");if(await y.isVisible().catch(()=>!1)){let _=await y.textContent();throw Error(`RPC error: ${_}`)}await m.fill(`${o}`),await f.fill(n);let P=t.getByRole("button",{name:/save/i});await wt(P).toBeEnabled(),await P.click(),await t.getByTestId(S.headerBackButton).click()}import{expect as gt}from"@playwright/test";function L(t){return new Promise(e=>setTimeout(e,t))}var K={confirmButton:"confirm-footer-button",cancelButton:"confirm-footer-cancel-button"};async function yt(t,e){let o=t.getByTestId(K.confirmButton);await L(2e3);let r=(await o.textContent())?.includes("Review alert"),i=await o.isDisabled().catch(()=>!1);if(r&&i){await t.getByTestId("edit-gas-fees-row").locator("> div").first().click();let l=t.getByRole("dialog");await gt(l).toBeVisible();let m=l.locator("h4",{hasText:"Insufficient funds"}),f=await l.getByTestId("alert-modal__selected-alert").textContent();if(await m.isVisible().catch(()=>!1))throw Error(`${f}`)}if(e){let a=t.getByTestId("edit-gas-fee-icon");if(await a.scrollIntoViewIfNeeded(),await a.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"}),m=t.getByRole("textbox",{name:"Priority fee"}),f=t.getByRole("button",{name:"Save",exact:!0});await l.fill(e.maxBaseFee),await m.fill(e.priorityFee),await f.click()}}await gt(o).toBeEnabled(),await o.click();let s=t.getByRole("dialog");await s.isVisible().catch(()=>!1)&&await s.locator("h4",{hasText:"Your assets may be at risk"}).isVisible().catch(()=>!1)&&(await s.getByTestId("alert-modal-acknowledge-checkbox").click(),await s.getByTestId("confirm-alert-modal-submit-button").click()),await t.waitForEvent("close",{timeout:15e3})}import{expect as Gt}from"@playwright/test";async function ht(t){let e=t.getByRole("button",{name:"Connect",exact:!0});await e.waitFor({state:"visible",timeout:25e3}),await t.getByRole("button",{name:"Edit accounts"}).click();let n=t.getByTestId("modal-page");await n.waitFor({state:"visible",timeout:25e3});let r=await t.locator("[data-testid^='multichain-account-cell-entropy:']").all();for(let p of r)await p.locator("input[type='checkbox']").isChecked().catch(()=>!1)||await p.click();await n.getByTestId("connect-more-accounts-button").click(),await e.click(),await t.getByRole("heading",{name:"Connecting",exact:!0}).waitFor({state:"detached",timeout:3e4});let c;await Gt.poll(async()=>(c=await t.locator("div[class='permissions-connect']").isVisible().catch(()=>!1),c),{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 R(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 R(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}},j=async t=>{try{await jt(Bt.loadingSpinner,t,Kt)}catch(e){console.warn("Warning during MetaMask load:",e)}return await L(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 z(t);let n=t.getByTestId(S.lockButton);await qt(n).toBeVisible(),await n.click(),await j(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=x(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 Tt({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(i=>i.url().startsWith("chrome-extension://")).map(i=>i.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 At from"fs";import Yt from"path";async function Ct(t){let e=x(t),o=Yt.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(n){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${n.message}`)}}var A=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 Ct(this.name)}async promptPage(e){let o=await this.promptUrl();return await Tt({context:e,path:o,locator:"div[data-testid='multichain-page']"})}};import{expect as J}from"@playwright/test";async function X(t,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 J(o).toBeVisible({timeout:15e3}),await o.click(),await J(t.getByRole("heading",{name:/accounts/i})).toBeVisible();let r=t.getByTestId(C.addMultichainAccountButton),i=await r.textContent();i?.includes("Syncing")&&await J.poll(async()=>(await r.textContent())?.trim()??"",{timeout:6e4}).not.toBe(i);let s=await t.getByTestId(/^multichain-account-cell-(?:entropy|keyring):/).all(),c=null;for(let a of s)if(await a.scrollIntoViewIfNeeded(),(await a.textContent())?.includes(e)){c=a;break}if(!c)throw Error(`Account with name "${e}" not found.`);await c?.click()}import{expect as Y}from"@playwright/test";async function W({page:t}){await t.locator(`div:has(> button[data-testid='${w.openSettingsButton}'])`).click(),await t.getByTestId(S.networksButton).click();let n=t.getByTestId("networks-page-list");await Y(n).toBeVisible(),await Y(n).toContainText(/networks/i);let r="div:has(> p:has-text('Show test networks'))";await n.locator(r).scrollIntoViewIfNeeded();let i=n.locator(r),s=await i.locator("label[class='toggle-button toggle-button--off']").isVisible().catch(()=>!1),c=t.getByTestId("settings-header-back-button"),a=t.getByTestId("drawer-close-button");if(!s){await c.click(),await a.click(),console.info("Testnet networks are already visible.");return}await i.locator("label[class='toggle-button toggle-button--off']").click(),await t.getByTestId("Sepolia").scrollIntoViewIfNeeded(),await Y(t.getByTestId("Sepolia")).toBeVisible(),await c.click(),await a.click()}async function Z({page:t,mainAccountName:e,...o}){console.info(Pt("yellowBright",`
|
|
3
|
+
\u{1F98A} MetaMask onboarding started...`,{validateStream:!1}));let n=await q("metamask"),r=new A,i=t.locator("img[class='loading-spinner']"),s=t.getByTestId(g.createWalletButton),c=t.getByTestId(g.importWalletButton),a=t.getByTestId(g.createNewPasswordInput),p=t.getByTestId(g.confirmNewPasswordInput),l=t.getByTestId(g.confirmPasswordCheckbox),m=t.getByTestId(g.createPasswordButton),f=t.getByTestId(g.metamaskMetricsIAgreeButton),k=t.getByTestId(g.onboardingDoneButton);if(await i.waitFor({state:"detached",timeout:3e4}),o.mode==="create"){let P=t.getByTestId(g.useSecretRecoveryPhraseButton);await s.click(),await P.click(),await a.fill(n),await p.fill(n),await l.click(),await m.click(),await t.getByTestId(g.revealSecretRecoveryPhraseButton).click(),await t.getByTestId(g.recoveryPhraseRemindMeLaterButton).click(),await f.click()}if(o.mode==="import"){let{secretRecoveryPhrase:P}=o,I=P.split(" "),_=t.getByTestId(g.importUsingSecretRecoveryPhraseButton);await c.click(),await _.click();let nt=t.getByTestId(g.secretRecoveryPhraseTextAreaInput);await nt.fill(I[0]),await nt.press("Space");for(let G=1;G<I.length;G++){let at=t.getByTestId(`import-srp__srp-word-${G}`);await at.fill(I[G]),await at.press("Space")}await t.getByTestId(g.importWalletConfirmButton).click(),await a.fill(n),await p.fill(n),await l.click(),await m.click(),await t.getByTestId("passkey-maybe-later-button").click(),await f.click();let Mt=t.getByTestId("wallet-ready");await D(Mt).toContainText(/your wallet is ready/i)}await k.click();let T=`chrome-extension://${await r.extensionId()}/sidepanel.html`,y=await t.context().browser()?.newBrowserCDPSession(),N;await D.poll(async()=>{if(y){let{targetInfos:P}=await y.send("Target.getTargets"),I=P.find(_=>_.url===T);return N=I,!!I}},{timeout:15e3}).toBe(!0),N&&await y?.send("Target.closeTarget",{targetId:N.targetId}),await t.goto(await r.indexUrl()),await i.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 X(t,e),await L(5e3),console.info(Pt("greenBright","\u2728 MetaMask onboarding completed successfully",{validateStream:!1}))}import{expect as Zt}from"@playwright/test";async function It(t){let e=t.getByTestId(K.cancelButton);await Zt(e).toBeEnabled(),await e.click()}import{expect as b}from"@playwright/test";async function St({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 i=t.getByTestId(C.addMultichainAccountButton),s=await i.textContent();s?.includes("Syncing")&&await b.poll(async()=>(await i.textContent())?.trim()??"",{timeout:6e4}).not.toBe(s);let c=await t.getByTestId(/^multichain-account-cell/).all(),a=null;for(let y of c)if((await y.textContent())?.includes(e)){a=y;break}if(!a)throw Error(`Account with name "${e}" not found.`);if((await a.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 m=t.locator(`div[aria-label='${C.renameAccountLabel}']`);await b(m).toBeVisible(),await m.click();let f=t.getByRole("dialog"),k=f.getByRole("heading",{name:/rename/i});await b(k).toBeVisible();let E=f.getByRole("textbox");await b(E).toBeVisible(),await E.fill(o);let T=f.getByRole("button",{name:/confirm/i});await b(T).toBeEnabled(),await T.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 tt}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 a=t.getByRole("tab",{name:"Custom"});await a.click(),await t.locator("p:has-text('Testnets')").isVisible().catch(()=>!1)||(await r.click(),await W({page:t}),await n.click(),await a.click());let m=t.locator(`div div[data-testid='${o}']`),f=await m.textContent();tt(f).toBe(o),await m.click();return}await t.getByRole("tab",{name:"Popular"}).click();let s=t.locator(`div div[data-testid='${o}']`),c=await s.textContent();tt(c).toBe(o),await s.click(),await tt(n).toContainText(o,{timeout:3e4})}import{expect as Rt}from"@playwright/test";async function $(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 i=t.getByTestId(O.unlockButton);await Rt(i).toBeVisible(),await i.click(),await j(t),await Rt(t.getByTestId(w.buyButton)).toBeVisible({timeout:3e4})}var V=class extends A{page;constructor(e){super(),this.page=e}async onboard(e){await Z({page:this.page,...e})}async unlock(){await $(this.page)}async lock(){await kt(this.page)}async renameAccount({newAccountName:e,currentAccountName:o}){await St({page:this.page,newAccountName:e,currentAccountName:o})}async addAccount({privateKey:e,accountName:o}){await mt({page:this.page,privateKey:e,accountName:o})}async switchAccount(e){await X(this.page,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(){await ht(await this.promptPage(this.page.context()))}async confirmTransaction(e){await yt(await this.promptPage(this.page.context()),e)}async rejectTransaction(){await It(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 et(t,e){let o=await t.newPage();return await te(async()=>{await o.goto(e),await R(o)}).toPass(),o}async function ot(t,e){let o=await e.newPage();for(let{origin:n,localStorage:r}of t){let i=o.mainFrame();await i.goto(n),await i.evaluate(s=>{s.forEach(({name:c,value:a})=>{window.localStorage.setItem(c,a)})},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 Q(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 H,Va=({slowMo:t=0,profileName:e}={})=>ae.extend({contextPath:async({browserName:o},n,r)=>{let i=await v(`${o}-${r.testId}`);await n(i)},context:async({context:o,contextPath:n},r)=>{let i=new A,s=x(i.name),c=await F(i.name),a=ne.resolve(s,e??"wallet-data");if(!Nt.existsSync(a))throw new Error("\u274C Cache for MetaMask wallet data not found. Create it first");Nt.cpSync(a,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:m,origins:f}=await o.storageState();m&&await l.addCookies(m),f&&f.length>0&&ot(f,l);let k=await i.indexUrl();await l.waitForEvent("page",{predicate:h=>h.url().startsWith(k),timeout:3e4}),H=l.pages().find(h=>h.url().startsWith(k))??await et(l,k);for(let h of l.pages())h.url().includes("about:blank")&&await h.close();await H.locator("img[class='loading-spinner']").waitFor({state:"detached"}),await $(H),await r(l),await Q(l,n)},metamaskPage:async({context:o},n)=>{await n(H)},metamask:async({context:o},n)=>{let r=new V(H);await n(r)},createAnvilNode:async({context:o},n,r)=>{let i=r.workerIndex,s;await n(async c=>{s=se.define({instance:ie.anvil(c)});let a=await s.start(i),p=`http://${a.host}:${a.port}`,m=c?.chainId??31337;return{rpcUrl:p,anvil:a,chainId:m}}),s&&await s.stop(i)},connectToAnvil:async({context:o,metamask:n,createAnvilNode:r},i)=>{await i(async()=>{let{chainId:s,rpcUrl:c}=await r({chainId:2251});await n.addCustomNetwork({chainId:s,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:c})})}});import{test as de}from"@playwright/test";import{Instance as ue,Pool as me}from"prool";import _t from"fs";import ce from"path";import{chromium as le}from"@playwright/test";async function Ft({workerInfo:t,profileName:e,slowMo:o}){let n=new A,r=await v(t.workerIndex.toString()),i=x(n.name),s=ce.resolve(i,e??"wallet-data");if(!_t.existsSync(s))throw new Error(`Cache for ${n.name} does not exist. Create it first!`);_t.cpSync(s,r,{recursive:!0,force:!0});let c=await F(n.name),a=await le.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${c}`],slowMo:process.env.HEADLESS?0:o}),p=await n.indexUrl();await a.waitForEvent("page",{predicate:m=>m.url().startsWith(p),timeout:4e4});let l=a.pages().find(m=>m.url().startsWith(p));l||(l=await a.newPage(),await l.goto(p),await R(l));for(let m of a.pages())m.url().includes("about:blank")&&await m.close();return{context:a,walletPage:l,contextPath:r}}var nr=({profileName:t,slowMo:e}={})=>de.extend({workerScopeContents:[async({browser:o},n,r)=>{let{context:i,contextPath:s,walletPage:c}=await Ft({workerInfo:r,profileName:t,slowMo:e});await i.grantPermissions(["clipboard-read"]);let a=new V(c);await a.unlock(),await n({wallet:a,walletPage:c,context:i}),await Q(i,s)},{scope:"worker"}],createAnvilNode:async({context:o},n,r)=>{let i=r.workerIndex,s;await n(async c=>{s=me.define({instance:ue.anvil(c)});let a=await s.start(i),p=`http://${a.host}:${a.port}`,m=c?.chainId??31337;return{rpcUrl:p,anvil:a,chainId:m}}),s&&await s.stop(i)},connectToAnvil:async({context:o,createAnvilNode:n,workerScopeContents:r},i)=>{await i(async()=>{let{wallet:s}=r,{chainId:c,rpcUrl:a}=await n({chainId:2251});await s.addCustomNetwork({chainId:c,currencySymbol:"ETH",networkName:"Anvil Localnet",rpcUrl:a})})}});export{V as Metamask,Va as metamaskFixture,nr as metamaskWorkerScopeFixture,$e as workerScopeContext};
|
package/package.json
CHANGED