@tobelabs/chainwright 0.6.1-alpha.0 → 0.6.1-alpha.2
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/core/index.d.ts +1 -1
- package/dist/{types-dJRYhQT9.d.ts → types-COvLLLoP.d.ts} +3 -1
- package/dist/wallets/index.d.ts +20 -961
- package/dist/wallets/index.js +1 -16
- package/dist/wallets/keplr/index.d.ts +152 -0
- package/dist/wallets/keplr/index.js +3 -0
- package/dist/wallets/metamask/index.d.ts +203 -0
- package/dist/wallets/metamask/index.js +3 -0
- package/dist/wallets/meteor/index.d.ts +150 -0
- package/dist/wallets/meteor/index.js +7 -0
- package/dist/wallets/petra/index.d.ts +157 -0
- package/dist/wallets/petra/index.js +3 -0
- package/dist/wallets/phantom/index.d.ts +195 -0
- package/dist/wallets/phantom/index.js +3 -0
- package/dist/wallets/solflare/index.d.ts +142 -0
- package/dist/wallets/solflare/index.js +2 -0
- package/package.json +26 -4
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import xt 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 W}from"@playwright/test";import ft from"zod";async function k({page:t,newAccountName:o}){let e=ft.string().min(1,"Account name cannot be an empty string").parse(o);await t.locator(m.settingsMenu).click(),await W(t.getByText("Settings").first()).toBeVisible();let r=t.locator(h.editAccountButton);await W(r).toBeVisible(),await r.click(),await W(t.getByText("Account name").first()).toBeVisible();let n=t.locator(h.renameAccountInput);if(await n.getAttribute("value")===e)throw Error(`The account to be renamed "${e}" already exists.`);await n.fill(e);let l=t.locator(h.saveButton);await W(l).toBeEnabled(),await l.click(),await W(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 O({page:t,accountName:o,mode:e,...a}){let r=xt.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 a?a.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 ${r} already exists in wallet`);await k({page:t,newAccountName:r})}if(e==="mnemonic"){let d=("mnemonicPhrase"in a?a.mnemonicPhrase:"").split(" ");await t.locator(h.addAccountWithMnemonicButton).click();for(let[g,v]of d.entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+g)}"]`).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 ${r} already exists in wallet`);await k({page:t,newAccountName:r})}}function L(t){return new Promise(o=>setTimeout(o,t))}var C={approveButton:'button:has-text("Approve")',cancelButton:'button:has-text("Cancel")'};var ht=/^[A-Z0-9]+(?:_[A-Z0-9]+)+$/;function j(t){return ht.test(t)?t.toLowerCase().replace(/_/g," ").replace(/\b[a-z]/g,o=>o.toUpperCase()):t}async function yt(t,o){for(;;){let a=o();if(a||t.isClosed())break;try{let r=t.locator("div:has(> h2:has-text('Simulation error'))");if(await r.isVisible().catch(()=>!1)){let s=await r.locator("p").textContent();throw new Error(`[Confirm Transaction Error]: ${j(s||"Unexpected error!")}`)}}catch(r){if(t.isClosed())break;throw r instanceof Error?r:new Error(`[Confirm Transaction Error]: ${r}`)}if(a||t.isClosed())break;await L(300)}}async function G(t){let o=!1;yt(t,()=>o).catch(async r=>{t.isClosed()||console.error(r.message)}),await t.locator(C.approveButton).click(),o=!0}import{expect as gt}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 n=await t.getByRole("dialog").locator("> div > div > button[type='button']").all(),s=null;for(let l of n)if((await l.textContent())?.toLowerCase()?.trim().includes(o.toLowerCase().trim())){s=l;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function X(t,o){o&&await E(t,o);let e=t.locator(C.approveButton);await gt(e).toBeEnabled({timeout:2e4}),await e.click()}async function Z(t){return await t.getByRole("button",{name:"Copy Address",exact:!0}).click(),await t.evaluate(async()=>await navigator.clipboard.readText())}import{expect as J}from"@playwright/test";async function Y(t){let o=t.locator(m.settingsMenu);await J(o).toBeVisible(),await o.click(),await J(t.getByText("Settings").first()).toBeVisible()}async function q(t){await Y(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 it}from"util";import{expect as b}from"@playwright/test";async function I(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}import et from"fs";import Bt from"path";import Pt from"path";var tt=".wallet-cache",ot=".wallet-context";var Q="13.22.0",_="https://github.com/amaify/chainwright/releases/download/v0.1.0/",Wo=`https://github.com/MetaMask/metamask-extension/releases/download/v${Q}/metamask-chrome-${Q}.zip`,Io=`${_}solflare-wallet-extension-v2.19.1.zip`,_o=`${_}petra-wallet-extension-v2.4.8.zip`,Mo=`${_}phantom-wallet-extension-v26.10.0.zip`,Fo=`${_}meteor-wallet-extension-v0.7.0.zip`,Do=`${_}keplr-wallet-extension-v0.13.3.zip`;function y(t){return Pt.resolve(process.cwd(),tt,t)}async function N(t){let o=y(t),e=Bt.resolve(o,"password.txt");try{if(!et.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return et.readFileSync(e,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as bt}from"@playwright/test";async function at({context:t,path:o,locator:e}){let a;try{await bt.poll(async()=>(a=t.pages().filter(r=>r.url().startsWith("chrome-extension://")).find(r=>r.url().match(o)),!!a),{timeout:3e4}).toBe(!0)}catch{let r=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).map(n=>n.url());throw new Error(`Popup page with path "${o}" not found in context after 30s. Pages in context: ${JSON.stringify(r)}`)}if(!a)throw new Error(`Popup page with path ${o} not found in context.`);return await kt(a,e),await a.setViewportSize({width:360,height:592}),a}async function kt(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 rt from"fs";import At from"path";async function nt(t){let o=y(t),e=At.resolve(o,"extension-id.txt");try{if(!rt.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return rt.readFileSync(e,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${a.message}`)}}var P=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 nt(this.name)}async promptPage(o){let e=await this.promptUrl();return await at({context:o,path:e,locator:"div[id='prompt']"})}};var T=3e4;async function K({page:t,...o}){console.info(it("yellowBright",`
|
|
3
|
+
Petra onboarding started...`,{validateStream:!1}));let e=new P,a=await N("petra"),r=t.locator(w.createWalletButton),n=t.locator(w.importWalletButton),s=t.locator(w.createNewPasswordInput),l=t.locator(w.confirmNewPasswordInput),u=t.locator(w.confirmPasswordCheckbox),d=t.locator(w.continueButton),p=t.locator(w.getStartedButton),x=t.locator(w.onboardingCompleteText);if(o.mode==="create"){let f=t.locator(w.createSeedPhraseButton);await r.click(),await f.click(),await s.fill(a),await l.fill(a),await u.click(),await d.click(),await t.locator(w.skipCopyRecoveryPhraseButton).click(),await p.click(),await b(x).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await b(t.locator(m.depositButton)).toBeVisible({timeout:T}),await b(t.locator(m.sendButton)).toBeVisible({timeout:T})}if(o.mode==="importPrivateKey"){let{privateKey:f}=o,B=t.locator(w.importUsingPrivateKeyButton),S=t.locator(w.privateKeyInput),g=t.locator(w.importButton);await n.click(),await B.click(),await S.fill(f),await g.click(),await s.fill(a),await l.fill(a),await u.click(),await d.click(),await p.click(),await b(x).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await I(t),await b(t.locator(m.depositButton)).toBeVisible({timeout:T}),await b(t.locator(m.sendButton)).toBeVisible({timeout:T})}if(o.mode==="importMnemonic"){let{secretRecoveryPhrase:f}=o,B=t.locator(w.importUsingMnemonicButton);await n.click(),await B.click();for(let[S,g]of f.split(" ").entries())await t.locator(`input[name="mnemonic-${String.fromCharCode(97+S)}"]`).fill(g);await d.click(),await s.fill(a),await l.fill(a),await u.click(),await d.click(),await p.click(),await b(x).toBeVisible({timeout:25e3}),await t.goto(await e.indexUrl()),await b(t.locator(m.depositButton)).toBeVisible({timeout:T}),await b(t.locator(m.sendButton)).toBeVisible({timeout:T})}if(await k({page:t,newAccountName:o.accountName}),o.addWallet&&o.addWallet.length>0){for(let{...f}of o.addWallet)await O({page:t,...f});await E(t,o.accountName)}await L(1500),console.info(it("greenBright","\u2728 Petra onboarding completed successfully",{validateStream:!1}))}import{expect as St}from"@playwright/test";async function ct(t){let o=t.locator(C.cancelButton);await St(o).toBeEnabled(),await o.click()}async function st(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 n=t.locator($.backButton);await n.click(),await n.click()}import{expect as lt}from"@playwright/test";async function M(t){let o=await N("petra"),e=t.locator(V.passwordInput);if(!await e.isVisible().then(()=>!0).catch(()=>!1)){console.info("\u{1F4A1} Wallet is already unlocked");return}await lt(e).toBeVisible({timeout:15e3}),await e.fill(o);let r=t.locator(V.unlockButton);await lt(r).toBeEnabled(),await r.click(),await Promise.allSettled([t.locator(m.sendButton).waitFor({state:"visible",timeout:2e4}),t.locator(m.receiveButton).waitFor({state:"visible",timeout:2e4})])}var A=class extends P{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 q(this.page)}async renameAccount({newAccountName:o}){await k({page:this.page,newAccountName:o})}async switchNetwork(o){await st(this.page,o)}async switchAccount(o){await E(this.page,o)}async getAccountAddress(){return await Z(this.page)}async addAccount({accountName:o,...e}){await O({page:this.page,accountName:o,...e})}async connectToApp(o){await X(await this.promptPage(this.page.context()),o)}async confirmTransaction(){await G(await this.promptPage(this.page.context()))}async rejectTransaction(){await ct(await this.promptPage(this.page.context()))}};import mt from"fs";import It from"path";import{test as _t,chromium as Mt}from"@playwright/test";import Ct from"path";async function F(t){return Ct.resolve(process.cwd(),ot,t)}import{expect as Et}from"@playwright/test";async function z(t,o){let e=await t.newPage();return await Et(async()=>{await e.goto(o),await I(e)}).toPass(),e}async function H(t,o){let e=await o.newPage();for(let{origin:a,localStorage:r}of t){let n=e.mainFrame();await n.goto(a),await n.evaluate(s=>{s.forEach(({name:l,value:u})=>{window.localStorage.setItem(l,u)})},r)}await e.close()}import Tt from"fs/promises";async function pt(t){await Tt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var vt=35e3;async function R(t,o){try{await Promise.race([t.close(),new Promise((e,a)=>setTimeout(()=>a(new Error("Context close timed out")),vt))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await pt(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}import ut from"fs";import Wt from"path";async function U(t){try{let o=y(t),e=Wt.resolve(o,"extension-path.txt");if(!ut.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=ut.readFileSync(e,"utf-8").trim();if(!a)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return a}catch(o){throw new Error(`\u274C Failed to get ${t} extension path: ${o.message}`)}}var D,ka=({slowMo:t=0,profileName:o}={})=>_t.extend({contextPath:async({browserName:e},a,r)=>{let n=await F(`${e}-${r.testId}`);await a(n)},context:async({context:e,contextPath:a},r)=>{let n=new P,s=y(n.name),l=await U(n.name),u=It.resolve(s,o??"wallet-data");if(!mt.existsSync(u))throw new Error("\u274C Cache for Petra wallet data not found. Create it first");mt.cpSync(u,a,{recursive:!0,force:!0});let d=[`--disable-extensions-except=${l}`,`--load-extension=${l}`];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 p=await Mt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:t});await p.grantPermissions(["clipboard-read"]);let{cookies:x,origins:f}=await e.storageState();x&&await p.addCookies(x),f&&f.length>0&&H(f,p);let B=await n.indexUrl();D=p.pages().find(g=>g.url().startsWith(B))||await z(p,B);for(let g of p.pages()){let v=g.url();(v.includes("about:blank")||v.includes(n.onboardingPath))&&await g.close()}await D.bringToFront(),await M(D),await r(p),await R(p,a)},petraPage:async({context:e},a)=>{await a(D)},petra:async({context:e},a)=>{let r=new A(D);await a(r)}});import{test as Ot}from"@playwright/test";import wt from"fs";import Ft from"path";import{chromium as Dt}from"@playwright/test";async function dt({wallet:t,workerInfo:o,profileName:e,slowMo:a}){let r=await F(o.workerIndex.toString()),n=y(t.name),s=Ft.resolve(n,e??"wallet-data");if(!wt.existsSync(s))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);wt.cpSync(s,r,{recursive:!0,force:!0});let l=await U(t.name),u=await Dt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${l}`],slowMo:process.env.HEADLESS?0:a}),d=await t.indexUrl(),p=u.pages()[0];return p||(p=await u.newPage()),await p.goto(d),{context:u,walletPage:p,contextPath:r}}var Ra=({slowMo:t,profileName:o,dappUrl:e}={})=>Ot.extend({workerScopeContents:[async({browser:a},r,n)=>{let s=new P,{context:l,contextPath:u,walletPage:d}=await dt({wallet:s,workerInfo:n,profileName:o,slowMo:t});await l.grantPermissions(["clipboard-read"]);for(let x of l.pages())x.url().includes(s.onboardingPath)&&await x.close();let p=new A(d);await p.unlock(),await r({wallet:p,walletPage:d,context:l}),await R(l,u)},{scope:"worker"}],dappPage:[async({workerScopeContents:a},r)=>{let{context:n}=a,s=await n.newPage();e&&await s.goto(e),await r(s)},{scope:"worker"}],petraPage:async({workerScopeContents:a},r)=>{await r(a.walletPage)},petra:async({workerScopeContents:a},r)=>{let n=new A(a.walletPage);await r(n)}});export{A as Petra,ka as petraFixture,Ra as petraWorkerScopeFixture};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import * as _playwright_test from '@playwright/test';
|
|
2
|
+
import { BrowserContext, Page } from '@playwright/test';
|
|
3
|
+
import { b as WalletProfileFixtureArgs, c as WorkerScopeFixtureArgs } from '../../types-COvLLLoP.js';
|
|
4
|
+
import { WorkerScopeFixture } from '../index.js';
|
|
5
|
+
|
|
6
|
+
declare class PhantomProfile {
|
|
7
|
+
readonly name: "phantom";
|
|
8
|
+
readonly onboardingPath = "/onboarding.html";
|
|
9
|
+
indexUrl(): Promise<string>;
|
|
10
|
+
promptUrl(): Promise<string>;
|
|
11
|
+
extensionId(): Promise<string>;
|
|
12
|
+
promptPage(context: BrowserContext): Promise<_playwright_test.Page>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type OnboardingArgs = {
|
|
16
|
+
mode: "create";
|
|
17
|
+
accountName: string;
|
|
18
|
+
toggleNetworkMode?: SwitchNetwork;
|
|
19
|
+
addWallet?: Array<AddAccountArgs>;
|
|
20
|
+
} | {
|
|
21
|
+
mode: "recovery phrase";
|
|
22
|
+
secretRecoveryPhrase: string;
|
|
23
|
+
toggleNetworkMode?: SwitchNetwork;
|
|
24
|
+
addWallet?: Array<AddAccountArgs>;
|
|
25
|
+
} | {
|
|
26
|
+
mode: "private key";
|
|
27
|
+
privateKey: string;
|
|
28
|
+
accountName: string;
|
|
29
|
+
chain: "Ethereum" | "Solana" | "Base" | "Sui" | "Bitcoin" | "Polygon" | "HyperEVM";
|
|
30
|
+
toggleNetworkMode?: SwitchNetwork;
|
|
31
|
+
addWallet?: Array<AddAccountArgs>;
|
|
32
|
+
};
|
|
33
|
+
type RenameAccountArgs = {
|
|
34
|
+
currentAccountName: string;
|
|
35
|
+
newAccountName: string;
|
|
36
|
+
};
|
|
37
|
+
type AddAccountArgs = {
|
|
38
|
+
privateKey: string;
|
|
39
|
+
accountName: string;
|
|
40
|
+
chain: "Ethereum" | "Solana" | "Base" | "Sui" | "Bitcoin" | "Polygon" | "HyperEVM";
|
|
41
|
+
};
|
|
42
|
+
type OptionalChains = "Ethereum" | "Monad" | "Base" | "Sui" | "Polygon" | "Bitcoin" | "Hyperevm";
|
|
43
|
+
type ToggleOptionalChainMode = "on" | "off";
|
|
44
|
+
type ToggleOptionalChainArgs = {
|
|
45
|
+
supportedChains: Array<OptionalChains>;
|
|
46
|
+
toggleMode: ToggleOptionalChainMode;
|
|
47
|
+
};
|
|
48
|
+
type SwitchNetwork = ({
|
|
49
|
+
mode: "on";
|
|
50
|
+
chain: "Solana";
|
|
51
|
+
network: "Solana Devnet" | "Solana Testnet" | "Solana Localnet";
|
|
52
|
+
} | {
|
|
53
|
+
mode: "on";
|
|
54
|
+
chain: "Ethereum";
|
|
55
|
+
network: "Ethereum Sepolia" | "Monad Testnet" | "Base Sepolia" | "Polygon Amoy" | "HyperEVM Testnet";
|
|
56
|
+
}) | {
|
|
57
|
+
mode: "off";
|
|
58
|
+
};
|
|
59
|
+
type GetAccountAddress = {
|
|
60
|
+
accountName: string;
|
|
61
|
+
chain: {
|
|
62
|
+
mode: "mainnet";
|
|
63
|
+
network: "Solana" | "Ethereum" | "Monad" | "Base" | "Sui" | "Polygon" | "Bitcoin" | "Hyperevm";
|
|
64
|
+
} | {
|
|
65
|
+
mode: "testnet";
|
|
66
|
+
network: "Devnet" | "Sepolia" | "Testnet";
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
type PhantomFixture = {
|
|
70
|
+
contextPath: string;
|
|
71
|
+
autoCloseNotification: undefined;
|
|
72
|
+
phantom: Phantom;
|
|
73
|
+
phantomPage: Page;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
declare class Phantom extends PhantomProfile {
|
|
77
|
+
page: Page;
|
|
78
|
+
constructor(page: Page);
|
|
79
|
+
/**
|
|
80
|
+
* Onboards the wallet.
|
|
81
|
+
* This function onboards the wallet by entering the password and other required information.
|
|
82
|
+
* @param {OnboardingArgs} args - The arguments required for onboarding.
|
|
83
|
+
* @param args.mode - Create a new wallet or import via private key / mnemonic.
|
|
84
|
+
* @param args.password - The password for the wallet.
|
|
85
|
+
* @param args.secretRecoveryPhrase - The secret recovery phrase for the wallet when importing a wallet.
|
|
86
|
+
* @example
|
|
87
|
+
* const phantom = new Phantom(page);
|
|
88
|
+
* await phantom.onboard({ mode: "importPrivateKey", password: "password", privateKey: "private key" });
|
|
89
|
+
*/
|
|
90
|
+
onboard({ ...args }: OnboardingArgs): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Unlocks the wallet by entering the password.
|
|
93
|
+
* @example
|
|
94
|
+
* const phantom = new Phantom(page);
|
|
95
|
+
* await phantom.unlock();
|
|
96
|
+
*/
|
|
97
|
+
unlock(): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Locks the wallet by entering the password.
|
|
100
|
+
* This function locks the wallet by opening the settings page and then locking the wallet.
|
|
101
|
+
* @example
|
|
102
|
+
* const phantom = new Phantom(page);
|
|
103
|
+
* await phantom.lock();
|
|
104
|
+
*/
|
|
105
|
+
lock(): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Renames an account in the wallet.
|
|
108
|
+
* @param {RenameAccountArgs} args - The arguments to rename the account.
|
|
109
|
+
* @param args.currentName - The current name of the active account.
|
|
110
|
+
* @param args.newAccountName - The new name of the account.
|
|
111
|
+
* @example
|
|
112
|
+
* const phantom = new Phantom(page);
|
|
113
|
+
* await phantom.renameAccount({ newAccountName: "New Account Name", currentAccountName: "Account 1" });
|
|
114
|
+
*/
|
|
115
|
+
renameAccount({ ...args }: RenameAccountArgs): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Switches the current account to the given account.
|
|
118
|
+
* @param {string} accountName - The name of the account to switch to.
|
|
119
|
+
* @example
|
|
120
|
+
* const phantom = new phantom(page);
|
|
121
|
+
* await phantom.switchAccount("Account 1");
|
|
122
|
+
*/
|
|
123
|
+
switchAccount(accountName: string): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Retrieves the current account's address.
|
|
126
|
+
* @param {string} accountName - The name of the account to switch to.
|
|
127
|
+
* @returns A promise that resolves with the current account's address as a string.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* const phantom = new Phantom(page);
|
|
131
|
+
* const address = await phantom.getAccountAddress();
|
|
132
|
+
*/
|
|
133
|
+
getAccountAddress({ accountName, chain }: GetAccountAddress): Promise<string>;
|
|
134
|
+
/**
|
|
135
|
+
* Adds an account to the wallet via a private key or mnemonic phrase.
|
|
136
|
+
* @param {{ accountName, ...args }: AddAccount} - The arguments to add the account.
|
|
137
|
+
* @param {string} args.accountName - The name of the account to add.
|
|
138
|
+
* @param {string} args.privateKey - The private key of the account to add, if the mode is "privateKey".
|
|
139
|
+
* @param {string[]} args.mnemonicPhrase - The mnemonic phrase of the account to add, if the mode is "mnemonic".
|
|
140
|
+
* @example
|
|
141
|
+
* const phantom = new Phantom(page);
|
|
142
|
+
* await phantom.addAccount(TBD);
|
|
143
|
+
*/
|
|
144
|
+
addAccount({ ...args }: AddAccountArgs): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Toggles the optional chains on or off.
|
|
147
|
+
* @param {ToggleOptionalchainArgs} args - The arguments to toggle the optional chains.
|
|
148
|
+
* @param {string} args.toggleMode - The mode of the optional chains. Can be either "on" or "off".
|
|
149
|
+
* @param {string[]} args.supportedChains - The list of supported chains.
|
|
150
|
+
* @example
|
|
151
|
+
* const phantom = new Phantom(page);
|
|
152
|
+
* await phantom.toggleOptionalChains({ supportedChains: ["Monad", "Bitcoin"], toggleMode: "off" });
|
|
153
|
+
*/
|
|
154
|
+
toggleOptionalChains({ toggleMode, supportedChains }: ToggleOptionalChainArgs): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Toggles the testnet network on or off.
|
|
157
|
+
* @param {SwitchNetwork} args - The arguments to toggle the testnet network.
|
|
158
|
+
* @param {string} args.mode - The mode of the testnet network. Can be either "on" or "off".
|
|
159
|
+
* @param {string} args.chain - The name of the chain to toggle the testnet network for. Can be either "Solana" or "Ethereum".
|
|
160
|
+
* @param {string} args.network - The name of the network to toggle the testnet network for. For example, "Solana Testnet".
|
|
161
|
+
* @example
|
|
162
|
+
* const phantom = new Phantom(page);
|
|
163
|
+
* await phantom.switchNetwork({ mode: "on", chain: "Solana", network: "Solana Testnet" });
|
|
164
|
+
*/
|
|
165
|
+
switchNetwork({ ...args }: SwitchNetwork): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Connects to an app by clicking on the "Connect to app" button.
|
|
168
|
+
* If an account is provided, it will be selected before connecting to the app.
|
|
169
|
+
* @param {string} [account] - The account to select before connecting to the app.
|
|
170
|
+
* @example
|
|
171
|
+
* const phantom = new Phantom(page);
|
|
172
|
+
* await phantom.connectToApp("Account 1");
|
|
173
|
+
*/
|
|
174
|
+
connectToApp(account?: string): Promise<void>;
|
|
175
|
+
/**
|
|
176
|
+
* Confirms a transaction in the wallet by clicking on the "Confirm" button.
|
|
177
|
+
* @example
|
|
178
|
+
* const phantom = new Phantom(page);
|
|
179
|
+
* await phantom.confirmTransaction();
|
|
180
|
+
*/
|
|
181
|
+
confirmTransaction(): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Rejects a transaction in the wallet by clicking on the "Reject" button.
|
|
184
|
+
* @example
|
|
185
|
+
* const phantom = new Phantom(page);
|
|
186
|
+
* await phantom.rejectTransaction();
|
|
187
|
+
*/
|
|
188
|
+
rejectTransaction(): Promise<void>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
declare const phantomFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PhantomFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
192
|
+
|
|
193
|
+
declare const phantomWorkerScopeFixture: ({ slowMo, profileName, dappUrl }?: WorkerScopeFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PhantomFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Phantom>>;
|
|
194
|
+
|
|
195
|
+
export { Phantom, phantomFixture, phantomWorkerScopeFixture };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
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"},Y={accountProfileContainer:"sortable-account-container"};var m={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 O({page:t,privateKey:e,accountName:o,chain:a}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.addAccountButton).click(),await t.locator(m.importPrivateKeyButton).click();let s=t.locator("span[id^='button--listbox-input--']"),p=await s.textContent(),u=t.locator("input[name='name']"),y=t.locator("textarea[name='privateKey']");p!==a&&(await s.click(),await t.locator("ul[id^='listbox--listbox-input--']").locator(`li[data-label='${a}']`).click()),await u.fill(o),await y.fill(e),await t.locator("button:has-text('Import')").click()}var I={confirmButton:"primary-button",cancelButton:"secondary-button"};async function Z(t){let e=t.getByTestId(I.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 xt}from"@playwright/test";import Bt from"zod";async function S(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 l of r)if((await l.textContent())?.includes(o)){n=l;break}if(!n)throw new Error(`Account with name "${o}" not found in the account list.`);await n.click()}async function tt(t,e){e&&await S(t,e);let o=t.getByTestId(I.confirmButton);await xt(o).toBeEnabled({timeout:15e3}),await o.click()}import Pt from"zod";var kt=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");async function et({page:t,accountName:e,chain:o}){let a=Pt.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 s=t.getByRole("button",{name:/Account Address(?:es)?/i});await s.waitFor({state:"visible",timeout:2e4});let u=await s.locator("div[data-name='row.pair'] > div").last().textContent();if(u&&Number(u)===1){await s.locator("> div > div").last().click(),await t.getByTestId("header--back").click();let h=t.getByTestId(B.closeMenuButton);await h.waitFor({state:"visible",timeout:15e3}),await h.click()}else{await s.click();let w=new RegExp(`${kt(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 A=t.getByTestId("header--back");await A.waitFor({state:"visible",timeout:15e3}),await A.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 ot(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click(),await t.getByTestId(B.lockWalletButton).click()}import{styleText as ut}from"util";import{expect as St}from"@playwright/test";function W(t){return new Promise(e=>setTimeout(e,t))}import it from"fs";import Ct from"path";import At from"path";var nt=".wallet-cache",rt=".wallet-context";var at="13.22.0",N="https://github.com/amaify/chainwright/releases/download/v0.1.0/",xe=`https://github.com/MetaMask/metamask-extension/releases/download/v${at}/metamask-chrome-${at}.zip`,Pe=`${N}solflare-wallet-extension-v2.19.1.zip`,ke=`${N}petra-wallet-extension-v2.4.8.zip`,Ae=`${N}phantom-wallet-extension-v26.10.0.zip`,Ce=`${N}meteor-wallet-extension-v0.7.0.zip`,be=`${N}keplr-wallet-extension-v0.13.3.zip`;function P(t){return At.resolve(process.cwd(),nt,t)}async function R(t){let e=P(t),o=Ct.resolve(e,"password.txt");try{if(!it.existsSync(o))throw new Error("\u274C password.txt not found. Run setup script first.");return it.readFileSync(o,"utf-8")}catch(a){throw new Error(`\u274C Failed to get ${t} password from cache: ${a.message}`)}}import{expect as bt}from"@playwright/test";async function ct({context:t,path:e,locator:o}){let a;try{await bt.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 Tt(a,o),await a.setViewportSize({width:360,height:592}),a}async function Tt(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 st from"fs";import It from"path";async function lt(t){let e=P(t),o=It.resolve(e,"extension-id.txt");try{if(!st.existsSync(o))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return st.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 lt(this.name)}async promptPage(e){let o=await this.promptUrl();return await ct({context:e,path:o,locator:"div[id='root']"})}};async function v(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 W(300)}}async function D({page:t,currentAccountName:e,newAccountName:o}){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.manageAccountsButton).click();let l=await t.getByTestId(Y.accountProfileContainer).locator("div[data-testid^='manage-accounts-sortable'] div > p").all(),s=null;for(let f of l)if((await f.textContent())?.toLowerCase()===e.toLowerCase()){s=f;break}if(!s)throw new Error(`Account with name "${e}" not found`);await s.click(),await t.locator("button:has-text('Account Name')").click();let u=t.locator("input[name='name']");await u.clear(),await u.fill(o),await t.getByTestId("primary-button").click(),await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function $(t){await t.getByTestId(g.openMenuButton).click(),await t.getByTestId(g.settingsButton).click()}async function U({page:t,...e}){await $(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:p}=e;await t.locator(`button:has-text("${p}")`).click()}if(e.mode==="on"&&e.chain==="Ethereum"){let{network:p}=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("${p}")`).click()}await t.getByTestId("header--back").click(),await t.getByTestId(B.closeMenuButton).click()}async function z({page:t,addWallet:e,...o}){console.info(ut("yellowBright",`
|
|
3
|
+
Phantom onboarding started...`,{validateStream:!1}));let a=await R("phantom");if(o.mode==="create"){await t.locator(m.createNewWalletButton).click(),await t.getByTestId(m.createSeedPhraseWalletButton).click();let f=t.getByTestId(m.passwordInput),d=t.getByTestId(m.passwordConfirmInput),C=t.getByTestId(m.termsCheckBox),x=t.locator(m.continueButton);await f.fill(a),await d.fill(a),await C.click(),await x.click(),await x.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.getByTestId(m.recoveryPhraseSavedCheckbox).click(),await x.click(),await W(1e3),await x.click(),await t.locator(m.getStartedButton).last().click()}if(o.mode==="recovery phrase"){let h=o.secretRecoveryPhrase.split(" ");await t.locator(m.IAlreadyHaveAWalletButton).click(),await t.locator(m.importRecoveryPhraseButton).click();for(let[G,L]of Object.entries(h))await t.getByTestId(`${m.recoveryPhraseInput}-${G}`).fill(L);await t.locator(m.importWalletButton).click(),await t.locator("p:has-text('Finding accounts with activity')").waitFor({state:"detached",timeout:6e4});let A=t.locator(m.continueButton);await A.click();let b=t.getByTestId(m.passwordInput),M=t.getByTestId(m.passwordConfirmInput),J=t.getByTestId(m.termsCheckBox);await b.fill(a),await M.fill(a),await J.click(),await A.click(),await A.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(m.getStartedButton).last().click()}if(o.mode==="private key"){await t.locator(m.IAlreadyHaveAWalletButton).click();let{privateKey:h,chain:f,accountName:d}=o;await t.locator(m.importPrivateKeyButton).click();let x=t.locator("span[id='button--listbox-input--1']"),A=await x.textContent(),b=t.locator("input[name='name']"),M=t.locator("textarea[name='privateKey']");A!==f&&(await x.click(),await t.locator("ul[id='listbox--listbox-input--1']").locator(`li[data-label='${f}']`).click()),await b.fill(d),await M.fill(h),await t.locator("button:has-text('Import')").click();let q=t.getByTestId(m.passwordInput),Q=t.getByTestId(m.passwordConfirmInput),G=t.getByTestId(m.termsCheckBox);await q.fill(a),await Q.fill(a),await G.click();let L=t.locator(m.continueButton);await L.click(),await L.locator("> div > svg").waitFor({state:"detached",timeout:3e4}),await t.locator(m.getStartedButton).last().click()}let n=await t.context().newPage(),r=await new k().indexUrl();await n.goto(r);let s=await t.context().browser()?.newBrowserCDPSession(),p;await St.poll(async()=>{if(s){let{targetInfos:w}=await s.send("Target.getTargets"),f=w.filter(d=>d.title==="Phantom Wallet").find(d=>!d.attached&&d.url===r);return p=f,!!f}},{timeout:2e4}).toBe(!0),p&&await s?.send("Target.closeTarget",{targetId:p.targetId});let u=await n.getByTestId("home-header-account-name").textContent();if(o.mode==="create"){let{accountName:w}=o;await D({page:n,newAccountName:w,currentAccountName:"Account 1"})}if(e&&e.length>0){let w=!1;v(n,()=>w).catch(d=>console.error({error:d}));for(let{accountName:d,chain:C,privateKey:x}of e)await O({page:n,privateKey:x,accountName:d,chain:C}),w=!0;let f=o.mode==="create"?o.accountName:u;f&&await S(n,f)}o.toggleNetworkMode&&await U({page:n,...o.toggleNetworkMode}),await W(3e3),console.info(ut("greenBright","\u2728 Phantom onboarding completed successfully",{validateStream:!1}))}async function mt(t){let e=t.getByTestId(I.cancelButton);await t.getByTestId("approve-transaction").waitFor({state:"attached"}),await e.click()}async function pt({page:t,supportedChains:e,toggleMode:o="off"}){if(await $(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 l of e){let s=t.locator(`button[id='toggle-${l.toLowerCase()}']`),u=await s.locator(`label[data-testid='toggle-${l.toLowerCase()}-switch'] > input[aria-label='Toggle']`).isChecked().catch(()=>!1);o==="off"&&u&&await s.click(),o==="on"&&!u&&await s.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 T=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 ot(this.page)}async renameAccount({...e}){await D({page:this.page,...e})}async switchAccount(e){await S(this.page,e)}async getAccountAddress({accountName:e,chain:o}){return await et({page:this.page,accountName:e,chain:o})}async addAccount({...e}){await O({page:this.page,...e})}async toggleOptionalChains({toggleMode:e,supportedChains:o}){await pt({page:this.page,supportedChains:o,toggleMode:e})}async switchNetwork({...e}){await U({page:this.page,...e})}async connectToApp(e){await tt(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await Z(await this.promptPage(this.page.context()))}async rejectTransaction(){await mt(await this.promptPage(this.page.context()))}};import ft from"fs";import Ft from"path";import{test as Mt,chromium as Lt}from"@playwright/test";import vt from"path";async function _(t){return vt.resolve(process.cwd(),rt,t)}import{expect as Et}from"@playwright/test";async function j(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function F(t,e){let o=await t.newPage();return await Et(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(l=>{l.forEach(({name:s,value:p})=>{window.localStorage.setItem(s,p)})},n)}await o.close()}import Wt from"fs/promises";async function dt(t){await Wt.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Nt=35e3;async function H(t,e){try{await Promise.race([t.close(),new Promise((o,a)=>setTimeout(()=>a(new Error("Context close timed out")),Nt))])}catch(o){console.warn(`Browser context close did not complete cleanly: ${o.message}`)}try{await dt(e)}catch(o){console.error(`Failed to remove temporary context directory at ${e}. Error:`,o)}}import wt from"fs";import _t from"path";async function K(t){try{let e=P(t),o=_t.resolve(e,"extension-path.txt");if(!wt.existsSync(o))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let a=wt.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}`)}}var E,va=({slowMo:t=0,profileName:e}={})=>Mt.extend({contextPath:async({browserName:o},a,n)=>{let r=await _(`${o}-${n.testId}`);await a(r)},context:async({context:o,contextPath:a},n)=>{let r=new k,l=P(r.name),s=await K(r.name),p=Ft.resolve(l,e??"wallet-data");if(!ft.existsSync(p))throw new Error("\u274C Cache for Phantom wallet data not found. Create it first");ft.cpSync(p,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 u=await Lt.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${s}`,`--load-extension=${s}`],slowMo:process.env.HEADLESS?0:t});await u.grantPermissions(["clipboard-read"]);let{cookies:y,origins:w}=await o.storageState();y&&await u.addCookies(y),w&&w.length>0&&await X(w,u);let h=await r.indexUrl();E=u.pages().find(d=>d.url().startsWith(h))||await F(u,h);for(let d of u.pages())d.url().includes("about:blank")&&await d.close();await E.bringToFront(),await V(E),await n(u),await H(u,a)},phantomPage:async({context:o},a)=>{await a(E)},phantom:async({context:o},a)=>{let n=new T(E);await a(n)},autoCloseNotification:[async({context:o},a)=>{let n=!1,l=v(E,()=>n);await a(void 0),n=!0,await l.catch(s=>{console.error(`Auto close notification error: ${s.message}`)})},{auto:!0}]});import{test as Dt}from"@playwright/test";import gt from"fs";import Ot from"path";import{chromium as Rt}from"@playwright/test";async function ht({workerInfo:t,profileName:e,slowMo:o}){let a=new k,n=await _(t.workerIndex.toString()),r=P(a.name),l=Ot.resolve(r,e??"wallet-data");if(!gt.existsSync(l))throw new Error(`Cache for ${a.name} does not exist. Create it first!`);gt.cpSync(l,n,{recursive:!0,force:!0});let s=await K(a.name),p=await Rt.launchPersistentContext(n,{headless:!1,args:[`--disable-extensions-except=${s}`],slowMo:process.env.HEADLESS?0:o}),u=await a.indexUrl(),y=await F(p,u);return{context:p,walletPage:y,contextPath:n}}var ja=({slowMo:t,profileName:e,dappUrl:o}={})=>Dt.extend({workerScopeContents:[async({browser:a},n,r)=>{let{context:l,contextPath:s,walletPage:p}=await ht({workerInfo:r,profileName:e,slowMo:t});await l.grantPermissions(["clipboard-read"]);for(let y of l.pages())y.url().includes("about:blank")&&await y.close();let u=new T(p);await u.unlock(),await n({wallet:u,walletPage:p,context:l}),await H(l,s)},{scope:"worker"}],dappPage:[async({workerScopeContents:a},n)=>{let{context:r}=a,l=await r.newPage();o&&await l.goto(o),await n(l)},{scope:"worker"}],phantomPage:async({workerScopeContents:a},n)=>{await n(a.walletPage)},phantom:async({workerScopeContents:a},n)=>{let r=new T(a.walletPage);await n(r)},autoCloseNotification:[async({workerScopeContents:a},n)=>{let r=!1,l=()=>r,s=v(a.walletPage,l);await n(void 0),r=!0,await s.catch(p=>{console.error(`Auto close notification error: ${p.message}`)})},{auto:!0}]});export{T as Phantom,va as phantomFixture,ja as phantomWorkerScopeFixture};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as _playwright_test from '@playwright/test';
|
|
2
|
+
import { BrowserContext, Page } from '@playwright/test';
|
|
3
|
+
import z from 'zod';
|
|
4
|
+
import { b as WalletProfileFixtureArgs, c as WorkerScopeFixtureArgs } from '../../types-COvLLLoP.js';
|
|
5
|
+
import { WorkerScopeFixture } from '../index.js';
|
|
6
|
+
|
|
7
|
+
declare class SolflareProfile {
|
|
8
|
+
readonly name: "solflare";
|
|
9
|
+
readonly onboardingPath = "wallet.html#/onboard";
|
|
10
|
+
indexUrl(): Promise<string>;
|
|
11
|
+
promptUrl(): Promise<string>;
|
|
12
|
+
extensionId(): Promise<string>;
|
|
13
|
+
promptPage(context: BrowserContext): Promise<_playwright_test.Page>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type OnboardingArgs = {
|
|
17
|
+
recoveryPhrase: string;
|
|
18
|
+
walletName: string;
|
|
19
|
+
network?: "Mainnet" | "Devnet" | "Testnet";
|
|
20
|
+
addWallet?: Array<AddAccountArgs>;
|
|
21
|
+
};
|
|
22
|
+
type SwitchNetwork = Omit<Required<OnboardingArgs>, "recoveryPhrase">["network"];
|
|
23
|
+
declare const addAccountSchema: z.ZodObject<{
|
|
24
|
+
walletName: z.ZodString;
|
|
25
|
+
privateKey: z.ZodArray<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
type AddAccountArgs = z.infer<typeof addAccountSchema>;
|
|
28
|
+
type RenameAccountArgs = {
|
|
29
|
+
currentAccountName: string;
|
|
30
|
+
newAccountName: string;
|
|
31
|
+
};
|
|
32
|
+
type SolflareFixture = {
|
|
33
|
+
contextPath: string;
|
|
34
|
+
solflare: Solflare;
|
|
35
|
+
solflarePage: Page;
|
|
36
|
+
autoCloseNotification: undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
declare class Solflare extends SolflareProfile {
|
|
40
|
+
page: Page;
|
|
41
|
+
constructor(page: Page);
|
|
42
|
+
/**
|
|
43
|
+
* Onboards the wallet.
|
|
44
|
+
* This function onboards the wallet by entering the password and other required information.
|
|
45
|
+
* @param {OnboardingArgs} args - The arguments required for onboarding.
|
|
46
|
+
* @param args.mode - Create a new wallet or import via private key / mnemonic.
|
|
47
|
+
* @param args.password - The password for the wallet.
|
|
48
|
+
* @param args.secretRecoveryPhrase - The secret recovery phrase for the wallet when importing a wallet.
|
|
49
|
+
* @example
|
|
50
|
+
* const solflare = new Solflare(page);
|
|
51
|
+
* await solflare.onboard({ mode: "importPrivateKey", password: "password", privateKey: "private key" });
|
|
52
|
+
*/
|
|
53
|
+
onboard({ recoveryPhrase, network, addWallet, walletName }: OnboardingArgs): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Unlocks the wallet by entering the password.
|
|
56
|
+
* @example
|
|
57
|
+
* const solflare = new Solflare(page);
|
|
58
|
+
* await solflare.unlock();
|
|
59
|
+
*/
|
|
60
|
+
unlock(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Locks the wallet by entering the password.
|
|
63
|
+
* This function locks the wallet by opening the settings page and then locking the wallet.
|
|
64
|
+
* @example
|
|
65
|
+
* const solflare = new Solflare(page);
|
|
66
|
+
* await solflare.lock();
|
|
67
|
+
*/
|
|
68
|
+
lock(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Renames an account in the wallet.
|
|
71
|
+
* @param {Omit<RenameAccount, "page">} args - The arguments to rename the account.
|
|
72
|
+
* @param args.newAccountName - The new name of the account.
|
|
73
|
+
* @example
|
|
74
|
+
* const solflare = new Solflare(page);
|
|
75
|
+
* await solflare.renameAccount({ newAccountName: "New Account Name" });
|
|
76
|
+
*/
|
|
77
|
+
renameAccount({ currentAccountName, newAccountName }: RenameAccountArgs): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Switches the current network to the given network.
|
|
80
|
+
* @param {SwitchNetwork} networkName - The name of the network to switch to.
|
|
81
|
+
* @example
|
|
82
|
+
* const solflare = new Solflare(page);
|
|
83
|
+
* await solflare.switchNetwork("network name");
|
|
84
|
+
*/
|
|
85
|
+
switchNetwork(network: SwitchNetwork): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Switches the current account to the given account.
|
|
88
|
+
* @param {string} accountName - The name of the account to switch to.
|
|
89
|
+
* @example
|
|
90
|
+
* const solflare = new solflare(page);
|
|
91
|
+
* await solflare.switchAccount("Account 1");
|
|
92
|
+
*/
|
|
93
|
+
switchAccount(accountName: string): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Retrieves the current account's address.
|
|
96
|
+
* @returns A promise that resolves with the current account's address as a string.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const solflare = new Solflare(page);
|
|
100
|
+
* const address = await solflare.getAccountAddress();
|
|
101
|
+
*/
|
|
102
|
+
getAccountAddress(): Promise<string>;
|
|
103
|
+
/**
|
|
104
|
+
* Adds an account to the wallet via a private key or mnemonic phrase.
|
|
105
|
+
* @param {{ accountName, ...args }: AddAccount} - The arguments to add the account.
|
|
106
|
+
* @param {string} args.walletName - The name of the account to add.
|
|
107
|
+
* @param {string} args.privateKey - The private key of the account to add, if the mode is "privateKey".
|
|
108
|
+
* @example
|
|
109
|
+
* const solflare = new Solflare(page);
|
|
110
|
+
* await solflare.addAccount({ walletName: "Gamify", privateKey: "private key"});
|
|
111
|
+
*/
|
|
112
|
+
addAccount({ privateKey, walletName }: AddAccountArgs): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Connects to an app by clicking on the "Connect to app" button.
|
|
115
|
+
* If an account is provided, it will be selected before connecting to the app.
|
|
116
|
+
* @param {string} [account] - The account to select before connecting to the app.
|
|
117
|
+
* @example
|
|
118
|
+
* const solflare = new Solflare(page);
|
|
119
|
+
* await solflare.connectToApp("Account 1");
|
|
120
|
+
*/
|
|
121
|
+
connectToApp(account?: string): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Confirms a transaction in the wallet by clicking on the "Confirm" button.
|
|
124
|
+
* @example
|
|
125
|
+
* const solflare = new Solflare(page);
|
|
126
|
+
* await solflare.confirmTransaction();
|
|
127
|
+
*/
|
|
128
|
+
confirmTransaction(): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Rejects a transaction in the wallet by clicking on the "Reject" button.
|
|
131
|
+
* @example
|
|
132
|
+
* const solflare = new Solflare(page);
|
|
133
|
+
* await solflare.rejectTransaction();
|
|
134
|
+
*/
|
|
135
|
+
rejectTransaction(): Promise<void>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
declare const solflareFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & SolflareFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
139
|
+
|
|
140
|
+
declare const solflareWorkerScopeFixture: ({ slowMo, profileName, dappUrl }?: WorkerScopeFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & SolflareFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Solflare>>;
|
|
141
|
+
|
|
142
|
+
export { Solflare, solflareFixture, solflareWorkerScopeFixture };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var h={networkSettings:"li-settings-network",selectNetwork:"select-network",confirmModal:"modal-confirm",confifmButton:"btn-confirm",securityAndPrivacyButton:"nav-item-security-and-privacy",lockButton:"btn-lock"},A={portfolioButton:"nav-item-portfolio",walletSelectorButton:"icon-section-wallet-picker-arrow-right"};import C from"zod";var H=C.object({walletName:C.string().min(1,"Wallet name cannot be an empty string"),privateKey:C.array(C.number()).length(64)});async function b({page:t,privateKey:e,walletName:r}){let o=H.parse({privateKey:e,walletName:r});await t.getByTestId(A.walletSelectorButton).click(),await t.getByTestId("icon-btn-add").click(),await t.getByTestId("li-add-wallet-privateKey-add").click();let p=t.getByTestId("input-name"),c=t.getByTestId("input-private-key");await p.fill(o.walletName),await c.fill(`${o.privateKey}`),await t.getByTestId("btn-import").click(),await t.locator("span:has-text('My wallets')").waitFor({state:"attached"}),await t.getByRole("dialog").getByTestId("icon-btn-close").click()}import{expect as mt}from"@playwright/test";var v={approveButton:"btn-approve",rejectButton:"btn-reject"};async function K(t){let e=t.getByTestId(v.approveButton);await t.getByTestId("section-network-fee").waitFor({state:"attached",timeout:3e4}).catch(async()=>{console.error("Network fee section did not appear within the timeout period, which may indicate that the transaction confirmation popup did not load correctly."),await t.getByText("Network mismatch",{exact:!0}).isVisible().catch(()=>!1)&&(await t.getByRole("button",{name:"Close",exact:!0}).click(),console.error("A 'Network mismatch' error was detected in the popup. Closing the popup and aborting the transaction confirmation process."))});let o=t.locator("div[data-id='control-label']");await o.isVisible().catch(()=>!1)&&await o.click(),await mt(e).toBeEnabled(),await e.click()}async function E(t,e){await t.getByTestId("icon-section-wallet-picker-arrow-right").click();let o=t.getByTestId("list-item-m-title").filter({hasText:e}).locator("xpath=../..");if(!await o.isVisible().catch(()=>!1))throw new Error(`Account "${e}" not found. Make sure the account is onboarded or verify the account name.`);await o.click()}async function j(t,e){e&&await E(t,e),await t.getByRole("button",{name:"Connect",exact:!0}).click()}async function z(t){return await t.getByTestId("icon-section-wallet-picker-copy").click(),await t.evaluate(async()=>await navigator.clipboard.readText())}async function W(t){let e=t.getByRole("button",{name:"settings",exact:!0});await e.waitFor({state:"attached",timeout:3e4}),await e.click()}async function q(t){await W(t),await t.getByTestId(h.securityAndPrivacyButton).click(),await t.getByTestId("li-settings-lock").getByTestId(h.lockButton).click()}import{styleText as Y}from"util";import Q from"fs";import wt from"path";import ut from"path";var X=".wallet-cache",J=".wallet-context";var G="13.22.0",S="https://github.com/amaify/chainwright/releases/download/v0.1.0/",oe=`https://github.com/MetaMask/metamask-extension/releases/download/v${G}/metamask-chrome-${G}.zip`,re=`${S}solflare-wallet-extension-v2.19.1.zip`,ae=`${S}petra-wallet-extension-v2.4.8.zip`,ne=`${S}phantom-wallet-extension-v26.10.0.zip`,ie=`${S}meteor-wallet-extension-v0.7.0.zip`,se=`${S}keplr-wallet-extension-v0.13.3.zip`;function d(t){return ut.resolve(process.cwd(),X,t)}async function N(t){let e=d(t),r=wt.resolve(e,"password.txt");try{if(!Q.existsSync(r))throw new Error("\u274C password.txt not found. Run setup script first.");return Q.readFileSync(r,"utf-8")}catch(o){throw new Error(`\u274C Failed to get ${t} password from cache: ${o.message}`)}}var y={alreadyHaveAWalletButton:"btn-already-have-wallet",recoveryPhraseInput:"input-recovery-phrase",continueButton:"btn-continue",passwordInput:"input-new-password",repeatPasswordInput:"input-repeat-password",quickSetupButton:"btn-quick-setup",IAgreeButton:"btn-explore"};async function _({page:t,currentAccountName:e,newAccountName:r}){await t.getByTestId(A.walletSelectorButton).click();let a=t.locator(`button[data-testid^='li-wallets']:has-text('${e}')`);if(!await a.isVisible().catch(()=>!1))throw new Error(`Account "${e}" not found. Make sure the account is available.`);await a.hover({timeout:2e4}),await a.getByTestId("icon-btn-three-dots").click({timeout:2e4});let p=t.getByTestId("li-manage-wallet-rename-wallet");await p.click();let c=t.getByTestId("input-name");await c.clear(),await c.fill(r),await t.getByTestId("btn-save").click(),await p.waitFor({state:"attached",timeout:15e3}),await t.getByTestId("icon-btn-close").click()}async function F(t,e){await W(t);let o=t.getByTestId("li-settings-network").getByRole("combobox");await o.locator(" > p").textContent()!==e?(await o.click(),await t.getByTestId(h.selectNetwork).getByRole("option",{name:e,exact:!0}).click(),(e==="Devnet"||e==="Testnet")&&await t.getByTestId(h.confirmModal).getByTestId(h.confifmButton).click()):console.info(`Network is already set to ${e}`),await t.getByTestId(A.portfolioButton).click()}async function Z({page:t,recoveryPhrase:e,network:r,walletName:o,addWallet:a}){console.info(Y("yellowBright",`
|
|
2
|
+
Solflare onboarding started...`,{validateStream:!1}));let n=await N("solflare");await t.getByTestId(y.alreadyHaveAWalletButton).click();let p=e.split(" ");for(let[I,g]of Object.entries(p))await t.getByTestId(`${y.recoveryPhraseInput}-${Number(I)+1}`).fill(g);let c=t.getByTestId(y.continueButton);await c.click();let w=t.getByTestId(y.passwordInput),m=t.getByTestId(y.repeatPasswordInput);if(await w.fill(n),await m.fill(n),await c.click(),await t.locator("div",{hasText:"Detecting your existing accounts. This process can take up to a minute."}).waitFor({state:"detached"}),await t.getByTestId(y.quickSetupButton).click(),await t.getByTestId(y.IAgreeButton).click(),await _({page:t,currentAccountName:"Main Wallet",newAccountName:o}),r&&await F(t,r),a&&a?.length>0)for(let{privateKey:I,walletName:g}of a)await b({page:t,privateKey:I,walletName:g});console.info(Y("greenBright","\u2728 Solflare onboarding completed successfully",{validateStream:!1}))}async function tt(t){await t.getByTestId(v.rejectButton).click()}async function M(t){let e=await N("solflare");await t.getByTestId("input-password").fill(e),await t.getByTestId("btn-unlock").click(),await t.getByTestId("nav-main").waitFor({state:"attached",timeout:3e4})}import{expect as dt}from"@playwright/test";async function et({context:t,path:e,locator:r}){let o;try{await dt.poll(async()=>(o=t.pages().filter(a=>a.url().startsWith("chrome-extension://")).find(a=>a.url().match(e)),!!o),{timeout:3e4}).toBe(!0)}catch{let a=t.pages().filter(n=>n.url().startsWith("chrome-extension://")).map(n=>n.url());throw new Error(`Popup page with path "${e}" not found in context after 30s. Pages in context: ${JSON.stringify(a)}`)}if(!o)throw new Error(`Popup page with path ${e} not found in context.`);return await ft(o,r),await o.setViewportSize({width:360,height:592}),o}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 ot from"fs";import gt from"path";async function rt(t){let e=d(t),r=gt.resolve(e,"extension-id.txt");try{if(!ot.existsSync(r))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return ot.readFileSync(r,"utf-8")}catch(o){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${o.message}`)}}var x=class{name="solflare";onboardingPath="wallet.html#/onboard";async indexUrl(){return`chrome-extension://${await this.extensionId()}/wallet.html#/portfolio`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/confirm_popup.html`}async extensionId(){return await rt(this.name)}async promptPage(e){let r=await this.promptUrl();return await et({context:e,path:r,locator:"div[data-testid='page-dapp-connect'], div[data-testid='page-tx-sign']"})}};var P=class extends x{page;constructor(e){super(),this.page=e}async onboard({recoveryPhrase:e,network:r,addWallet:o,walletName:a}){await Z({page:this.page,recoveryPhrase:e,network:r,addWallet:o,walletName:a})}async unlock(){await M(this.page)}async lock(){await q(this.page)}async renameAccount({currentAccountName:e,newAccountName:r}){await _({page:this.page,currentAccountName:e,newAccountName:r})}async switchNetwork(e){await F(this.page,e)}async switchAccount(e){await E(this.page,e)}async getAccountAddress(){return await z(this.page)}async addAccount({privateKey:e,walletName:r}){await b({page:this.page,privateKey:e,walletName:r})}async connectToApp(e){await j(await this.promptPage(this.page.context()),e)}async confirmTransaction(){await K(await this.promptPage(this.page.context()))}async rejectTransaction(){await tt(await this.promptPage(this.page.context()))}};import st from"fs";import St from"path";import{test as Bt,chromium as kt}from"@playwright/test";import yt from"path";async function B(t){return yt.resolve(process.cwd(),J,t)}import{expect as xt}from"@playwright/test";async function k(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function U(t,e){let r=await t.newPage();return await xt(async()=>{await r.goto(e),await k(r)}).toPass(),r}async function V(t,e){let r=await e.newPage();for(let{origin:o,localStorage:a}of t){let n=r.mainFrame();await n.goto(o),await n.evaluate(l=>{l.forEach(({name:p,value:c})=>{window.localStorage.setItem(p,c)})},a)}await r.close()}import ht from"fs/promises";async function at(t){await ht.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var Pt=35e3;async function D(t,e){try{await Promise.race([t.close(),new Promise((r,o)=>setTimeout(()=>o(new Error("Context close timed out")),Pt))])}catch(r){console.warn(`Browser context close did not complete cleanly: ${r.message}`)}try{await at(e)}catch(r){console.error(`Failed to remove temporary context directory at ${e}. Error:`,r)}}import nt from"fs";import At from"path";async function O(t){try{let e=d(t),r=At.resolve(e,"extension-path.txt");if(!nt.existsSync(r))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let o=nt.readFileSync(r,"utf-8").trim();if(!o)throw new Error("\u274C extension-path.txt is empty. Run setup script first.");return o}catch(e){throw new Error(`\u274C Failed to get ${t} extension path: ${e.message}`)}}function it(t){return new Promise(e=>setTimeout(e,t))}async function L(t,e){let o=!1;for(;!e();){let a=e();if(a||o||t.isClosed())break;try{let n=t.locator("div[role='dialog']").locator("button[data-testid='icon-btn-whats-new-modal-close']");await n.isVisible().catch(()=>!1)&&(await n.click(),o=!0)}catch(n){console.error("[autoCloseSolflareNotification]: ",n)}if(a||o||t.isClosed())break;await it(150)}}var T,or=({slowMo:t=0,profileName:e}={})=>Bt.extend({contextPath:async({browserName:r},o,a)=>{let n=await B(`${r}-${a.testId}`);await o(n)},context:async({context:r,contextPath:o},a)=>{let n=new x,l=d(n.name),p=await O(n.name),c=St.resolve(l,e??"wallet-data");if(!st.existsSync(c))throw new Error("\u274C Cache for Solflare wallet data not found. Create it first");await st.promises.cp(c,o,{recursive:!0,force:!0});let w=[`--disable-extensions-except=${p}`,`--load-extension=${p}`];process.env.HEADLESS&&(w.push("--headless=new"),t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!"));let m=await kt.launchPersistentContext(o,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:t});await m.grantPermissions(["clipboard-read"]);let{cookies:f,origins:u}=await r.storageState();f&&await m.addCookies(f),u&&u.length>0&&V(u,m);let R=await n.indexUrl(),$=R.split("#")[0]??"";await m.waitForEvent("page",{predicate:g=>g.url().includes($),timeout:15e3}),T=m.pages().find(g=>g.url().startsWith($))||await U(m,R);for(let g of m.pages())g.url().includes("about:blank")&&await g.close();await M(T),await a(m),await D(m,o)},solflarePage:async({context:r},o)=>{await o(T)},solflare:async({context:r},o)=>{let a=new P(T);await o(a)},autoCloseNotification:[async({context:r},o)=>{let a=!1,l=L(T,()=>a);await o(void 0),a=!0,await l.catch(p=>{console.error(`Auto close notification error: ${p.message}`)})},{auto:!0}]});import{test as Ct}from"@playwright/test";import ct from"fs";import Tt from"path";import{chromium as It}from"@playwright/test";async function lt({workerInfo:t,profileName:e,slowMo:r}){let o=new x,a=await B(t.workerIndex.toString()),n=d(o.name),l=Tt.resolve(n,e??"wallet-data");if(!ct.existsSync(l))throw new Error(`Cache for ${o.name} does not exist. Create it first!`);ct.cpSync(l,a,{recursive:!0,force:!0});let p=await O(o.name),c=await It.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${p}`],slowMo:process.env.HEADLESS?0:r}),w=await o.indexUrl(),m=w.split("#")[0]??"";await c.waitForEvent("page",{predicate:u=>u.url().includes(m),timeout:15e3});let f=c.pages().find(u=>u.url().startsWith(m));f||(f=await c.newPage(),await f.goto(w),await k(f));for(let u of c.pages())u.url().includes("about:blank")&&await u.close();return{context:c,walletPage:f,contextPath:a}}var Pr=({slowMo:t,profileName:e,dappUrl:r}={})=>Ct.extend({workerScopeContents:[async({browser:o},a,n)=>{let{context:l,contextPath:p,walletPage:c}=await lt({workerInfo:n,profileName:e,slowMo:t});await l.grantPermissions(["clipboard-read"]);let w=new P(c);await w.unlock(),await a({wallet:w,walletPage:c,context:l}),await D(l,p)},{scope:"worker"}],dappPage:[async({workerScopeContents:o},a)=>{let{context:n}=o,l=await n.newPage();r&&await l.goto(r),await a(l)},{scope:"worker"}],solflarePage:async({workerScopeContents:o},a)=>{await a(o.walletPage)},solflare:async({workerScopeContents:o},a)=>{let n=new P(o.walletPage);await a(n)},autoCloseNotification:[async({workerScopeContents:o},a)=>{let n=!1,l=()=>n,p=L(o.walletPage,l);await a(void 0),n=!0,await p.catch(c=>{console.error(`Auto close notification error: ${c.message}`)})},{auto:!0}]});export{P as Solflare,or as solflareFixture,Pr as solflareWorkerScopeFixture};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tobelabs/chainwright",
|
|
3
|
-
"version": "0.6.1-alpha.
|
|
3
|
+
"version": "0.6.1-alpha.2",
|
|
4
4
|
"description": "An end-to-end testing framework built on top of Playwright for Web3 wallet interactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,17 +23,39 @@
|
|
|
23
23
|
"dapp"
|
|
24
24
|
],
|
|
25
25
|
"exports": {
|
|
26
|
-
"./
|
|
26
|
+
"./wallet": {
|
|
27
27
|
"types": "./dist/wallets/index.d.ts",
|
|
28
28
|
"import": "./dist/wallets/index.js"
|
|
29
29
|
},
|
|
30
|
+
"./keplr": {
|
|
31
|
+
"types": "./dist/wallets/keplr/index.d.ts",
|
|
32
|
+
"import": "./dist/wallets/keplr/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./metamask": {
|
|
35
|
+
"types": "./dist/wallets/metamask/index.d.ts",
|
|
36
|
+
"import": "./dist/wallets/metamask/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./meteor": {
|
|
39
|
+
"types": "./dist/wallets/meteor/index.d.ts",
|
|
40
|
+
"import": "./dist/wallets/meteor/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./petra": {
|
|
43
|
+
"types": "./dist/wallets/petra/index.d.ts",
|
|
44
|
+
"import": "./dist/wallets/petra/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./phantom": {
|
|
47
|
+
"types": "./dist/wallets/phantom/index.d.ts",
|
|
48
|
+
"import": "./dist/wallets/phantom/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./solflare": {
|
|
51
|
+
"types": "./dist/wallets/solflare/index.d.ts",
|
|
52
|
+
"import": "./dist/wallets/solflare/index.js"
|
|
53
|
+
},
|
|
30
54
|
"./core": {
|
|
31
55
|
"types": "./dist/core/index.d.ts",
|
|
32
56
|
"import": "./dist/core/index.js"
|
|
33
57
|
}
|
|
34
58
|
},
|
|
35
|
-
"main": "./dist/wallets/index.js",
|
|
36
|
-
"types": "./dist/wallets/index.d.ts",
|
|
37
59
|
"bin": {
|
|
38
60
|
"chainwright": "dist/cli/index.js"
|
|
39
61
|
},
|