chainwright 0.9.9 → 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};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import tt from"fs";import yt from"path";import{chromium as Pt}from"@playwright/test";import ht from"path";var Y=".wallet-cache",Q=".wallet-context";var J="13.33.0",M="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Ot=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Rt=`${M}solflare-wallet-extension-v2.19.1.zip`,Ut=`${M}petra-wallet-extension-v2.4.8.zip`,$t=`${M}phantom-wallet-extension-v26.10.0.zip`,Vt=`${M}meteor-wallet-extension-v0.7.0.zip`,Kt=`${M}keplr-wallet-extension-v0.13.3.zip`;async function
|
|
2
|
-
Already on ${o}, no need to switch network.`),"Exit";await
|
|
3
|
-
Switching to the ${o} account aborted. Account is already selected.`);return}await t.locator(u.openSidebarMenuButton).click();let p=await t.locator("div:has(div > button[type='button'][aria-label='Close'])").nth(-2).locator("div").nth(2).locator("> div").nth(1).locator("div").nth(1).locator("div > h2").all(),s=null;for(let d of p)if((await d.textContent())?.toLowerCase()===o.toLowerCase()){s=d;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function nt(t,o){await f(t),o&&await C(t,o);let
|
|
4
|
-
`))}async function R({page:t,network:o,accountName:
|
|
5
|
-
Meteor onboarding started...`,{validateStream:!1}));let c=await F("meteor"),l=await new g().indexUrl();await t.goto(l);let p=t.locator(m.switchNetworkButton),s=await p.textContent(),d=o.split("net")[0]?.toLowerCase()??"";s?.toLowerCase().includes(d)||(await p.click(),await t.locator("div[role='menu']").locator(`> button:has-text('${o}')`).click());let P=t.locator("input[placeholder='Enter Password']"),
|
|
6
|
-
Retrying search for account. ${y} attempts left`),await
|
|
7
|
-
`),{validateStream:!1}))}await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click();let j=t.locator("section[role='dialog']").locator("button:has-text('Close')").first();if(await j.isVisible().then(()=>!0).catch(()=>!1)&&await j.click(),await B({page:t,newAccountName:
|
|
1
|
+
import tt from"fs";import yt from"path";import{chromium as Pt}from"@playwright/test";import ht from"path";var Y=".wallet-cache",Q=".wallet-context";var J="13.33.0",M="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Ot=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,Rt=`${M}solflare-wallet-extension-v2.19.1.zip`,Ut=`${M}petra-wallet-extension-v2.4.8.zip`,$t=`${M}phantom-wallet-extension-v26.10.0.zip`,Vt=`${M}meteor-wallet-extension-v0.7.0.zip`,Kt=`${M}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return ht.resolve(process.cwd(),Q,t)}import xt from"path";function h(t){return xt.resolve(process.cwd(),Y,t)}import Z from"fs";import gt from"path";async function N(t){try{let o=h(t),r=gt.resolve(o,"extension-path.txt");if(!Z.existsSync(r))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let e=Z.readFileSync(r,"utf-8").trim();if(!e)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return e}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function ot({wallet:t,workerInfo:o,profileName:r,slowMo:e}){let a=await T(o.workerIndex.toString()),c=h(t.name),w=yt.resolve(c,r??"wallet-data");if(!tt.existsSync(w))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);tt.cpSync(w,a,{recursive:!0,force:!0});let l=await N(t.name),p=await Pt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:e}),s=await t.indexUrl(),d=p.pages()[0];return d||(d=await p.newPage()),await d.goto(s),{context:p,walletPage:d,contextPath:a}}var u={openSidebarMenuButton:"button[aria-label='open sidebar']",addWalletButton:"button:has-text('Add Wallet')",settingsButton:"button:has-text('Settings')",settingsMenuBackButton:"button[aria-label='Back']"};var m={importExistingWalletButton:'button:has-text("Import an existing wallet")',switchNetworkButton:"button[id^='menu-button']",privateKeyButton:"button:has-text('Private Key')",secretPhraseButton:"button:has-text('Secret Phrase')",findMyAccountButton:"button:has-text('Find my account')"};async function _(t,o,r){let e=t.locator(m.switchNetworkButton).last();await e.scrollIntoViewIfNeeded();let a=await e.textContent(),c=o.split("net")[0]?.toLowerCase()??"";if(a?.toLowerCase()===c)return console.info(`
|
|
2
|
+
Already on ${o}, no need to switch network.`),"Exit";await e.click();let l=t.locator(r).last().locator(`> button:has-text('${o}')`);await l.click(),await t.locator("div > h2:has-text('Meteor Community')").isVisible().catch(()=>!1)&&await l.click()}async function B({page:t,newAccountName:o}){await t.locator(u.openSidebarMenuButton).click(),await t.locator("div:has(> h2):has(> svg)").click();let a=t.locator("input[placeholder='Ex. My Meteor Wallet']"),c=t.locator("button[type='submit']:has-text('Update')");await a.fill(o),await c.click(),await t.locator("div[id='root'] button[aria-label='Close']").click()}async function I({page:t,accountName:o,network:r,...e}){await t.locator(u.openSidebarMenuButton).click(),await t.locator(u.addWalletButton).click(),await _(t,r,"section[role='dialog'] div[role='menu']"),await t.locator(m.importExistingWalletButton).click();let l=t.locator('button:has-text("Continue")');e.mode==="secretPhrase"&&(await t.locator(m.secretPhraseButton).click(),await l.scrollIntoViewIfNeeded(),await l.click(),await t.locator("textarea:not([disabled])").fill(e.secretPhrase)),e.mode==="privateKey"&&(await t.locator(m.privateKeyButton).click(),await l.scrollIntoViewIfNeeded(),await l.click(),await t.locator("textarea:not([disabled])").fill(e.privateKey)),await t.locator(m.findMyAccountButton).click(),await t.locator("button[type='submit'][data-loading]").waitFor({state:"detached",timeout:25e3}),await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click(),await B({page:t,newAccountName:o})}var b={approveButton:"button:has-text('Approve')",connectButton:"button:has-text('Connect')",cancelButton:"button:has-text('Cancel')",logoutButton:"button:has-text('Logout')"};import et from"fs";import bt from"path";async function F(t){let o=h(t),r=bt.resolve(o,"password.txt");try{if(!et.existsSync(r))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(r,"utf-8")}catch(e){throw new Error(`\u274C Failed to get ${t} password from cache: ${e.message}`)}}async function f(t){let o=await F("meteor"),r=t.locator("input[placeholder='Enter Password']"),e=t.locator('button:has-text("Unlock")');if(!await r.waitFor({state:"visible",timeout:5e3}).then(()=>!0).catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await r.fill(o),await e.click()}async function rt(t){await f(t),await t.locator(b.logoutButton).click()}async function at(t){await f(t),await t.locator(b.approveButton).click(),await t.locator("h2:has-text('Executing Transaction')").waitFor({state:"attached"}),await t.waitForEvent("close",{timeout:15e3})}async function C(t,o){if((await t.locator("div:has(button[type='button'][aria-label='open sidebar'])").nth(-2).locator("div:has(div > h2)").locator("div > h2").textContent())?.toLowerCase()===o.toLowerCase()){console.info(`
|
|
3
|
+
Switching to the ${o} account aborted. Account is already selected.`);return}await t.locator(u.openSidebarMenuButton).click();let p=await t.locator("div:has(div > button[type='button'][aria-label='Close'])").nth(-2).locator("div").nth(2).locator("> div").nth(1).locator("div").nth(1).locator("div > h2").all(),s=null;for(let d of p)if((await d.textContent())?.toLowerCase()===o.toLowerCase()){s=d;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function nt(t,o){await f(t),o&&await C(t,o);let r=t.getByRole("heading",{name:"Connect Request",exact:!0});await Promise.all([t.locator(b.connectButton).click(),r.waitFor({state:"detached",timeout:3e4})])}async function it(t){let e=t.locator("div:has(button[type='button'][aria-label='open sidebar'])").nth(-2).locator("div:has(div > h2)").locator("div > svg"),a=t.locator(".chakra-toast").last();await e.click(),await a.waitFor({state:"visible",timeout:5e3});let c=await t.evaluate(async()=>await navigator.clipboard.readText());return await a.waitFor({state:"hidden",timeout:5e3}),c}async function ct(t){await t.locator(u.openSidebarMenuButton).click(),await t.locator("button:has-text('Lock Wallet')").click()}import{styleText as O}from"util";function E(t){return new Promise(o=>setTimeout(o,t))}import{expect as kt}from"@playwright/test";async function st({context:t,path:o,locator:r}){let e;try{await kt.poll(async()=>(e=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(o)),!!e),{timeout:3e4}).toBe(!0)}catch{let a=t.pages().filter(c=>c.url().startsWith("chrome-extension://")).map(c=>c.url());throw new Error(`Popup page with path "${o}" not found in context after 30s. Pages in context: ${JSON.stringify(a)}`)}if(!e)throw new Error(`Popup page with path ${o} not found in context.`);return await At(e,r),await e.setViewportSize({width:360,height:592}),e}async function At(t,o){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(o).first().waitFor({state:"attached",timeout:4e4})}import lt from"fs";import Bt from"path";async function pt(t){let o=h(t),r=Bt.resolve(o,"extension-id.txt");try{if(!lt.existsSync(r))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return lt.readFileSync(r,"utf-8")}catch(e){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${e.message}`)}}var g=class{name="meteor";onboardingPath="ext_index_popup.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/ext_index_popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/ext_index.html`}async extensionId(){return await pt(this.name)}async promptPage(o){let r=await this.promptUrl();return await st({context:o,path:r,locator:"div[id='root']"})}};async function S(t){await t.locator(u.openSidebarMenuButton).click(),await t.locator(u.settingsButton).click()}async function L(t,o){if(await S(t),await _(t,o,"div[role='menu']")==="Exit"){let c=t.locator(u.settingsMenuBackButton);await c.scrollIntoViewIfNeeded(),await c.click();return}let e=t.locator("p:has-text('Available Balance')");if(await E(1e3),!await e.isVisible().catch(()=>!1))throw new Error([`There is no associated account for the ${o} network in your wallet.`,`Please add an account to the ${o} network in your wallet using the "addAccount" method.`,"NOTE: For the account to be persisted across tests, do this when onboarding the wallet."].join(`
|
|
4
|
+
`))}async function R({page:t,network:o,accountName:r,additionalAccounts:e,...a}){console.info(O("yellowBright",`
|
|
5
|
+
Meteor onboarding started...`,{validateStream:!1}));let c=await F("meteor"),l=await new g().indexUrl();await t.goto(l);let p=t.locator(m.switchNetworkButton),s=await p.textContent(),d=o.split("net")[0]?.toLowerCase()??"";s?.toLowerCase().includes(d)||(await p.click(),await t.locator("div[role='menu']").locator(`> button:has-text('${o}')`).click());let P=t.locator("input[placeholder='Enter Password']"),A=t.locator("input[placeholder='Confirm Password']"),K=t.locator("label.chakra-checkbox .chakra-checkbox__control"),x=t.locator('button:has-text("Continue")');await P.fill(c),await A.fill(c),await K.click(),await x.click(),await t.locator(m.importExistingWalletButton).click(),a.mode==="secretPhrase"&&(await t.locator(m.secretPhraseButton).click(),await x.scrollIntoViewIfNeeded(),await x.click(),await t.locator("textarea:not([disabled])").fill(a.secretPhrase)),a.mode==="privateKey"&&(await t.locator(m.privateKeyButton).click(),await x.scrollIntoViewIfNeeded(),await x.click(),await t.locator("textarea:not([disabled])").fill(a.privateKey));let H=t.locator(m.findMyAccountButton);await H.click();let z=t.locator("button[type='submit'][data-loading]");if(await z.waitFor({state:"detached",timeout:25e3}),await t.getByRole("status").locator("div[id='toast-1-title']:has-text('No Account Found')").isVisible().catch(()=>!1)){let y=5,k=!1;for(;y>0;){if(console.info(`
|
|
6
|
+
Retrying search for account. ${y} attempts left`),await E(15e3),await H.click(),await z.waitFor({state:"detached",timeout:2e4}),await t.locator("div:has-text('Import Your Account')").nth(-2).locator("button").isVisible().catch(()=>!1)){k=!0;break}y-=1}if(!k)throw Error(O("redBright",["No Account Found","Account associated with the private key not found. Please make sure you are trying to import an account on the correct network(Mainnet/Testnet)."].join(`
|
|
7
|
+
`),{validateStream:!1}))}await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click();let j=t.locator("section[role='dialog']").locator("button:has-text('Close')").first();if(await j.isVisible().then(()=>!0).catch(()=>!1)&&await j.click(),await B({page:t,newAccountName:r}),e&&e.length>0){for(let{accountName:q,network:G,...ft}of e)await I({page:t,accountName:q,network:G,...ft});await S(t);let y=t.locator(m.switchNetworkButton).last();await y.scrollIntoViewIfNeeded();let k=await y.textContent(),X=o.split("net")[0]?.toLowerCase()??"";k?.toLowerCase()!==X&&await L(t,o),await C(t,r)}await E(3e3),console.info(O("greenBright","\u2728 Meteor onboarding completed successfully"))}async function ut(t){await f(t),await t.locator(b.cancelButton).click()}var v=class extends g{page;constructor(o){super(),this.page=o}async onboard({network:o,accountName:r,additionalAccounts:e,...a}){await R({page:this.page,network:o,accountName:r,additionalAccounts:e,...a})}async unlock(){await f(this.page)}async lock(){await ct(this.page)}async renameAccount({newAccountName:o}){await B({page:this.page,newAccountName:o})}async switchNetwork(o){await L(this.page,o)}async switchAccount(o){await C(this.page,o)}async getAccountAddress(){return await it(this.page)}async addAccount({accountName:o,network:r,...e}){await I({page:this.page,accountName:o,network:r,...e})}async openSettings(){await S(this.page)}async connectToApp(o){await nt(await this.promptPage(this.page.context()),o)}async confirmDisconnect(){await rt(await this.promptPage(this.page.context()))}async confirmTransaction(){await at(await this.promptPage(this.page.context()))}async rejectTransaction(){await ut(await this.promptPage(this.page.context()))}};import wt from"fs";import Mt from"path";import{test as Tt,chromium as Et}from"@playwright/test";import{expect as Ct}from"@playwright/test";async function U(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function $(t,o){let r=await t.newPage();return await Ct(async()=>{await r.goto(o),await U(r)}).toPass(),r}async function V(t,o){let r=await o.newPage();for(let{origin:e,localStorage:a}of t){let c=r.mainFrame();await c.goto(e),await c.evaluate(w=>{w.forEach(({name:l,value:p})=>{window.localStorage.setItem(l,p)})},a)}await r.close()}import St from"fs/promises";async function mt(t){await St.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var vt=35e3;async function D(t,o){try{await Promise.race([t.close(),new Promise((r,e)=>setTimeout(()=>e(new Error("Context close timed out")),vt))])}catch(r){console.warn(`Browser context close did not complete cleanly: ${r.message}`)}try{await mt(o)}catch(r){console.error(`Failed to remove temporary context directory at ${o}. Error:`,r)}}var W,Ir=({slowMo:t=0,profileName:o}={})=>Tt.extend({contextPath:async({browserName:r},e,a)=>{let c=await T(`${r}-${a.testId}`);await e(c)},context:async({context:r,contextPath:e},a)=>{let c=new g,w=h(c.name),l=await N(c.name),p=Mt.resolve(w,o??"wallet-data");if(!wt.existsSync(p))throw new Error("\u274C Cache for Meteor wallet data not found. Create it first");wt.cpSync(p,e,{recursive:!0,force:!0}),process.env.HEADLESS&&t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!");let s=await Et.launchPersistentContext(e,{headless:!1,args:[`--disable-extensions-except=${l}`,`--load-extension=${l}`],slowMo:process.env.HEADLESS?0:t});await s.grantPermissions(["clipboard-read"]);let{cookies:d,origins:P}=await r.storageState();d&&await s.addCookies(d),P&&P.length>0&&V(P,s);let A=await c.indexUrl();W=s.pages().find(x=>x.url().startsWith(A))||await $(s,A);for(let x of s.pages())x.url().includes("about:blank")&&await x.close();await W.bringToFront(),await f(W),await a(s),await D(s,e)},meteorPage:async({context:r},e)=>{await e(W)},meteor:async({context:r},e)=>{let a=new v(W);await e(a)}});import{test as Wt}from"@playwright/test";var Vr=({slowMo:t,profileName:o}={})=>Wt.extend({workerScopeContents:[async({browser:r},e,a)=>{let{context:c,contextPath:w,walletPage:l}=await ot({workerInfo:a,profileName:o,slowMo:t,wallet:new g});await c.grantPermissions(["clipboard-read"]);for(let s of c.pages())s.url().includes("about:blank")&&await s.close();let p=new v(l);await p.unlock(),await e({wallet:p,walletPage:l,context:c}),await D(c,w)},{scope:"worker"}]});export{v as Meteor,Ir as meteorFixture,Vr as meteorWorkerScopeFixture,ot as workerScopeContext};
|
package/package.json
CHANGED