chainwright 0.9.4 → 0.9.6

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/README.md CHANGED
@@ -127,6 +127,7 @@ export default defineWalletSetup(
127
127
  await petra.onboard({
128
128
  mode: "importMnemonic",
129
129
  accountName: "default",
130
+ network: "Testnet",
130
131
  secretRecoveryPhrase: "test test test...", // Seed phrase for the main account
131
132
  additionalAccounts: [
132
133
  {
@@ -21,15 +21,18 @@ declare class PetraProfile {
21
21
  type OnboardingArgs = {
22
22
  mode: "create";
23
23
  accountName: string;
24
+ network: SwitchNetwork;
24
25
  additionalAccounts?: Array<AddAccount>;
25
26
  } | {
26
27
  mode: "importMnemonic";
27
28
  accountName: string;
29
+ network: SwitchNetwork;
28
30
  secretRecoveryPhrase: string;
29
31
  additionalAccounts?: Array<AddAccount>;
30
32
  } | {
31
33
  mode: "importPrivateKey";
32
34
  accountName: string;
35
+ network: SwitchNetwork;
33
36
  privateKey: string;
34
37
  additionalAccounts?: Array<AddAccount>;
35
38
  };
@@ -1,3 +1,3 @@
1
- import J from"fs";import yt from"path";import{chromium as gt}from"@playwright/test";import ft from"path";var G=".wallet-cache",X=".wallet-context";var j="13.33.0",W="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Nt=`https://github.com/MetaMask/metamask-extension/releases/download/v${j}/metamask-chrome-${j}.zip`,Rt=`${W}solflare-wallet-extension-v2.19.1.zip`,Ut=`${W}petra-wallet-extension-v2.4.8.zip`,$t=`${W}phantom-wallet-extension-v26.10.0.zip`,Vt=`${W}meteor-wallet-extension-v0.7.0.zip`,Kt=`${W}keplr-wallet-extension-v0.13.3.zip`;async function I(t){return ft.resolve(process.cwd(),X,t)}import xt from"path";function x(t){return xt.resolve(process.cwd(),G,t)}import Z from"fs";import ht from"path";async function O(t){try{let o=x(t),e=ht.resolve(o,"extension-path.txt");if(!Z.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let r=Z.readFileSync(e,"utf-8").trim();if(!r)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return r}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function Y({wallet:t,workerInfo:o,profileName:e,slowMo:r}){let a=await I(o.workerIndex.toString()),c=x(t.name),s=yt.resolve(c,e??"wallet-data");if(!J.existsSync(s))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);J.cpSync(s,a,{recursive:!0,force:!0});let p=await O(t.name),u=await gt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:r}),d=await t.indexUrl(),l=u.pages()[0];return l||(l=await u.newPage()),await l.goto(d),{context:u,walletPage:l,contextPath:a}}import Bt from"zod";var m={depositButton:"button:has-text('Deposit')",sendButton:"button:has-text('Send')",receiveButton:"button:has-text('Receive')",settingsMenu:"button[aria-label='Settings']",lockButton:"global-menu-lock",accountMenuButton:"button[data-part='trigger']",accountDialog:"div[role='dialog']",backButton:"button[id='back-button']"},$={networkSection:"a[href='/settings/network']",backButton:"button[id='back-button']"},V={passwordInput:"input[name='password']",unlockButton:"button:has-text('Unlock')"},h={accountOptionsMenuButton:"button[data-scope='popover']",editAccountButton:"button[aria-label='Edit account name']",renameAccountInput:"input[name='name']",saveButton:"button:has-text('Save')",cancelButton:"button:has-text('Cancel')",addAccountButton:"button:has-text('Add accounts')",addAccountWithPrivateKeyButton:"button:has-text('Import private key')",addAccountWithMnemonicButton:"button:has-text('Import mnemonic')"};var w={createWalletButton:"button:has-text('Create an account')",createSeedPhraseButton:"button:has-text('Create a seed phrase wallet')",createNewPasswordInput:"input[name='password']",confirmNewPasswordInput:"input[name='confirmPassword']",confirmPasswordCheckbox:"label>div[data-scope='checkbox']",continueButton:"button:has-text('Continue')",skipCopyRecoveryPhraseButton:"button:has-text('Skip')",getStartedButton:"button:has-text('Get started')",onboardingCompleteText:"h1:has-text('Your wallet is ready, you may close this window')",importWalletButton:"button:has-text('Import an account')",importUsingPrivateKeyButton:"button:has-text('Import private key')",importUsingMnemonicButton:"button:has-text('Import mnemonic')",importButton:"button:has-text('Import')",privateKeyInput:"input[name='privateKey']"};import{expect as _}from"@playwright/test";import Pt from"zod";async function A({page:t,newAccountName:o}){let e=Pt.string().min(1,"Account name cannot be an empty string").parse(o);await t.locator(m.settingsMenu).click(),await _(t.getByText("Settings").first()).toBeVisible();let a=t.locator(h.editAccountButton);await _(a).toBeVisible(),await a.click(),await _(t.getByText("Account name").first()).toBeVisible();let c=t.locator(h.renameAccountInput);if(await c.getAttribute("value")===e)throw Error(`The account to be renamed "${e}" already exists.`);await c.fill(e);let p=t.locator(h.saveButton);await _(p).toBeEnabled(),await p.click(),await _(t.getByText(e).first()).toBeVisible(),await t.locator(m.backButton).click(),await Promise.allSettled([t.locator(m.depositButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4})])}async function L({page:t,accountName:o,mode:e,...r}){let a=Bt.string().max(14,"For switching accounts reason, account name should not be longer than 14 characters. The reason for this is because the name will be truncated. Hence, it will be difficult to select the account.").parse(o);if(await t.locator(h.accountOptionsMenuButton).first().click(),await t.getByRole("dialog").locator(h.addAccountButton).click(),e==="privateKey"){let u="privateKey"in r?r.privateKey:"";if(await t.locator(h.addAccountWithPrivateKeyButton).click(),await t.locator(w.privateKeyInput).fill(u),await t.locator(w.importButton).click(),(await t.getByRole("status").locator("div[data-part='description']",{hasText:"Account already exists in wallet"}).textContent({timeout:3e3}).catch(()=>null))?.includes("Account already exists in wallet"))throw Error(`Account ${a} already exists in wallet`);await A({page:t,newAccountName:a})}if(e==="mnemonic"){let d=("mnemonicPhrase"in r?r.mnemonicPhrase:"").split(" ");await t.locator(h.addAccountWithMnemonicButton).click();for(let[y,v]of d.entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+y)}"]`).fill(v);if(await t.locator(w.continueButton).click(),(await t.getByRole("status").locator("div[data-part='description']",{hasText:"Account already exists in wallet"}).textContent({timeout:3e3}).catch(()=>null))?.includes("Account already exists in wallet"))throw Error(`Account ${a} already exists in wallet`);await A({page:t,newAccountName:a})}}function N(t){return new Promise(o=>setTimeout(o,t))}var S={approveButton:'button:has-text("Approve")',cancelButton:'button:has-text("Cancel")'};var bt=/^[A-Z0-9]+(?:_[A-Z0-9]+)+$/;function q(t){return bt.test(t)?t.toLowerCase().replace(/_/g," ").replace(/\b[a-z]/g,o=>o.toUpperCase()):t}async function At(t,o){for(;;){let r=o();if(r||t.isClosed())break;try{let a=t.locator("div:has(> h2:has-text('Simulation error'))");if(await a.isVisible().catch(()=>!1)){let s=await a.locator("p").textContent();throw new Error(`[Confirm Transaction Error]: ${q(s||"Unexpected error!")}`)}}catch(a){if(t.isClosed())break;throw a instanceof Error?a:new Error(`[Confirm Transaction Error]: ${a}`)}if(r||t.isClosed())break;await N(300)}}async function Q(t){let o=!1;At(t,()=>o).catch(async a=>{t.isClosed()||console.error(a.message)}),await t.locator(S.approveButton).click(),o=!0}import{expect as kt}from"@playwright/test";async function C(t,o){let e=t.locator(h.accountOptionsMenuButton).first();if((await e.textContent())?.split("Switch wallet")[1]?.split("0x")[0]?.toLowerCase().trim().includes(o.toLowerCase().trim())){console.info(`
2
- Switching to ${o} account aborted because the account is already selected.`);return}await e.click();let c=await t.getByRole("dialog").locator("> div > div > button[type='button']").all(),s=null;for(let p of c)if((await p.textContent())?.toLowerCase()?.trim().includes(o.toLowerCase().trim())){s=p;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function tt(t,o){o&&await C(t,o);let e=t.locator(S.approveButton);await kt(e).toBeEnabled({timeout:2e4}),await e.click()}async function ot(t){return await t.getByRole("button",{name:"Copy Address",exact:!0}).click(),await t.evaluate(async()=>await navigator.clipboard.readText())}import{expect as et}from"@playwright/test";async function rt(t){let o=t.locator(m.settingsMenu);await et(o).toBeVisible(),await o.click(),await et(t.getByText("Settings").first()).toBeVisible()}async function at(t){await rt(t),await t.getByRole("button",{name:/lock wallet/i}).click(),await t.getByRole("heading",{name:/welcome/i}).waitFor({state:"visible",timeout:2e4})}import{styleText as lt}from"util";import{expect as B}from"@playwright/test";async function F(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import nt from"fs";import St from"path";async function R(t){let o=x(t),e=St.resolve(o,"password.txt");try{if(!nt.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return nt.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} password from cache: ${r.message}`)}}import{expect as Ct}from"@playwright/test";async function it({context:t,path:o,locator:e}){let r;try{await Ct.poll(async()=>(r=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(o)),!!r),{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(!r)throw new Error(`Popup page with path ${o} not found in context.`);return await Et(r,e),await r.setViewportSize({width:360,height:592}),r}async function Et(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 ct from"fs";import Tt from"path";async function st(t){let o=x(t),e=Tt.resolve(o,"extension-id.txt");try{if(!ct.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ct.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${r.message}`)}}var g=class{name="petra";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/index.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/prompt.html`}async extensionId(){return await st(this.name)}async promptPage(o){let e=await this.promptUrl();return await it({context:o,path:e,locator:"div[id='prompt']"})}};var E=3e4;async function K({page:t,...o}){console.info(lt("yellowBright",`
3
- Petra onboarding started...`,{validateStream:!1}));let e=new g,r=await R("petra"),a=t.locator(w.createWalletButton),c=t.locator(w.importWalletButton),s=t.locator(w.createNewPasswordInput),p=t.locator(w.confirmNewPasswordInput),u=t.locator(w.confirmPasswordCheckbox),d=t.locator(w.continueButton),l=t.locator(w.getStartedButton),b=t.locator(w.onboardingCompleteText);if(o.mode==="create"){let f=t.locator(w.createSeedPhraseButton);await a.click(),await f.click(),await s.fill(r),await p.fill(r),await u.click(),await d.click(),await t.locator(w.skipCopyRecoveryPhraseButton).click(),await l.click(),await B(b).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await B(t.locator(m.depositButton)).toBeVisible({timeout:E}),await B(t.locator(m.sendButton)).toBeVisible({timeout:E})}if(o.mode==="importPrivateKey"){let{privateKey:f}=o,P=t.locator(w.importUsingPrivateKeyButton),k=t.locator(w.privateKeyInput),y=t.locator(w.importButton);await c.click(),await P.click(),await k.fill(f),await y.click(),await s.fill(r),await p.fill(r),await u.click(),await d.click(),await l.click(),await B(b).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await F(t),await B(t.locator(m.depositButton)).toBeVisible({timeout:E}),await B(t.locator(m.sendButton)).toBeVisible({timeout:E})}if(o.mode==="importMnemonic"){let{secretRecoveryPhrase:f}=o,P=t.locator(w.importUsingMnemonicButton);await c.click(),await P.click();for(let[k,y]of f.split(" ").entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+k)}"]`).fill(y);await d.click(),await s.fill(r),await p.fill(r),await u.click(),await d.click(),await l.click(),await B(b).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await B(t.locator(m.depositButton)).toBeVisible({timeout:E}),await B(t.locator(m.sendButton)).toBeVisible({timeout:E})}if(await A({page:t,newAccountName:o.accountName}),o.additionalAccounts&&o.additionalAccounts.length>0){for(let{...f}of o.additionalAccounts)await L({page:t,...f});await C(t,o.accountName)}await N(1500),console.info(lt("greenBright","\u2728 Petra onboarding completed successfully",{validateStream:!1}))}import{expect as vt}from"@playwright/test";async function pt(t){let o=t.locator(S.cancelButton);await vt(o).toBeEnabled(),await o.click()}async function ut(t,o){await t.locator(m.settingsMenu).click(),await t.locator($.networkSection).click(),await t.locator(`div:has(> span:has-text("${o}"))`).first().click();let c=t.locator($.backButton);await c.click(),await c.click()}import{expect as mt}from"@playwright/test";async function M(t){let o=await R("petra"),e=t.locator(V.passwordInput);if(!await e.isVisible().then(()=>!0).catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await mt(e).toBeVisible({timeout:15e3}),await e.fill(o);let a=t.locator(V.unlockButton);await mt(a).toBeEnabled(),await a.click(),await Promise.allSettled([t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.receiveButton).waitFor({state:"visible",timeout:2e4})])}var T=class extends g{page;constructor(o){super(),this.page=o}async onboard(o){await K({page:this.page,...o})}async unlock(){await M(this.page)}async lock(){await at(this.page)}async renameAccount({newAccountName:o}){await A({page:this.page,newAccountName:o})}async switchNetwork(o){await ut(this.page,o)}async switchAccount(o){await C(this.page,o)}async getAccountAddress(){return await ot(this.page)}async addAccount({accountName:o,...e}){await L({page:this.page,accountName:o,...e})}async connectToApp(o){await tt(await this.promptPage(this.page.context()),o)}async confirmTransaction(){await Q(await this.promptPage(this.page.context()))}async rejectTransaction(){await pt(await this.promptPage(this.page.context()))}};import dt from"fs";import Ft from"path";import{test as Mt,chromium as Dt}from"@playwright/test";import{expect as Wt}from"@playwright/test";async function z(t,o){let e=await t.newPage();return await Wt(async()=>{await e.goto(o),await F(e)}).toPass(),e}async function H(t,o){let e=await o.newPage();for(let{origin:r,localStorage:a}of t){let c=e.mainFrame();await c.goto(r),await c.evaluate(s=>{s.forEach(({name:p,value:u})=>{window.localStorage.setItem(p,u)})},a)}await e.close()}import It from"fs/promises";async function wt(t){await It.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var _t=35e3;async function U(t,o){try{await Promise.race([t.close(),new Promise((e,r)=>setTimeout(()=>r(new Error("Context close timed out")),_t))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await wt(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}var D,Ir=({slowMo:t=0,profileName:o}={})=>Mt.extend({contextPath:async({browserName:e},r,a)=>{let c=await I(`${e}-${a.testId}`);await r(c)},context:async({context:e,contextPath:r},a)=>{let c=new g,s=x(c.name),p=await O(c.name),u=Ft.resolve(s,o??"wallet-data");if(!dt.existsSync(u))throw new Error("\u274C Cache for Petra wallet data not found. Create it first");dt.cpSync(u,r,{recursive:!0,force:!0});let d=[`--disable-extensions-except=${p}`,`--load-extension=${p}`];process.env.HEADLESS&&(d.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await Dt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:b,origins:f}=await e.storageState();b&&await l.addCookies(b),f&&f.length>0&&H(f,l);let P=await c.indexUrl();D=l.pages().find(y=>y.url().startsWith(P))||await z(l,P);for(let y of l.pages()){let v=y.url();(v.includes("about:blank")||v.includes(c.onboardingPath))&&await y.close()}await D.bringToFront(),await M(D),await a(l),await U(l,r)},petraPage:async({context:e},r)=>{await r(D)},petra:async({context:e},r)=>{let a=new T(D);await r(a)}});import{test as Ot}from"@playwright/test";var Rr=({slowMo:t,profileName:o}={})=>Ot.extend({workerScopeContents:[async({browser:e},r,a)=>{let c=new g,{context:s,contextPath:p,walletPage:u}=await Y({wallet:c,workerInfo:a,profileName:o,slowMo:t});await s.grantPermissions(["clipboard-read"]);for(let l of s.pages())l.url().includes(c.onboardingPath)&&await l.close();let d=new T(u);await d.unlock(),await r({wallet:d,walletPage:u,context:s}),await U(s,p)},{scope:"worker"}]});export{T as Petra,Ir as petraFixture,Rr as petraWorkerScopeFixture,Y as workerScopeContext};
1
+ import Y from"fs";import gt from"path";import{chromium as Pt}from"@playwright/test";import xt from"path";var X=".wallet-cache",Z=".wallet-context";var G="13.33.0",W="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Nt=`https://github.com/MetaMask/metamask-extension/releases/download/v${G}/metamask-chrome-${G}.zip`,Rt=`${W}solflare-wallet-extension-v2.19.1.zip`,Ut=`${W}petra-wallet-extension-v2.4.8.zip`,$t=`${W}phantom-wallet-extension-v26.10.0.zip`,Vt=`${W}meteor-wallet-extension-v0.7.0.zip`,Kt=`${W}keplr-wallet-extension-v0.13.3.zip`;async function I(t){return xt.resolve(process.cwd(),Z,t)}import ht from"path";function x(t){return ht.resolve(process.cwd(),X,t)}import J from"fs";import yt from"path";async function O(t){try{let o=x(t),e=yt.resolve(o,"extension-path.txt");if(!J.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let r=J.readFileSync(e,"utf-8").trim();if(!r)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return r}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}async function q({wallet:t,workerInfo:o,profileName:e,slowMo:r}){let a=await I(o.workerIndex.toString()),c=x(t.name),s=gt.resolve(c,e??"wallet-data");if(!Y.existsSync(s))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);Y.cpSync(s,a,{recursive:!0,force:!0});let p=await O(t.name),u=await Pt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:r}),d=await t.indexUrl(),l=u.pages()[0];return l||(l=await u.newPage()),await l.goto(d),{context:u,walletPage:l,contextPath:a}}import bt from"zod";var m={depositButton:"button:has-text('Deposit')",sendButton:"button:has-text('Send')",receiveButton:"button:has-text('Receive')",settingsMenu:"button[aria-label='Settings']",lockButton:"global-menu-lock",accountMenuButton:"button[data-part='trigger']",accountDialog:"div[role='dialog']",backButton:"button[id='back-button']"},V={networkSection:"a[href='/settings/network']",backButton:"button[id='back-button']"},K={passwordInput:"input[name='password']",unlockButton:"button:has-text('Unlock')"},h={accountOptionsMenuButton:"button[data-scope='popover']",editAccountButton:"button[aria-label='Edit account name']",renameAccountInput:"input[name='name']",saveButton:"button:has-text('Save')",cancelButton:"button:has-text('Cancel')",addAccountButton:"button:has-text('Add accounts')",addAccountWithPrivateKeyButton:"button:has-text('Import private key')",addAccountWithMnemonicButton:"button:has-text('Import mnemonic')"};var w={createWalletButton:"button:has-text('Create an account')",createSeedPhraseButton:"button:has-text('Create a seed phrase wallet')",createNewPasswordInput:"input[name='password']",confirmNewPasswordInput:"input[name='confirmPassword']",confirmPasswordCheckbox:"label>div[data-scope='checkbox']",continueButton:"button:has-text('Continue')",skipCopyRecoveryPhraseButton:"button:has-text('Skip')",getStartedButton:"button:has-text('Get started')",onboardingCompleteText:"h1:has-text('Your wallet is ready, you may close this window')",importWalletButton:"button:has-text('Import an account')",importUsingPrivateKeyButton:"button:has-text('Import private key')",importUsingMnemonicButton:"button:has-text('Import mnemonic')",importButton:"button:has-text('Import')",privateKeyInput:"input[name='privateKey']"};import{expect as _}from"@playwright/test";import Bt from"zod";async function S({page:t,newAccountName:o}){let e=Bt.string().min(1,"Account name cannot be an empty string").parse(o);await t.locator(m.settingsMenu).click(),await _(t.getByText("Settings").first()).toBeVisible();let a=t.locator(h.editAccountButton);await _(a).toBeVisible(),await a.click(),await _(t.getByText("Account name").first()).toBeVisible();let c=t.locator(h.renameAccountInput);if(await c.getAttribute("value")===e)throw Error(`The account to be renamed "${e}" already exists.`);await c.fill(e);let p=t.locator(h.saveButton);await _(p).toBeEnabled(),await p.click(),await _(t.getByText(e).first()).toBeVisible(),await t.locator(m.backButton).click(),await Promise.allSettled([t.locator(m.depositButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4})])}async function L({page:t,accountName:o,mode:e,...r}){let a=bt.string().max(14,"For switching accounts reason, account name should not be longer than 14 characters. The reason for this is because the name will be truncated. Hence, it will be difficult to select the account.").parse(o);if(await t.locator(h.accountOptionsMenuButton).first().click(),await t.getByRole("dialog").locator(h.addAccountButton).click(),e==="privateKey"){let u="privateKey"in r?r.privateKey:"";if(await t.locator(h.addAccountWithPrivateKeyButton).click(),await t.locator(w.privateKeyInput).fill(u),await t.locator(w.importButton).click(),(await t.getByRole("status").locator("div[data-part='description']",{hasText:"Account already exists in wallet"}).textContent({timeout:3e3}).catch(()=>null))?.includes("Account already exists in wallet"))throw Error(`Account ${a} already exists in wallet`);await S({page:t,newAccountName:a})}if(e==="mnemonic"){let d=("mnemonicPhrase"in r?r.mnemonicPhrase:"").split(" ");await t.locator(h.addAccountWithMnemonicButton).click();for(let[y,k]of d.entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+y)}"]`).fill(k);if(await t.locator(w.continueButton).click(),(await t.getByRole("status").locator("div[data-part='description']",{hasText:"Account already exists in wallet"}).textContent({timeout:3e3}).catch(()=>null))?.includes("Account already exists in wallet"))throw Error(`Account ${a} already exists in wallet`);await S({page:t,newAccountName:a})}}function N(t){return new Promise(o=>setTimeout(o,t))}var C={approveButton:'button:has-text("Approve")',cancelButton:'button:has-text("Cancel")'};var kt=/^[A-Z0-9]+(?:_[A-Z0-9]+)+$/;function Q(t){return kt.test(t)?t.toLowerCase().replace(/_/g," ").replace(/\b[a-z]/g,o=>o.toUpperCase()):t}async function At(t,o){for(;;){let r=o();if(r||t.isClosed())break;try{let a=t.locator("div:has(> h2:has-text('Simulation error'))");if(await a.isVisible().catch(()=>!1)){let s=await a.locator("p").textContent();throw new Error(`[Confirm Transaction Error]: ${Q(s||"Unexpected error!")}`)}}catch(a){if(t.isClosed())break;throw a instanceof Error?a:new Error(`[Confirm Transaction Error]: ${a}`)}if(r||t.isClosed())break;await N(300)}}async function tt(t){let o=!1;At(t,()=>o).catch(async a=>{t.isClosed()||console.error(a.message)}),await t.locator(C.approveButton).click(),o=!0}import{expect as St}from"@playwright/test";async function E(t,o){let e=t.locator(h.accountOptionsMenuButton).first();if((await e.textContent())?.split("Switch wallet")[1]?.split("0x")[0]?.toLowerCase().trim().includes(o.toLowerCase().trim())){console.info(`
2
+ Switching to ${o} account aborted because the account is already selected.`);return}await e.click();let c=await t.getByRole("dialog").locator("> div > div > button[type='button']").all(),s=null;for(let p of c)if((await p.textContent())?.toLowerCase()?.trim().includes(o.toLowerCase().trim())){s=p;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function ot(t,o){o&&await E(t,o);let e=t.locator(C.approveButton);await St(e).toBeEnabled({timeout:2e4}),await e.click()}async function et(t){return await t.getByRole("button",{name:"Copy Address",exact:!0}).click(),await t.evaluate(async()=>await navigator.clipboard.readText())}import{expect as rt}from"@playwright/test";async function at(t){let o=t.locator(m.settingsMenu);await rt(o).toBeVisible(),await o.click(),await rt(t.getByText("Settings").first()).toBeVisible()}async function nt(t){await at(t),await t.getByRole("button",{name:/lock wallet/i}).click(),await t.getByRole("heading",{name:/welcome/i}).waitFor({state:"visible",timeout:2e4})}import{styleText as pt}from"util";import{expect as B}from"@playwright/test";async function F(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import it from"fs";import Ct from"path";async function R(t){let o=x(t),e=Ct.resolve(o,"password.txt");try{if(!it.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return it.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} password from cache: ${r.message}`)}}import{expect as Et}from"@playwright/test";async function ct({context:t,path:o,locator:e}){let r;try{await Et.poll(async()=>(r=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(o)),!!r),{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(!r)throw new Error(`Popup page with path ${o} not found in context.`);return await Tt(r,e),await r.setViewportSize({width:360,height:592}),r}async function Tt(t,o){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(o).first().waitFor({state:"attached",timeout:4e4})}import st from"fs";import vt from"path";async function lt(t){let o=x(t),e=vt.resolve(o,"extension-id.txt");try{if(!st.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return st.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${r.message}`)}}var g=class{name="petra";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/index.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/prompt.html`}async extensionId(){return await lt(this.name)}async promptPage(o){let e=await this.promptUrl();return await ct({context:o,path:e,locator:"div[id='prompt']"})}};async function U(t,o){await t.locator(m.settingsMenu).click(),await t.locator(V.networkSection).click(),await t.locator(`div:has(> span:has-text("${o}"))`).first().click();let c=t.locator(V.backButton);await c.click(),await c.click()}var T=3e4;async function z({page:t,network:o,...e}){console.info(pt("yellowBright",`
3
+ Petra onboarding started...`,{validateStream:!1}));let r=new g,a=await R("petra"),c=t.locator(w.createWalletButton),s=t.locator(w.importWalletButton),p=t.locator(w.createNewPasswordInput),u=t.locator(w.confirmNewPasswordInput),d=t.locator(w.confirmPasswordCheckbox),l=t.locator(w.continueButton),b=t.locator(w.getStartedButton),P=t.locator(w.onboardingCompleteText);if(e.mode==="create"){let f=t.locator(w.createSeedPhraseButton);await c.click(),await f.click(),await p.fill(a),await u.fill(a),await d.click(),await l.click(),await t.locator(w.skipCopyRecoveryPhraseButton).click(),await b.click(),await B(P).toBeVisible({timeout:25e3}),await t.goto(await r.indexUrl()),await B(t.locator(m.depositButton)).toBeVisible({timeout:T}),await B(t.locator(m.sendButton)).toBeVisible({timeout:T})}if(e.mode==="importPrivateKey"){let{privateKey:f}=e,A=t.locator(w.importUsingPrivateKeyButton),y=t.locator(w.privateKeyInput),k=t.locator(w.importButton);await s.click(),await A.click(),await y.fill(f),await k.click(),await p.fill(a),await u.fill(a),await d.click(),await l.click(),await b.click(),await B(P).toBeVisible({timeout:25e3}),await t.goto(await r.indexUrl()),await F(t),await B(t.locator(m.depositButton)).toBeVisible({timeout:T}),await B(t.locator(m.sendButton)).toBeVisible({timeout:T})}if(e.mode==="importMnemonic"){let{secretRecoveryPhrase:f}=e,A=t.locator(w.importUsingMnemonicButton);await s.click(),await A.click();for(let[y,k]of f.split(" ").entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+y)}"]`).fill(k);await l.click(),await p.fill(a),await u.fill(a),await d.click(),await l.click(),await b.click(),await B(P).toBeVisible({timeout:25e3}),await t.goto(await r.indexUrl()),await B(t.locator(m.depositButton)).toBeVisible({timeout:T}),await B(t.locator(m.sendButton)).toBeVisible({timeout:T})}if(await S({page:t,newAccountName:e.accountName}),e.additionalAccounts&&e.additionalAccounts.length>0){for(let{...f}of e.additionalAccounts)await L({page:t,...f});await E(t,e.accountName)}await U(t,o),await N(1500),console.info(pt("greenBright","\u2728 Petra onboarding completed successfully",{validateStream:!1}))}import{expect as Wt}from"@playwright/test";async function ut(t){let o=t.locator(C.cancelButton);await Wt(o).toBeEnabled(),await o.click()}import{expect as mt}from"@playwright/test";async function M(t){let o=await R("petra"),e=t.locator(K.passwordInput);if(!await e.isVisible().then(()=>!0).catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await mt(e).toBeVisible({timeout:15e3}),await e.fill(o);let a=t.locator(K.unlockButton);await mt(a).toBeEnabled(),await a.click(),await Promise.allSettled([t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.receiveButton).waitFor({state:"visible",timeout:2e4})])}var v=class extends g{page;constructor(o){super(),this.page=o}async onboard(o){await z({page:this.page,...o})}async unlock(){await M(this.page)}async lock(){await nt(this.page)}async renameAccount({newAccountName:o}){await S({page:this.page,newAccountName:o})}async switchNetwork(o){await U(this.page,o)}async switchAccount(o){await E(this.page,o)}async getAccountAddress(){return await et(this.page)}async addAccount({accountName:o,...e}){await L({page:this.page,accountName:o,...e})}async connectToApp(o){await ot(await this.promptPage(this.page.context()),o)}async confirmTransaction(){await tt(await this.promptPage(this.page.context()))}async rejectTransaction(){await ut(await this.promptPage(this.page.context()))}};import dt from"fs";import Mt from"path";import{test as Dt,chromium as Ot}from"@playwright/test";import{expect as It}from"@playwright/test";async function H(t,o){let e=await t.newPage();return await It(async()=>{await e.goto(o),await F(e)}).toPass(),e}async function j(t,o){let e=await o.newPage();for(let{origin:r,localStorage:a}of t){let c=e.mainFrame();await c.goto(r),await c.evaluate(s=>{s.forEach(({name:p,value:u})=>{window.localStorage.setItem(p,u)})},a)}await e.close()}import _t from"fs/promises";async function wt(t){await _t.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Ft=35e3;async function $(t,o){try{await Promise.race([t.close(),new Promise((e,r)=>setTimeout(()=>r(new Error("Context close timed out")),Ft))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await wt(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}var D,_r=({slowMo:t=0,profileName:o}={})=>Dt.extend({contextPath:async({browserName:e},r,a)=>{let c=await I(`${e}-${a.testId}`);await r(c)},context:async({context:e,contextPath:r},a)=>{let c=new g,s=x(c.name),p=await O(c.name),u=Mt.resolve(s,o??"wallet-data");if(!dt.existsSync(u))throw new Error("\u274C Cache for Petra wallet data not found. Create it first");dt.cpSync(u,r,{recursive:!0,force:!0});let d=[`--disable-extensions-except=${p}`,`--load-extension=${p}`];process.env.HEADLESS&&(d.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let l=await Ot.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:b,origins:P}=await e.storageState();b&&await l.addCookies(b),P&&P.length>0&&j(P,l);let f=await c.indexUrl();D=l.pages().find(y=>y.url().startsWith(f))||await H(l,f);for(let y of l.pages()){let k=y.url();(k.includes("about:blank")||k.includes(c.onboardingPath))&&await y.close()}await D.bringToFront(),await M(D),await a(l),await $(l,r)},petraPage:async({context:e},r)=>{await r(D)},petra:async({context:e},r)=>{let a=new v(D);await r(a)}});import{test as Lt}from"@playwright/test";var Ur=({slowMo:t,profileName:o}={})=>Lt.extend({workerScopeContents:[async({browser:e},r,a)=>{let c=new g,{context:s,contextPath:p,walletPage:u}=await q({wallet:c,workerInfo:a,profileName:o,slowMo:t});await s.grantPermissions(["clipboard-read"]);for(let l of s.pages())l.url().includes(c.onboardingPath)&&await l.close();let d=new v(u);await d.unlock(),await r({wallet:d,walletPage:u,context:s}),await $(s,p)},{scope:"worker"}]});export{v as Petra,_r as petraFixture,Ur as petraWorkerScopeFixture,q as workerScopeContext};
@@ -20,6 +20,7 @@ type OnboardingArgs = {
20
20
  additionalAccounts?: Array<AddAccountArgs>;
21
21
  } | {
22
22
  mode: "recovery phrase";
23
+ accountName: string;
23
24
  secretRecoveryPhrase: string;
24
25
  toggleNetworkMode?: SwitchNetwork;
25
26
  additionalAccounts?: Array<AddAccountArgs>;
@@ -1,3 +1,3 @@
1
- import ot from"fs";import Ct from"path";import{chromium as At}from"@playwright/test";import Bt from"path";var Z=".wallet-cache",tt=".wallet-context";var Y="13.33.0",N="https://github.com/amaify/chainwright/releases/download/v0.1.0/",zt=`https://github.com/MetaMask/metamask-extension/releases/download/v${Y}/metamask-chrome-${Y}.zip`,jt=`${N}solflare-wallet-extension-v2.19.1.zip`,Xt=`${N}petra-wallet-extension-v2.4.8.zip`,Jt=`${N}phantom-wallet-extension-v26.10.0.zip`,qt=`${N}meteor-wallet-extension-v0.7.0.zip`,Qt=`${N}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return Bt.resolve(process.cwd(),tt,t)}import Pt from"path";function x(t){return Pt.resolve(process.cwd(),Z,t)}import et from"fs";import kt from"path";async function I(t){try{let e=x(t),o=kt.resolve(e,"extension-path.txt");if(!et.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=et.readFileSync(o,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function xe({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let n=await T(e.workerIndex.toString()),r=x(t.name),u=Ct.resolve(r,o??"wallet-data");if(!ot.existsSync(u))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);ot.cpSync(u,n,{recursive:!0,force:!0});let i=await I(t.name),m=await At.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:a}),l=await t.indexUrl(),h=m.pages()[0];return h||(h=await m.newPage()),await h.goto(l),{context:m,walletPage:h,contextPath:n}}var g={openMenuButton:"settings-menu-open-button",settingsButton:"sidebar_menu-button-settings",addAccountButton:"sidebar_menu-button-add_account",unlockWalletButton:"unlock-form-submit-button",manageAccountsButton:"sidebar_menu-button-manage_accounts",homeHeaderAccountName:"home-header-account-name"},B={lockWalletButton:"lock-menu-item",closeMenuButton:"settings-menu-close-button",developerSettingsButton:"settings-item-developer-settings",activeNetworksButton:"settings-item-active-networks"},at={accountProfileContainer:"sortable-account-container"};var p={createNewWalletButton:"button:has-text('Create a new wallet')",IAlreadyHaveAWalletButton:"button:has-text('I already have a wallet')",importRecoveryPhraseButton:"button:has-text('Import Recovery Phrase')",importPrivateKeyButton:"button:has-text('Import Private Key')",createSeedPhraseWalletButton:"create-manual-seed-phrase",passwordInput:"onboarding-form-password-input",passwordConfirmInput:"onboarding-form-confirm-password-input",termsCheckBox:"onboarding-form-terms-of-service-checkbox",continueButton:"button:has-text('Continue')",importWalletButton:"button:has-text('Import Wallet')",getStartedButton:"button:has-text('Get Started')",recoveryPhraseSavedCheckbox:"onboarding-form-saved-secret-recovery-phrase-checkbox",recoveryPhraseInput:"secret-recovery-phrase-word-input"};async function D({page:t,privateKey:e,accountName:o,chain:a}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.addAccountButton).click(),await t.locator(p.importPrivateKeyButton).click();let i=t.locator("span[id^='button--listbox-input--']"),m=await i.textContent(),l=t.locator("input[name='name']"),h=t.locator("textarea[name='privateKey']");m!==a&&(await i.click(),await t.locator("ul[id^='listbox--listbox-input--']").locator(`li[data-label='${a}']`).click()),await l.fill(o),await h.fill(e),await t.locator("button:has-text('Import')").click()}var S={confirmButton:"primary-button",cancelButton:"secondary-button"};async function nt(t){let e=t.getByTestId(S.confirmButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"});let a=t.getByRole("button",{name:"Confirm anyway",exact:!0});if(await a.isVisible().catch(()=>!1)){await a.click();return}await e.click()}import{expect as Tt}from"@playwright/test";import bt from"zod";async function v(t,e){let o=bt.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(g.openMenuButton).click();let n=null,r=await t.locator("div[data-testid='account-menu'] div[data-testid='tooltip_interactive-wrapper']").all();for(let u of r)if((await u.textContent())?.includes(o)){n=u;break}if(!n)throw new Error(`Account with name "${o}" not found in the account list.`);await n.click()}async function rt(t,e){e&&await v(t,e);let o=t.getByTestId(S.confirmButton);await Tt(o).toBeEnabled({timeout:15e3}),await o.click()}import It from"zod";var St=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function it({page:t,accountName:e,chain:o}){let a=It.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click(),await t.getByTestId(`manage-accounts-sortable-${a}`).click();let i=t.getByRole("button",{name:/Account Address(?:es)?/i});await i.waitFor({state:"visible",timeout:2e4});let l=await i.locator("div[data-name='row.pair'] > div").last().textContent();if(l&&Number(l)===1){await i.locator("> div > div").last().click(),await t.getByTestId("header--back").click();let y=t.getByTestId(B.closeMenuButton);await y.waitFor({state:"visible",timeout:15e3}),await y.click()}else{await i.click();let w=new RegExp(`${St(o.network)}`,"i");await t.getByRole("button",{name:w}).locator("> div").last().locator("> div").last().locator("div > button").last().click(),await t.getByRole("button",{name:"Close",exact:!0}).last().click();let C=t.getByTestId("header--back");await C.waitFor({state:"visible",timeout:15e3}),await C.click();let b=t.getByTestId(B.closeMenuButton);await b.waitFor({state:"visible",timeout:15e3}),await b.click()}return await t.evaluate(async()=>await navigator.clipboard.readText())}async function ct(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as pt}from"util";import{expect as Nt}from"@playwright/test";function _(t){return new Promise(e=>setTimeout(e,t))}import st from"fs";import vt from"path";async function R(t){let e=x(t),o=vt.resolve(e,"password.txt");try{if(!st.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return st.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Et}from"@playwright/test";async function lt({context:t,path:e,locator:o}){let a;try{await Et.poll(async()=>(a=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).find(n=>n.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).map(r=>r.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(n)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await Wt(a,o),await a.setViewportSize({width:360,height:592}),a}async function Wt(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 ut from"fs";import Ft from"path";async function mt(t){let e=x(t),o=Ft.resolve(e,"extension-id.txt");try{if(!ut.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ut.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var k=class{name="phantom";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await mt(this.name)}async promptPage(e){let o=await this.promptUrl();return await lt({context:e,path:o,locator:"div[id='root']"})}};async function E(t,e){let a=!1;for(;!e();){let n=e();if(n||a||t.isClosed())break;try{let r=t.locator("div[id='modal']").locator("div > svg").first();await r.isVisible().catch(()=>!1)&&(await r.click(),a=!0)}catch(r){if(t.isClosed())break;console.error("[autoClosePhantomNotification]: ",r)}if(n||a||t.isClosed())break;await _(300)}}async function $({page:t,currentAccountName:e,newAccountName:o}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click();let u=await t.getByTestId(at.accountProfileContainer).locator("div[data-testid^='manage-accounts-sortable'] div > p").all(),i=null;for(let f of u)if((await f.textContent())?.toLowerCase()===e.toLowerCase()){i=f;break}if(!i)throw new Error(`Account with name "${e}" not found`);await i.click(),await t.locator("button:has-text('Account Name')").click();let l=t.locator("input[name='name']");await l.clear(),await l.fill(o),await t.getByTestId("primary-button").click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function U(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click()}async function H({page:t,...e}){await U(t);let o=t.locator(`button[id='${B.developerSettingsButton}']`);await o.scrollIntoViewIfNeeded(),await o.click();let a=t.getByTestId("toggleTestNetwork"),r=await a.locator("label[data-testid='toggleTestNetwork-switch'] > input[aria-label='Toggle']").isChecked().catch(()=>!1);if(!r&&e.mode==="on"&&await a.click(),r&&e.mode==="off"){await a.click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click();return}if(e.mode==="on"&&e.chain==="Solana"){let{network:m}=e;await t.locator(`button:has-text("${m}")`).click()}if(e.mode==="on"&&e.chain==="Ethereum"){let{network:m}=e;if(!await t.getByText("EVM",{exact:!0}).isVisible().catch(()=>!1))throw new Error(["EVM testnet options are not available. Please ensure Ethereum is enabled in optional chains.","To enable Ethereum, call the 'toggleOptionalChain' action before switching the network.","toggleOptionalChain({ page: page, toggleMode: 'on', supportedChains: ['Ethereum'] })","Tip: For persistence, enable Ethereum in your setup file after the onboarding step completes."].join(`
2
- `));await t.locator(`button:has-text("${m}")`).click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function z({page:t,additionalAccounts:e,...o}){console.info(pt("yellowBright",`
3
- Phantom onboarding started...`,{validateStream:!1}));let a=await R("phantom");if(o.mode==="create"){await t.locator(p.createNewWalletButton).click(),await t.getByTestId(p.createSeedPhraseWalletButton).click();let f=t.getByTestId(p.passwordInput),d=t.getByTestId(p.passwordConfirmInput),A=t.getByTestId(p.termsCheckBox),P=t.locator(p.continueButton);await f.fill(a),await d.fill(a),await A.click(),await P.click(),await P.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByTestId(p.recoveryPhraseSavedCheckbox).click(),await P.click(),await _(1e3),await P.click(),await t.locator(p.getStartedButton).last().click()}if(o.mode==="recovery phrase"){let y=o.secretRecoveryPhrase.split(" ");await t.locator(p.IAlreadyHaveAWalletButton).click(),await t.locator(p.importRecoveryPhraseButton).click();for(let[G,O]of Object.entries(y))await t.getByTestId(`${p.recoveryPhraseInput}-${G}`).fill(O);await t.locator(p.importWalletButton).click(),await t.locator("p:has-text('Finding accounts with activity')").waitFor({state:"detached",timeout:6e4});let C=t.locator(p.continueButton);await C.click();let b=t.getByTestId(p.passwordInput),L=t.getByTestId(p.passwordConfirmInput),J=t.getByTestId(p.termsCheckBox);await b.fill(a),await L.fill(a),await J.click(),await C.click(),await C.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(p.getStartedButton).last().click()}if(o.mode==="private key"){await t.locator(p.IAlreadyHaveAWalletButton).click();let{privateKey:y,chain:f,accountName:d}=o;await t.locator(p.importPrivateKeyButton).click();let P=t.locator("span[id='button--listbox-input--1']"),C=await P.textContent(),b=t.locator("input[name='name']"),L=t.locator("textarea[name='privateKey']");C!==f&&(await P.click(),await t.locator("ul[id='listbox--listbox-input--1']").locator(`li[data-label='${f}']`).click()),await b.fill(d),await L.fill(y),await t.locator("button:has-text('Import')").click();let q=t.getByTestId(p.passwordInput),Q=t.getByTestId(p.passwordConfirmInput),G=t.getByTestId(p.termsCheckBox);await q.fill(a),await Q.fill(a),await G.click();let O=t.locator(p.continueButton);await O.click(),await O.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(p.getStartedButton).last().click()}let n=await t.context().newPage(),r=await new k().indexUrl();await n.goto(r);let i=await t.context().browser()?.newBrowserCDPSession(),m;await Nt.poll(async()=>{if(i){let{targetInfos:w}=await i.send("Target.getTargets"),f=w.filter(d=>d.title==="Phantom Wallet").find(d=>!d.attached&&d.url===r);return m=f,!!f}},{timeout:2e4}).toBe(!0),m&&await i?.send("Target.closeTarget",{targetId:m.targetId});let l=await n.getByTestId("home-header-account-name").textContent();if(o.mode==="create"){let{accountName:w}=o;await $({page:n,newAccountName:w,currentAccountName:"Account 1"})}if(e&&e.length>0){let w=!1;E(n,()=>w).catch(d=>console.error({error:d}));for(let{accountName:d,chain:A,privateKey:P}of e)await D({page:n,privateKey:P,accountName:d,chain:A}),w=!0;let f=o.mode==="create"?o.accountName:l;f&&await v(n,f)}o.toggleNetworkMode&&await H({page:n,...o.toggleNetworkMode}),await _(3e3),console.info(pt("greenBright","\u2728 Phantom onboarding completed successfully",{validateStream:!1}))}async function dt(t){let e=t.getByTestId(S.cancelButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"}),await e.click()}async function wt({page:t,supportedChains:e,toggleMode:o="off"}){if(await U(t),await t.locator("button[id='settings-item-active-networks']").click(),e.length===0)throw Error("Supported chains array cannot be empty for toggle mode other than 'onboard'");for(let u of e){let i=t.locator(`button[id='toggle-${u.toLowerCase()}']`),l=await i.locator(`label[data-testid='toggle-${u.toLowerCase()}-switch'] > input[aria-label='Toggle']`).isChecked().catch(()=>!1);o==="off"&&l&&await i.click(),o==="on"&&!l&&await i.click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function V(t){let e=await R("phantom"),o=t.locator("input[name='password']"),a=t.getByTestId("unlock-form-submit-button");await o.fill(e),await a.click(),await a.waitFor({state:"detached"})}var W=class extends k{page;constructor(e){super(),this.page=e}async onboard({...e}){await z({page:this.page,...e})}async unlock(){await V(this.page)}async lock(){await ct(this.page)}async renameAccount({...e}){await $({page:this.page,...e})}async switchAccount(e){await v(this.page,e)}async getAccountAddress({accountName:e,chain:o}){return await it({page:this.page,accountName:e,chain:o})}async addAccount({...e}){await D({page:this.page,...e})}async toggleOptionalChains({toggleMode:e,supportedChains:o}){await wt({page:this.page,supportedChains:o,toggleMode:e})}async switchNetwork({...e}){await H({page:this.page,...e})}async connectToApp(e){await rt(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await nt(await this.promptPage(this.page.context()))}async rejectTransaction(){await dt(await this.promptPage(this.page.context()))}};import gt from"fs";import Ot from"path";import{test as Dt,chromium as Rt}from"@playwright/test";import{expect as _t}from"@playwright/test";async function j(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function M(t,e){let o=await t.newPage();return await _t(async()=>{await o.goto(e),await j(o)}).toPass(),o}async function X(t,e){let o=await e.newPage();for(let{origin:a,localStorage:n}of t){let r=o.mainFrame();await r.goto(a),await r.evaluate(u=>{u.forEach(({name:i,value:m})=>{window.localStorage.setItem(i,m)})},n)}await o.close()}import Mt from"fs/promises";async function ft(t){await Mt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Lt=35e3;async function K(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Lt))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await ft(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var F,Ua=({slowMo:t=0,profileName:e}={})=>Dt.extend({contextPath:async({browserName:o},a,n)=>{let r=await T(`${o}-${n.testId}`);await a(r)},context:async({context:o,contextPath:a},n)=>{let r=new k,u=x(r.name),i=await I(r.name),m=Ot.resolve(u,e??"wallet-data");if(!gt.existsSync(m))throw new Error("\u274C Cache for Phantom wallet data not found. Create it first");gt.cpSync(m,a,{recursive:!0,force:!0}),process.env.HEADLESS&&t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!");let l=await Rt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${i}`,`--load-extension=${i}`],slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:h,origins:w}=await o.storageState();h&&await l.addCookies(h),w&&w.length>0&&await X(w,l);let y=await r.indexUrl();F=l.pages().find(d=>d.url().startsWith(y))||await M(l,y);for(let d of l.pages())d.url().includes("about:blank")&&await d.close();await F.bringToFront(),await V(F),await n(l),await K(l,a)},phantomPage:async({context:o},a)=>{await a(F)},phantom:async({context:o},a)=>{let n=new W(F);await a(n)},autoCloseNotification:[async({context:o},a)=>{let n=!1,u=E(F,()=>n);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});import{test as Ht}from"@playwright/test";import ht from"fs";import $t from"path";import{chromium as Ut}from"@playwright/test";async function yt({workerInfo:t,profileName:e,slowMo:o}){let a=new k,n=await T(t.workerIndex.toString()),r=x(a.name),u=$t.resolve(r,e??"wallet-data");if(!ht.existsSync(u))throw new Error(`Cache for ${a.name} does not exist. Create it first!`);ht.cpSync(u,n,{recursive:!0,force:!0});let i=await I(a.name),m=await Ut.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:o}),l=await a.indexUrl(),h=await M(m,l);return{context:m,walletPage:h,contextPath:n}}var rn=({slowMo:t,profileName:e}={})=>Ht.extend({workerScopeContents:[async({browser:o},a,n)=>{let{context:r,contextPath:u,walletPage:i}=await yt({workerInfo:n,profileName:e,slowMo:t});await r.grantPermissions(["clipboard-read"]);for(let l of r.pages())l.url().includes("about:blank")&&await l.close();let m=new W(i);await m.unlock(),await a({wallet:m,walletPage:i,context:r}),await K(r,u)},{scope:"worker"}],autoCloseNotification:[async({workerScopeContents:o},a)=>{let n=!1,r=()=>n,u=E(o.walletPage,r);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});export{W as Phantom,Ua as phantomFixture,rn as phantomWorkerScopeFixture,xe as workerScopeContext};
1
+ import at from"fs";import At from"path";import{chromium as bt}from"@playwright/test";import Pt from"path";var tt=".wallet-cache",et=".wallet-context";var Z="13.33.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",zt=`https://github.com/MetaMask/metamask-extension/releases/download/v${Z}/metamask-chrome-${Z}.zip`,jt=`${_}solflare-wallet-extension-v2.19.1.zip`,Xt=`${_}petra-wallet-extension-v2.4.8.zip`,Jt=`${_}phantom-wallet-extension-v26.10.0.zip`,qt=`${_}meteor-wallet-extension-v0.7.0.zip`,Qt=`${_}keplr-wallet-extension-v0.13.3.zip`;async function T(t){return Pt.resolve(process.cwd(),et,t)}import kt from"path";function x(t){return kt.resolve(process.cwd(),tt,t)}import ot from"fs";import Ct from"path";async function I(t){try{let e=x(t),o=Ct.resolve(e,"extension-path.txt");if(!ot.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=ot.readFileSync(o,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}async function xe({wallet:t,workerInfo:e,profileName:o,slowMo:a}){let n=await T(e.workerIndex.toString()),r=x(t.name),u=At.resolve(r,o??"wallet-data");if(!at.existsSync(u))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);at.cpSync(u,n,{recursive:!0,force:!0});let i=await I(t.name),m=await bt.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:a}),l=await t.indexUrl(),h=m.pages()[0];return h||(h=await m.newPage()),await h.goto(l),{context:m,walletPage:h,contextPath:n}}var g={openMenuButton:"settings-menu-open-button",settingsButton:"sidebar_menu-button-settings",addAccountButton:"sidebar_menu-button-add_account",unlockWalletButton:"unlock-form-submit-button",manageAccountsButton:"sidebar_menu-button-manage_accounts",homeHeaderAccountName:"home-header-account-name"},B={lockWalletButton:"lock-menu-item",closeMenuButton:"settings-menu-close-button",developerSettingsButton:"settings-item-developer-settings",activeNetworksButton:"settings-item-active-networks"},nt={accountProfileContainer:"sortable-account-container"};var p={createNewWalletButton:"button:has-text('Create a new wallet')",IAlreadyHaveAWalletButton:"button:has-text('I already have a wallet')",importRecoveryPhraseButton:"button:has-text('Import Recovery Phrase')",importPrivateKeyButton:"button:has-text('Import Private Key')",createSeedPhraseWalletButton:"create-manual-seed-phrase",passwordInput:"onboarding-form-password-input",passwordConfirmInput:"onboarding-form-confirm-password-input",termsCheckBox:"onboarding-form-terms-of-service-checkbox",continueButton:"button:has-text('Continue')",importWalletButton:"button:has-text('Import Wallet')",getStartedButton:"button:has-text('Get Started')",recoveryPhraseSavedCheckbox:"onboarding-form-saved-secret-recovery-phrase-checkbox",recoveryPhraseInput:"secret-recovery-phrase-word-input"};async function R({page:t,privateKey:e,accountName:o,chain:a}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.addAccountButton).click(),await t.locator(p.importPrivateKeyButton).click();let i=t.locator("span[id^='button--listbox-input--']"),m=await i.textContent(),l=t.locator("input[name='name']"),h=t.locator("textarea[name='privateKey']");m!==a&&(await i.click(),await t.locator("ul[id^='listbox--listbox-input--']").locator(`li[data-label='${a}']`).click()),await l.fill(o),await h.fill(e),await t.locator("button:has-text('Import')").click()}var S={confirmButton:"primary-button",cancelButton:"secondary-button"};async function rt(t){let e=t.getByTestId(S.confirmButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"});let a=t.getByRole("button",{name:"Confirm anyway",exact:!0});if(await a.isVisible().catch(()=>!1)){await a.click();return}await e.click()}import{expect as It}from"@playwright/test";import Tt from"zod";async function v(t,e){let o=Tt.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(g.openMenuButton).click();let n=null,r=await t.locator("div[data-testid='account-menu'] div[data-testid='tooltip_interactive-wrapper']").all();for(let u of r)if((await u.textContent())?.includes(o)){n=u;break}if(!n)throw new Error(`Account with name "${o}" not found in the account list.`);await n.click()}async function it(t,e){e&&await v(t,e);let o=t.getByTestId(S.confirmButton);await It(o).toBeEnabled({timeout:15e3}),await o.click()}import St from"zod";var vt=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function ct({page:t,accountName:e,chain:o}){let a=St.string().min(1,"Account name cannot be an empty string").parse(e);await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click(),await t.getByTestId(`manage-accounts-sortable-${a}`).click();let i=t.getByRole("button",{name:/Account Address(?:es)?/i});await i.waitFor({state:"visible",timeout:2e4});let l=await i.locator("div[data-name='row.pair'] > div").last().textContent();if(l&&Number(l)===1){await i.locator("> div > div").last().click(),await t.getByTestId("header--back").click();let y=t.getByTestId(B.closeMenuButton);await y.waitFor({state:"visible",timeout:15e3}),await y.click()}else{await i.click();let w=new RegExp(`${vt(o.network)}`,"i");await t.getByRole("button",{name:w}).locator("> div").last().locator("> div").last().locator("div > button").last().click(),await t.getByRole("button",{name:"Close",exact:!0}).last().click();let C=t.getByTestId("header--back");await C.waitFor({state:"visible",timeout:15e3}),await C.click();let b=t.getByTestId(B.closeMenuButton);await b.waitFor({state:"visible",timeout:15e3}),await b.click()}return await t.evaluate(async()=>await navigator.clipboard.readText())}async function st(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as wt}from"util";import{expect as _t}from"@playwright/test";function M(t){return new Promise(e=>setTimeout(e,t))}import lt from"fs";import Et from"path";async function D(t){let e=x(t),o=Et.resolve(e,"password.txt");try{if(!lt.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return lt.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as Wt}from"@playwright/test";async function ut({context:t,path:e,locator:o}){let a;try{await Wt.poll(async()=>(a=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).find(n=>n.url().match(e)),!!a),{timeout:3e4}).toBe(!0)}catch{let n=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).map(r=>r.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(n)}`)}if(!a)throw new Error(`Popup page with path ${e} not found in context.`);return await Ft(a,o),await a.setViewportSize({width:360,height:592}),a}async function Ft(t,e){await t.waitForLoadState("load",{timeout:4e4}),await t.waitForLoadState("domcontentloaded",{timeout:4e4}),await t.locator(e).first().waitFor({state:"attached",timeout:4e4})}import mt from"fs";import Nt from"path";async function pt(t){let e=x(t),o=Nt.resolve(e,"extension-id.txt");try{if(!mt.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return mt.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var P=class{name="phantom";onboardingPath="/onboarding.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/notification.html`}async extensionId(){return await pt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ut({context:e,path:o,locator:"div[id='root']"})}};async function E(t,e){let a=!1;for(;!e();){let n=e();if(n||a||t.isClosed())break;try{let r=t.locator("div[id='modal']").locator("div > svg").first();await r.isVisible().catch(()=>!1)&&(await r.click(),a=!0)}catch(r){if(t.isClosed())break;console.error("[autoClosePhantomNotification]: ",r)}if(n||a||t.isClosed())break;await M(300)}}async function $({page:t,currentAccountName:e,newAccountName:o}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click();let u=await t.getByTestId(nt.accountProfileContainer).locator("div[data-testid^='manage-accounts-sortable'] div > p").all(),i=null;for(let d of u)if((await d.textContent())?.toLowerCase()===e.toLowerCase()){i=d;break}if(!i)throw new Error(`Account with name "${e}" not found`);await i.click(),await t.locator("button:has-text('Account Name')").click();let l=t.locator("input[name='name']");await l.clear(),await l.fill(o),await t.getByTestId("primary-button").click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function U(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click()}async function H({page:t,...e}){await U(t);let o=t.locator(`button[id='${B.developerSettingsButton}']`);await o.scrollIntoViewIfNeeded(),await o.click();let a=t.getByTestId("toggleTestNetwork"),r=await a.locator("label[data-testid='toggleTestNetwork-switch'] > input[aria-label='Toggle']").isChecked().catch(()=>!1);if(!r&&e.mode==="on"&&await a.click(),r&&e.mode==="off"){await a.click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click();return}if(e.mode==="on"&&e.chain==="Solana"){let{network:m}=e;await t.locator(`button:has-text("${m}")`).click()}if(e.mode==="on"&&e.chain==="Ethereum"){let{network:m}=e;if(!await t.getByText("EVM",{exact:!0}).isVisible().catch(()=>!1))throw new Error(["EVM testnet options are not available. Please ensure Ethereum is enabled in optional chains.","To enable Ethereum, call the 'toggleOptionalChain' action before switching the network.","toggleOptionalChain({ page: page, toggleMode: 'on', supportedChains: ['Ethereum'] })","Tip: For persistence, enable Ethereum in your setup file after the onboarding step completes."].join(`
2
+ `));await t.locator(`button:has-text("${m}")`).click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function G({page:t,additionalAccounts:e,...o}){console.info(wt("yellowBright",`
3
+ Phantom onboarding started...`,{validateStream:!1}));let a=await D("phantom");if(o.mode==="create"){await t.locator(p.createNewWalletButton).click(),await t.getByTestId(p.createSeedPhraseWalletButton).click();let d=t.getByTestId(p.passwordInput),f=t.getByTestId(p.passwordConfirmInput),A=t.getByTestId(p.termsCheckBox),k=t.locator(p.continueButton);await d.fill(a),await f.fill(a),await A.click(),await k.click(),await k.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByTestId(p.recoveryPhraseSavedCheckbox).click(),await k.click(),await M(1e3),await k.click(),await t.locator(p.getStartedButton).last().click()}if(o.mode==="recovery phrase"){let y=o.secretRecoveryPhrase.split(" ");await t.locator(p.IAlreadyHaveAWalletButton).click(),await t.locator(p.importRecoveryPhraseButton).click();for(let[N,Y]of Object.entries(y))await t.getByTestId(`${p.recoveryPhraseInput}-${N}`).fill(Y);await t.locator(p.importWalletButton).click(),await t.locator("p:has-text('Finding accounts with activity')").waitFor({state:"detached",timeout:6e4});let C=t.locator(p.continueButton);await C.click();let b=t.getByTestId(p.passwordInput),O=t.getByTestId(p.passwordConfirmInput),X=t.getByTestId(p.termsCheckBox);await b.fill(a),await O.fill(a),await X.click(),await C.click(),await C.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByRole("textbox",{name:"Username @ Clear",exact:!0}).waitFor({state:"attached",timeout:5e3}).then(async()=>{await t.getByRole("button",{name:"Continue",exact:!0}).click()}).catch(()=>{}),await t.locator(p.getStartedButton).last().click()}if(o.mode==="private key"){await t.locator(p.IAlreadyHaveAWalletButton).click();let{privateKey:y,chain:d,accountName:f}=o;await t.locator(p.importPrivateKeyButton).click();let k=t.locator("span[id='button--listbox-input--1']"),C=await k.textContent(),b=t.locator("input[name='name']"),O=t.locator("textarea[name='privateKey']");C!==d&&(await k.click(),await t.locator("ul[id='listbox--listbox-input--1']").locator(`li[data-label='${d}']`).click()),await b.fill(f),await O.fill(y),await t.locator("button:has-text('Import')").click();let J=t.getByTestId(p.passwordInput),q=t.getByTestId(p.passwordConfirmInput),Q=t.getByTestId(p.termsCheckBox);await J.fill(a),await q.fill(a),await Q.click();let N=t.locator(p.continueButton);await N.click(),await N.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(p.getStartedButton).last().click()}let n=await t.context().newPage(),r=await new P().indexUrl();await n.goto(r);let i=await t.context().browser()?.newBrowserCDPSession(),m;await _t.poll(async()=>{if(i){let{targetInfos:w}=await i.send("Target.getTargets"),d=w.filter(f=>f.title==="Phantom Wallet").find(f=>!f.attached&&f.url===r);return m=d,!!d}},{timeout:2e4}).toBe(!0),m&&await i?.send("Target.closeTarget",{targetId:m.targetId});let l=await n.getByTestId("home-header-account-name").textContent();if(!l)throw new Error("Cannot find initial account name");if(o.mode==="create"||o.mode==="recovery phrase"){let{accountName:w}=o;await $({page:n,newAccountName:w,currentAccountName:l})}if(e&&e.length>0){let w=!1;E(n,()=>w).catch(d=>console.error({error:d}));for(let{accountName:d,chain:f,privateKey:A}of e)await R({page:n,privateKey:A,accountName:d,chain:f}),w=!0;await v(n,o.accountName)}o.toggleNetworkMode&&await H({page:n,...o.toggleNetworkMode}),await M(3e3),console.info(wt("greenBright","\u2728 Phantom onboarding completed successfully",{validateStream:!1}))}async function dt(t){let e=t.getByTestId(S.cancelButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"}),await e.click()}async function ft({page:t,supportedChains:e,toggleMode:o="off"}){if(await U(t),await t.locator("button[id='settings-item-active-networks']").click(),e.length===0)throw Error("Supported chains array cannot be empty for toggle mode other than 'onboard'");for(let u of e){let i=t.locator(`button[id='toggle-${u.toLowerCase()}']`),l=await i.locator(`label[data-testid='toggle-${u.toLowerCase()}-switch'] > input[aria-label='Toggle']`).isChecked().catch(()=>!1);o==="off"&&l&&await i.click(),o==="on"&&!l&&await i.click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function V(t){let e=await D("phantom"),o=t.locator("input[name='password']"),a=t.getByTestId("unlock-form-submit-button");await o.fill(e),await a.click(),await a.waitFor({state:"detached"})}var W=class extends P{page;constructor(e){super(),this.page=e}async onboard({...e}){await G({page:this.page,...e})}async unlock(){await V(this.page)}async lock(){await st(this.page)}async renameAccount({...e}){await $({page:this.page,...e})}async switchAccount(e){await v(this.page,e)}async getAccountAddress({accountName:e,chain:o}){return await ct({page:this.page,accountName:e,chain:o})}async addAccount({...e}){await R({page:this.page,...e})}async toggleOptionalChains({toggleMode:e,supportedChains:o}){await ft({page:this.page,supportedChains:o,toggleMode:e})}async switchNetwork({...e}){await H({page:this.page,...e})}async connectToApp(e){await it(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await rt(await this.promptPage(this.page.context()))}async rejectTransaction(){await dt(await this.promptPage(this.page.context()))}};import ht from"fs";import Rt from"path";import{test as Dt,chromium as $t}from"@playwright/test";import{expect as Mt}from"@playwright/test";async function z(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function L(t,e){let o=await t.newPage();return await Mt(async()=>{await o.goto(e),await z(o)}).toPass(),o}async function j(t,e){let o=await e.newPage();for(let{origin:a,localStorage:n}of t){let r=o.mainFrame();await r.goto(a),await r.evaluate(u=>{u.forEach(({name:i,value:m})=>{window.localStorage.setItem(i,m)})},n)}await o.close()}import Lt from"fs/promises";async function gt(t){await Lt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Ot=35e3;async function K(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Ot))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await gt(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}var F,Ua=({slowMo:t=0,profileName:e}={})=>Dt.extend({contextPath:async({browserName:o},a,n)=>{let r=await T(`${o}-${n.testId}`);await a(r)},context:async({context:o,contextPath:a},n)=>{let r=new P,u=x(r.name),i=await I(r.name),m=Rt.resolve(u,e??"wallet-data");if(!ht.existsSync(m))throw new Error("\u274C Cache for Phantom wallet data not found. Create it first");ht.cpSync(m,a,{recursive:!0,force:!0}),process.env.HEADLESS&&t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!");let l=await $t.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${i}`,`--load-extension=${i}`],slowMo:process.env.HEADLESS?0:t});await l.grantPermissions(["clipboard-read"]);let{cookies:h,origins:w}=await o.storageState();h&&await l.addCookies(h),w&&w.length>0&&await j(w,l);let y=await r.indexUrl();F=l.pages().find(f=>f.url().startsWith(y))||await L(l,y);for(let f of l.pages())f.url().includes("about:blank")&&await f.close();await F.bringToFront(),await V(F),await n(l),await K(l,a)},phantomPage:async({context:o},a)=>{await a(F)},phantom:async({context:o},a)=>{let n=new W(F);await a(n)},autoCloseNotification:[async({context:o},a)=>{let n=!1,u=E(F,()=>n);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});import{test as Vt}from"@playwright/test";import yt from"fs";import Ut from"path";import{chromium as Ht}from"@playwright/test";async function xt({workerInfo:t,profileName:e,slowMo:o}){let a=new P,n=await T(t.workerIndex.toString()),r=x(a.name),u=Ut.resolve(r,e??"wallet-data");if(!yt.existsSync(u))throw new Error(`Cache for ${a.name} does not exist. Create it first!`);yt.cpSync(u,n,{recursive:!0,force:!0});let i=await I(a.name),m=await Ht.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${i}`],slowMo:process.env.HEADLESS?0:o}),l=await a.indexUrl(),h=await L(m,l);return{context:m,walletPage:h,contextPath:n}}var rn=({slowMo:t,profileName:e}={})=>Vt.extend({workerScopeContents:[async({browser:o},a,n)=>{let{context:r,contextPath:u,walletPage:i}=await xt({workerInfo:n,profileName:e,slowMo:t});await r.grantPermissions(["clipboard-read"]);for(let l of r.pages())l.url().includes("about:blank")&&await l.close();let m=new W(i);await m.unlock(),await a({wallet:m,walletPage:i,context:r}),await K(r,u)},{scope:"worker"}],autoCloseNotification:[async({workerScopeContents:o},a)=>{let n=!1,r=()=>n,u=E(o.walletPage,r);await a(void 0),n=!0,await u.catch(i=>{console.error(`Auto close notification error: ${i.message}`)})},{auto:!0}]});export{W as Phantom,Ua as phantomFixture,rn as phantomWorkerScopeFixture,xe as workerScopeContext};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chainwright",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "Playwright Web3 wallet testing framework for end-to-end dApp automation with MetaMask, Phantom, Solflare, Petra, Meteor, and Keplr",
5
5
  "type": "module",
6
6
  "license": "MIT",