@tobelabs/chainwright 0.6.0 → 0.6.1-alpha.1
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/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/dist/worker-scope-context-ze2hEh79.d.ts +12 -0
- package/package.json +24 -6
- package/dist/wallets/index.d.ts +0 -975
- package/dist/wallets/index.js +0 -16
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
var w={openSidebarMenuButton:"button[aria-label='open sidebar']",addWalletButton:"button:has-text('Add Wallet')",settingsButton:"button:has-text('Settings')",settingsMenuBackButton:"button[aria-label='Back']"};var d={importExistingWalletButton:'button:has-text("Import an existing wallet")',switchNetworkButton:"button[id^='menu-button']",privateKeyButton:"button:has-text('Private Key')",findMyAccountButton:"button:has-text('Find my account')"};async function N(t,o,e){let r=t.locator(d.switchNetworkButton).last();await r.scrollIntoViewIfNeeded();let a=await r.textContent(),c=o.split("net")[0]?.toLowerCase()??"";if(a?.toLowerCase()===c)return console.info(`
|
|
2
|
+
Already on ${o}, no need to switch network.`),"Exit";await r.click();let u=t.locator(e).last().locator(`> button:has-text('${o}')`);await u.click(),await t.locator("div > h2:has-text('Meteor Community')").isVisible().catch(()=>!1)&&await u.click()}async function A({page:t,newAccountName:o}){await t.locator(w.openSidebarMenuButton).click(),await t.locator("div:has(> h2):has(> svg)").click();let a=t.locator("input[placeholder='Ex. My Meteor Wallet']"),c=t.locator("button[type='submit']:has-text('Update')");await a.fill(o),await c.click(),await t.locator("div[id='root'] button[aria-label='Close']").click()}async function _({page:t,privateKey:o,accountName:e,network:r}){await t.locator(w.openSidebarMenuButton).click(),await t.locator(w.addWalletButton).click(),await N(t,r,"section[role='dialog'] div[role='menu']"),await t.locator(d.importExistingWalletButton).click(),await t.locator(d.privateKeyButton).click();let l=t.locator('button:has-text("Continue")');await l.scrollIntoViewIfNeeded(),await l.click(),await t.locator("textarea:not([disabled])").fill(o),await t.locator(d.findMyAccountButton).click(),await t.locator("button[type='submit'][data-loading]").waitFor({state:"detached",timeout:25e3}),await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click(),await A({page:t,newAccountName:e})}var k={approveButton:"button:has-text('Approve')",connectButton:"button:has-text('Connect')",cancelButton:"button:has-text('Cancel')"};import Z from"fs";import ht from"path";import ft from"path";var Y=".wallet-cache",Q=".wallet-context";var J="13.22.0",v="https://github.com/amaify/chainwright/releases/download/v0.1.0/",ro=`https://github.com/MetaMask/metamask-extension/releases/download/v${J}/metamask-chrome-${J}.zip`,ao=`${v}solflare-wallet-extension-v2.19.1.zip`,no=`${v}petra-wallet-extension-v2.4.8.zip`,io=`${v}phantom-wallet-extension-v26.10.0.zip`,co=`${v}meteor-wallet-extension-v0.7.0.zip`,so=`${v}keplr-wallet-extension-v0.13.3.zip`;function f(t){return ft.resolve(process.cwd(),Y,t)}async function F(t){let o=f(t),e=ht.resolve(o,"password.txt");try{if(!Z.existsSync(e))throw new Error("\u274C password.txt not found. Run setup script first.");return Z.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} password from cache: ${r.message}`)}}async function h(t){let o=await F("meteor"),e=t.locator("input[placeholder='Enter Password']"),r=t.locator('button:has-text("Unlock")');await e.fill(o),await r.click()}async function tt(t){await h(t),await t.locator(k.approveButton).click(),await t.locator("h2:has-text('Executing Transaction')").waitFor({state:"attached"}),await t.waitForEvent("close",{timeout:15e3})}async function B(t,o){if((await t.locator("div:has(button[type='button'][aria-label='open sidebar'])").nth(-2).locator("div:has(div > h2)").locator("div > h2").textContent())?.toLowerCase()===o.toLowerCase()){console.info(`
|
|
3
|
+
Switching to the ${o} account aborted. Account is already selected.`);return}await t.locator(w.openSidebarMenuButton).click();let l=await t.locator("div:has(div > button[type='button'][aria-label='Close'])").nth(-2).locator("div").nth(2).locator("> div").nth(1).locator("div").nth(1).locator("div > h2").all(),s=null;for(let m of l)if((await m.textContent())?.toLowerCase()===o.toLowerCase()){s=m;break}if(!s)throw new Error(`Account with name "${o}" not found.`);await s.click()}async function ot(t,o){await h(t),o&&await B(t,o);let e=t.getByRole("heading",{name:"Connect Request",exact:!0});await Promise.all([t.locator(k.connectButton).click(),e.waitFor({state:"detached",timeout:3e4})])}async function et(t){return await t.locator("div:has(button[type='button'][aria-label='open sidebar'])").nth(-2).locator("div:has(div > h2)").locator("div > svg").click(),await t.evaluate(async()=>await navigator.clipboard.readText())}async function rt(t){await t.locator(w.openSidebarMenuButton).click(),await t.locator("button:has-text('Lock Wallet')").click()}import{styleText as O}from"util";function E(t){return new Promise(o=>setTimeout(o,t))}import{expect as xt}from"@playwright/test";async function at({context:t,path:o,locator:e}){let r;try{await xt.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 gt(r,e),await r.setViewportSize({width:360,height:592}),r}async function gt(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 nt from"fs";import yt from"path";async function it(t){let o=f(t),e=yt.resolve(o,"extension-id.txt");try{if(!nt.existsSync(e))throw new Error("\u274C extension-id.txt not found. Run setup script first.");return nt.readFileSync(e,"utf-8")}catch(r){throw new Error(`\u274C Failed to get ${t} extension ID from cache: ${r.message}`)}}var x=class{name="meteor";onboardingPath="ext_index_popup.html";async indexUrl(){return`chrome-extension://${await this.extensionId()}/ext_index_popup.html`}async promptUrl(){return`chrome-extension://${await this.extensionId()}/ext_index.html`}async extensionId(){return await it(this.name)}async promptPage(o){let e=await this.promptUrl();return await at({context:o,path:e,locator:"div[id='root']"})}};async function C(t){await t.locator(w.openSidebarMenuButton).click(),await t.locator(w.settingsButton).click()}async function I(t,o){if(await C(t),await N(t,o,"div[role='menu']")==="Exit"){let c=t.locator(w.settingsMenuBackButton);await c.scrollIntoViewIfNeeded(),await c.click();return}let r=t.locator("p:has-text('Available Balance')");if(await E(1e3),!await r.isVisible().catch(()=>!1))throw new Error([`There is no associated account for the ${o} network in your wallet.`,`Please add an account to the ${o} network in your wallet using the "addAccount" method.`,"NOTE: For the account to be persisted across tests, do this when onboarding the wallet."].join(`
|
|
4
|
+
`))}async function R({page:t,privateKey:o,network:e,accountName:r,addWallet:a}){console.info(O("yellowBright",`
|
|
5
|
+
Meteor onboarding started...`,{validateStream:!1}));let c=await F("meteor"),u=await new x().indexUrl();await t.goto(u);let l=t.locator(d.switchNetworkButton),s=await l.textContent(),m=e.split("net")[0]?.toLowerCase()??"";s?.toLowerCase().includes(m)||(await l.click(),await t.locator("div[role='menu']").locator(`> button:has-text('${e}')`).click());let y=t.locator("input[placeholder='Enter Password']"),S=t.locator("input[placeholder='Confirm Password']"),K=t.locator("label.chakra-checkbox .chakra-checkbox__control"),g=t.locator('button:has-text("Continue")');await y.fill(c),await S.fill(c),await K.click(),await g.click(),await t.locator(d.importExistingWalletButton).click(),await t.locator(d.privateKeyButton).click(),await g.scrollIntoViewIfNeeded(),await g.click(),await t.locator("textarea:not([disabled])").fill(o);let H=t.locator(d.findMyAccountButton);await H.click();let z=t.locator("button[type='submit'][data-loading]");if(await z.waitFor({state:"detached",timeout:25e3}),await t.getByRole("status").locator("div[id='toast-1-title']:has-text('No Account Found')").isVisible().catch(()=>!1)){let b=5,M=!1;for(;b>0;){if(console.info(`
|
|
6
|
+
Retrying search for account. ${b} attempts left`),await E(15e3),await H.click(),await z.waitFor({state:"detached",timeout:2e4}),await t.locator("div:has-text('Import Your Account')").nth(-2).locator("button").isVisible().catch(()=>!1)){M=!0;break}b-=1}if(!M)throw Error(O("redBright",["No Account Found","Account associated with the private key not found. Please make sure you are trying to import an account on the correct network(Mainnet/Testnet)."].join(`
|
|
7
|
+
`),{validateStream:!1}))}await t.locator("button:not([aria-label='Back'],[id^='menu-button']):has-text('Account')").click();let j=t.locator("section[role='dialog']").locator("button:has-text('Close')").first();if(await j.isVisible().then(()=>!0).catch(()=>!1)&&await j.click(),await A({page:t,newAccountName:r}),a&&a.length>0){for(let{privateKey:q,accountName:G,network:dt}of a)await _({page:t,privateKey:q,accountName:G,network:dt});await C(t);let b=t.locator(d.switchNetworkButton).last();await b.scrollIntoViewIfNeeded();let M=await b.textContent(),X=e.split("net")[0]?.toLowerCase()??"";M?.toLowerCase()!==X&&await I(t,e),await B(t,r)}await E(3e3),console.info(O("greenBright","\u2728 Meteor onboarding completed successfully"))}async function ct(t){await h(t),await t.locator(k.cancelButton).click()}var P=class extends x{page;constructor(o){super(),this.page=o}async onboard({network:o,privateKey:e,accountName:r,addWallet:a}){await R({page:this.page,network:o,privateKey:e,accountName:r,addWallet:a})}async unlock(){await h(this.page)}async lock(){await rt(this.page)}async renameAccount({newAccountName:o}){await A({page:this.page,newAccountName:o})}async switchNetwork(o){await I(this.page,o)}async switchAccount(o){await B(this.page,o)}async getAccountAddress(){return await et(this.page)}async addAccount({accountName:o,network:e,privateKey:r}){await _({page:this.page,accountName:o,network:e,privateKey:r})}async openSettings(){await C(this.page)}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 ct(await this.promptPage(this.page.context()))}};import pt from"fs";import Ct from"path";import{test as St,chromium as Mt}from"@playwright/test";import bt from"path";async function T(t){return bt.resolve(process.cwd(),Q,t)}import{expect as Pt}from"@playwright/test";async function U(t){await t.waitForLoadState("load",{timeout:15e3}),await t.waitForLoadState("domcontentloaded",{timeout:15e3})}async function $(t,o){let e=await t.newPage();return await Pt(async()=>{await e.goto(o),await U(e)}).toPass(),e}async function V(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(p=>{p.forEach(({name:u,value:l})=>{window.localStorage.setItem(u,l)})},a)}await e.close()}import At from"fs/promises";async function st(t){await At.rm(t,{maxRetries:50,retryDelay:500,recursive:!0,force:!0})}var kt=35e3;async function L(t,o){try{await Promise.race([t.close(),new Promise((e,r)=>setTimeout(()=>r(new Error("Context close timed out")),kt))])}catch(e){console.warn(`Browser context close did not complete cleanly: ${e.message}`)}try{await st(o)}catch(e){console.error(`Failed to remove temporary context directory at ${o}. Error:`,e)}}import lt from"fs";import Bt from"path";async function D(t){try{let o=f(t),e=Bt.resolve(o,"extension-path.txt");if(!lt.existsSync(e))throw new Error("\u274C extension-path.txt not found. Run setup script first.");let r=lt.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}`)}}var W,Ar=({slowMo:t=0,profileName:o}={})=>St.extend({contextPath:async({browserName:e},r,a)=>{let c=await T(`${e}-${a.testId}`);await r(c)},context:async({context:e,contextPath:r},a)=>{let c=new x,p=f(c.name),u=await D(c.name),l=Ct.resolve(p,o??"wallet-data");if(!pt.existsSync(l))throw new Error("\u274C Cache for Meteor wallet data not found. Create it first");pt.cpSync(l,r,{recursive:!0,force:!0}),process.env.HEADLESS&&t>0&&console.warn("\u26A0\uFE0F Slow motion makes no sense in headless mode. It will be ignored!");let s=await Mt.launchPersistentContext(r,{headless:!1,args:[`--disable-extensions-except=${u}`,`--load-extension=${u}`],slowMo:process.env.HEADLESS?0:t});await s.grantPermissions(["clipboard-read"]);let{cookies:m,origins:y}=await e.storageState();m&&await s.addCookies(m),y&&y.length>0&&V(y,s);let S=await c.indexUrl();W=s.pages().find(g=>g.url().startsWith(S))||await $(s,S);for(let g of s.pages())g.url().includes("about:blank")&&await g.close();await W.bringToFront(),await h(W),await a(s),await L(s,r)},meteorPage:async({context:e},r)=>{await r(W)},meteor:async({context:e},r)=>{let a=new P(W);await r(a)}});import{test as Tt}from"@playwright/test";import mt from"fs";import vt from"path";import{chromium as Et}from"@playwright/test";async function ut({wallet:t,workerInfo:o,profileName:e,slowMo:r}){let a=await T(o.workerIndex.toString()),c=f(t.name),p=vt.resolve(c,e??"wallet-data");if(!mt.existsSync(p))throw new Error(`Cache for ${t.name} does not exist. Create it first!`);mt.cpSync(p,a,{recursive:!0,force:!0});let u=await D(t.name),l=await Et.launchPersistentContext(a,{headless:!1,args:[`--disable-extensions-except=${u}`],slowMo:process.env.HEADLESS?0:r}),s=await t.indexUrl(),m=l.pages()[0];return m||(m=await l.newPage()),await m.goto(s),{context:l,walletPage:m,contextPath:a}}var Or=({slowMo:t,profileName:o,dappUrl:e}={})=>Tt.extend({workerScopeContents:[async({browser:r},a,c)=>{let{context:p,contextPath:u,walletPage:l}=await ut({workerInfo:c,profileName:o,slowMo:t,wallet:new x});await p.grantPermissions(["clipboard-read"]);for(let m of p.pages())m.url().includes("about:blank")&&await m.close();let s=new P(l);await s.unlock(),await a({wallet:s,walletPage:l,context:p}),await L(p,u)},{scope:"worker"}],dappPage:[async({workerScopeContents:r},a)=>{let{context:c}=r,p=await c.newPage();e&&await p.goto(e),await a(p)},{scope:"worker"}],meteorPage:async({workerScopeContents:r},a)=>{await a(r.walletPage)},meteor:async({workerScopeContents:r},a)=>{let c=new P(r.walletPage);await a(c)}});export{P as Meteor,Ar as meteorFixture,Or as meteorWorkerScopeFixture};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as _playwright_test from '@playwright/test';
|
|
2
|
+
import { Page, BrowserContext } from '@playwright/test';
|
|
3
|
+
import { b as WalletProfileFixtureArgs, c as WorkerScopeFixtureArgs } from '../../types-dJRYhQT9.js';
|
|
4
|
+
import { W as WorkerScopeFixture } from '../../worker-scope-context-ze2hEh79.js';
|
|
5
|
+
|
|
6
|
+
type RenameAccount = {
|
|
7
|
+
page: Page;
|
|
8
|
+
newAccountName: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
declare class PetraProfile {
|
|
12
|
+
readonly name: "petra";
|
|
13
|
+
readonly onboardingPath = "/onboarding.html";
|
|
14
|
+
indexUrl(): Promise<string>;
|
|
15
|
+
promptUrl(): Promise<string>;
|
|
16
|
+
extensionId(): Promise<string>;
|
|
17
|
+
promptPage(context: BrowserContext): Promise<_playwright_test.Page>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type OnboardingArgs = {
|
|
21
|
+
mode: "create";
|
|
22
|
+
accountName: string;
|
|
23
|
+
addWallet?: Array<AddAccount>;
|
|
24
|
+
} | {
|
|
25
|
+
mode: "importMnemonic";
|
|
26
|
+
accountName: string;
|
|
27
|
+
secretRecoveryPhrase: string;
|
|
28
|
+
addWallet?: Array<AddAccount>;
|
|
29
|
+
} | {
|
|
30
|
+
mode: "importPrivateKey";
|
|
31
|
+
accountName: string;
|
|
32
|
+
privateKey: string;
|
|
33
|
+
addWallet?: Array<AddAccount>;
|
|
34
|
+
};
|
|
35
|
+
type SwitchNetwork = "Mainnet" | "Testnet" | "Devnet" | "Shelbynet" | "Netna";
|
|
36
|
+
type AddAccount = {
|
|
37
|
+
mode: "privateKey";
|
|
38
|
+
accountName: string;
|
|
39
|
+
privateKey: string;
|
|
40
|
+
} | {
|
|
41
|
+
mode: "mnemonic";
|
|
42
|
+
accountName: string;
|
|
43
|
+
mnemonicPhrase: string;
|
|
44
|
+
};
|
|
45
|
+
type PetraFixture = {
|
|
46
|
+
contextPath: string;
|
|
47
|
+
petra: Petra;
|
|
48
|
+
petraPage: Page;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
declare class Petra extends PetraProfile {
|
|
52
|
+
page: Page;
|
|
53
|
+
constructor(page: Page);
|
|
54
|
+
/**
|
|
55
|
+
* Onboards the wallet.
|
|
56
|
+
* This function onboards the wallet by entering the password and other required information.
|
|
57
|
+
* @param {OnboardingArgs} args - The arguments required for onboarding.
|
|
58
|
+
* @param args.mode - Create a new wallet or import via private key / mnemonic.
|
|
59
|
+
* @param args.password - The password for the wallet.
|
|
60
|
+
* @param args.secretRecoveryPhrase - The secret recovery phrase for the wallet when importing a wallet.
|
|
61
|
+
* @example
|
|
62
|
+
* const petra = new Petra(page);
|
|
63
|
+
* await petra.onboard({ mode: "importPrivateKey", password: "password", privateKey: "private key" });
|
|
64
|
+
*/
|
|
65
|
+
onboard(args: OnboardingArgs): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Unlocks the wallet by entering the password.
|
|
68
|
+
* @example
|
|
69
|
+
* const petra = new Petra(page);
|
|
70
|
+
* await petra.unlock();
|
|
71
|
+
*/
|
|
72
|
+
unlock(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Locks the wallet by entering the password.
|
|
75
|
+
* This function locks the wallet by opening the settings page and then locking the wallet.
|
|
76
|
+
* @example
|
|
77
|
+
* const petra = new Petra(page);
|
|
78
|
+
* await petra.lock();
|
|
79
|
+
*/
|
|
80
|
+
lock(): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Renames an account in the wallet.
|
|
83
|
+
* @param {Omit<RenameAccount, "page">} args - The arguments to rename the account.
|
|
84
|
+
* @param args.newAccountName - The new name of the account.
|
|
85
|
+
* @example
|
|
86
|
+
* const petra = new Petra(page);
|
|
87
|
+
* await petra.renameAccount({ newAccountName: "New Account Name" });
|
|
88
|
+
*/
|
|
89
|
+
renameAccount({ newAccountName }: Omit<RenameAccount, "page">): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Switches the current network to the given network.
|
|
92
|
+
* @param {SwitchNetwork} networkName - The name of the network to switch to.
|
|
93
|
+
* @example
|
|
94
|
+
* const petra = new Petra(page);
|
|
95
|
+
* await petra.switchNetwork("Testnet");
|
|
96
|
+
*/
|
|
97
|
+
switchNetwork(networkName: SwitchNetwork): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Switches the current account to the given account.
|
|
100
|
+
* @param {string} accountName - The name of the account to switch to.
|
|
101
|
+
* @example
|
|
102
|
+
* const petra = new Petra(page);
|
|
103
|
+
* await petra.switchAccount("Account 1");
|
|
104
|
+
*/
|
|
105
|
+
switchAccount(accountName: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Retrieves the current account's address.
|
|
108
|
+
* @returns A promise that resolves with the current account's address as a string.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* const petra = new Petra(page);
|
|
112
|
+
* const address = await petra.getAccountAddress();
|
|
113
|
+
*/
|
|
114
|
+
getAccountAddress(): Promise<string>;
|
|
115
|
+
/**
|
|
116
|
+
* Adds an account to the wallet via a private key or mnemonic phrase.
|
|
117
|
+
* @param {{ accountName, ...args }: AddAccount} - The arguments to add the account.
|
|
118
|
+
* @param {string} args.accountName - The name of the account to add.
|
|
119
|
+
* @param {string} args.privateKey - The private key of the account to add, if the mode is "privateKey".
|
|
120
|
+
* @param {string[]} args.mnemonicPhrase - The mnemonic phrase of the account to add, if the mode is "mnemonic".
|
|
121
|
+
* @example
|
|
122
|
+
* const petra = new Petra(page);
|
|
123
|
+
* await petra.addAccount({ accountName: "Account 1", privateKey: "private key", mode: "privateKey" });
|
|
124
|
+
*/
|
|
125
|
+
addAccount({ accountName, ...args }: AddAccount): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Connects to an app by clicking on the "Connect to app" button.
|
|
128
|
+
* If an account is provided, it will be selected before connecting to the app.
|
|
129
|
+
* @param {string} [account] - The account to select before connecting to the app.
|
|
130
|
+
* @example
|
|
131
|
+
* const petra = new Petra(page);
|
|
132
|
+
* await petra.connectToApp("Account 1");
|
|
133
|
+
*/
|
|
134
|
+
connectToApp(account?: string): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Confirms a transaction in the wallet by clicking on the "Approve" button.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* const petra = new Petra(page);
|
|
140
|
+
* await petra.confirmTransaction();
|
|
141
|
+
*/
|
|
142
|
+
confirmTransaction(): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Rejects a transaction in the wallet by clicking on the "Cancel" button.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* const petra = new Petra(page);
|
|
148
|
+
* await petra.rejectTransaction();
|
|
149
|
+
* */
|
|
150
|
+
rejectTransaction(): Promise<void>;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
declare const petraFixture: ({ slowMo, profileName }?: WalletProfileFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PetraFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
|
|
154
|
+
|
|
155
|
+
declare const petraWorkerScopeFixture: ({ slowMo, profileName, dappUrl }?: WorkerScopeFixtureArgs) => _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & PetraFixture, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & WorkerScopeFixture<Petra>>;
|
|
156
|
+
|
|
157
|
+
export { Petra, petraFixture, petraWorkerScopeFixture };
|
|
@@ -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-dJRYhQT9.js';
|
|
4
|
+
import { W as WorkerScopeFixture } from '../../worker-scope-context-ze2hEh79.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-dJRYhQT9.js';
|
|
5
|
+
import { W as WorkerScopeFixture } from '../../worker-scope-context-ze2hEh79.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 };
|