@walletwright/core 0.2.1 → 0.3.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/CHANGELOG.md +48 -0
- package/README.md +2 -2
- package/dist/{cache-Bwv2i-hv.mjs → cache-BaWu5vyM.mjs} +242 -174
- package/dist/cache-BaWu5vyM.mjs.map +1 -0
- package/dist/cli.d.mts +23 -11
- package/dist/cli.mjs +97 -55
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +20 -30
- package/dist/index.mjs +88 -86
- package/dist/index.mjs.map +1 -1
- package/dist/mock-standard.d.mts +4 -19
- package/dist/mock-standard.mjs +16 -16
- package/dist/mock-standard.mjs.map +1 -1
- package/dist/mock.d.mts +3 -7
- package/dist/mock.mjs +14 -12
- package/dist/mock.mjs.map +1 -1
- package/dist/{types-CWxz_dJe.d.mts → types-CFQpWZhv.d.mts} +7 -30
- package/package.json +6 -5
- package/dist/cache-Bwv2i-hv.mjs.map +0 -1
|
@@ -52,12 +52,7 @@ type NetworkConfig = {
|
|
|
52
52
|
type NetworkActions = WalletActionsFor<NetworkApi>;
|
|
53
53
|
/** Lock and unlock the wallet itself, from its own UI. */
|
|
54
54
|
type SettingsActions = WalletActionsFor<SettingsApi>;
|
|
55
|
-
/**
|
|
56
|
-
* Optional, per-wallet capabilities beyond the universal connect/sign flow. A wallet declares only
|
|
57
|
-
* what has actually been driven against the real extension, so the registry never claims support it
|
|
58
|
-
* doesn't have: `network` is meaningless for Slush (Sui), and Phantom's settings UI has no analogue
|
|
59
|
-
* for much of MetaMask's. Anything undeclared throws a clear error at call time.
|
|
60
|
-
*/
|
|
55
|
+
/** Optional wallet capabilities beyond connect and sign. Undeclared actions throw at call time. */
|
|
61
56
|
type WalletActions = {
|
|
62
57
|
accounts?: AccountActions;
|
|
63
58
|
network?: NetworkActions;
|
|
@@ -70,12 +65,7 @@ type WalletActions = {
|
|
|
70
65
|
type WalletDefinition = {
|
|
71
66
|
/** Optional capabilities beyond connect/sign. Omit a group the wallet can't (or doesn't) drive. */
|
|
72
67
|
actions?: WalletActions;
|
|
73
|
-
/**
|
|
74
|
-
* Controls that only exist while a request is on screen, used to tell a real approval from the
|
|
75
|
-
* wallet's idle UI: MetaMask's approval window can render its home screen, buttons and all. A
|
|
76
|
-
* wallet that doesn't declare this falls back to "any button is visible", which is enough for a
|
|
77
|
-
* window that only ever opens for a request.
|
|
78
|
-
*/
|
|
68
|
+
/** Controls that exist only for a pending request, excluding idle wallet UI. */
|
|
79
69
|
approvalControls?: (popup: Page) => Locator;
|
|
80
70
|
/**
|
|
81
71
|
* Click the approve/confirm button in an approval popup (connect or sign). `password` is provided
|
|
@@ -91,12 +81,7 @@ type WalletDefinition = {
|
|
|
91
81
|
* holding the DB), e.g. forcing `completedOnboarding=true` in MetaMask's leveldb.
|
|
92
82
|
*/
|
|
93
83
|
finalizeCache?: (profileDir: string, extensionId: string) => Promise<void>;
|
|
94
|
-
/**
|
|
95
|
-
* Whether this wallet's approval window surfaces as a page when the browser runs headless, so the
|
|
96
|
-
* engine can find and drive it. Declared only once verified against the real extension: MetaMask's
|
|
97
|
-
* window is created but never exposed, and `launchWallet` refuses headless without this rather
|
|
98
|
-
* than hanging at the first approval.
|
|
99
|
-
*/
|
|
84
|
+
/** Whether the real wallet exposes approval windows as Playwright pages in headless mode. */
|
|
100
85
|
headlessApprovals?: boolean;
|
|
101
86
|
/** Run the import-from-seed onboarding flow. */
|
|
102
87
|
importWallet: (page: Page, seedPhrase: string, password: string) => Promise<void>;
|
|
@@ -122,11 +107,8 @@ type WalletDefinition = {
|
|
|
122
107
|
* rather than returning a page that never rendered. The returned page stays open as `Wallet.home`.
|
|
123
108
|
*/
|
|
124
109
|
reachUnlockScreen: (context: BrowserContext, extensionId: string) => Promise<Page>;
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
* a wallet declares it only once it has been driven against the real extension.
|
|
128
|
-
*/
|
|
129
|
-
reject?: (popup: Page) => Promise<void>;
|
|
110
|
+
/** Click the cancel/reject button in an approval popup, the counterpart of `approve`. */
|
|
111
|
+
reject: (popup: Page) => Promise<void>;
|
|
130
112
|
/** Unlock the wallet on its (already-open) home page. */
|
|
131
113
|
unlock: (page: Page, password: string) => Promise<void>;
|
|
132
114
|
};
|
|
@@ -138,12 +120,7 @@ type SettingsApi = {
|
|
|
138
120
|
/** Add and switch networks from the wallet's own UI. Throws if the wallet doesn't declare support. */
|
|
139
121
|
type NetworkApi = {
|
|
140
122
|
add: (config: NetworkConfig) => Promise<void>;
|
|
141
|
-
/**
|
|
142
|
-
* No wallet implements this today, so calling it throws. MetaMask 13.x scopes the active chain
|
|
143
|
-
* per dapp and has no wallet-side network selector, so switching is dapp-initiated: the dapp calls
|
|
144
|
-
* `wallet_addEthereumChain` (idempotent, adds when missing and switches when present) and
|
|
145
|
-
* `wallet.approve()` drives the popup. See the network recipe in Examples.
|
|
146
|
-
*/
|
|
123
|
+
/** MetaMask 13.x switches dapp-scoped networks through `wallet_addEthereumChain` approvals. */
|
|
147
124
|
switch: (chainId: number) => Promise<void>;
|
|
148
125
|
};
|
|
149
126
|
/** Manage accounts from the wallet's own UI. Throws if the wallet doesn't declare support. */
|
|
@@ -193,4 +170,4 @@ type Wallet = {
|
|
|
193
170
|
};
|
|
194
171
|
//#endregion
|
|
195
172
|
export { WalletSetup as a, WalletKind as i, Wallet as n, WalletDefinition as r, Ecosystem as t };
|
|
196
|
-
//# sourceMappingURL=types-
|
|
173
|
+
//# sourceMappingURL=types-CFQpWZhv.d.mts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walletwright/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Playwright wallet automation for MetaMask, Phantom, Rabby, Solflare, and Slush across EVM, Solana, and Sui. Connect and sign in real browser extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"e2e",
|
|
@@ -56,18 +56,19 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"adm-zip": "^0.6.0",
|
|
59
|
-
"classic-level": "^3.0.0"
|
|
59
|
+
"classic-level": "^3.0.0",
|
|
60
|
+
"zod": "^4.4.3"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@playwright/test": "1.62.1",
|
|
63
64
|
"@types/adm-zip": "^0.5.7",
|
|
64
65
|
"@types/node": "^26.2.0",
|
|
65
66
|
"prool": "^0.2.14",
|
|
66
|
-
"typescript": "^
|
|
67
|
+
"typescript": "^7.0.2",
|
|
67
68
|
"viem": "^2.55.11",
|
|
68
69
|
"vitest": "^4.1.10",
|
|
69
|
-
"@repo/
|
|
70
|
-
"@repo/config
|
|
70
|
+
"@repo/config-vitest": "0.0.0",
|
|
71
|
+
"@repo/typescript-config": "0.0.0"
|
|
71
72
|
},
|
|
72
73
|
"peerDependencies": {
|
|
73
74
|
"@playwright/test": ">=1.48 <2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cache-Bwv2i-hv.mjs","names":["add","NAVIGATION_TIMEOUT_MS","importWallet","unlockScreen","importWallet"],"sources":["../src/internal/download.ts","../src/wallets/metamask/actions/accounts.ts","../src/wallets/metamask/actions/network.ts","../src/internal/wait.ts","../src/internal/unlock-screen.ts","../src/wallets/metamask/onboarding.ts","../src/wallets/metamask/actions/settings.ts","../src/wallets/metamask/approve.ts","../src/internal/utils.ts","../src/wallets/metamask/onboarding-patch.ts","../src/wallets/metamask.ts","../src/wallets/phantom.ts","../src/wallets/rabby.ts","../src/wallets/slush.ts","../src/wallets/solflare.ts","../src/wallets/index.ts","../src/internal/chromium.ts","../src/internal/cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport AdmZip from \"adm-zip\";\n\nconst ZIP_SIGNATURE = Buffer.from([80, 75, 3, 4]);\n\n/**\n * Download an extension archive and extract it to `<cacheDir>/<name>`, returning that path. A `.crx`\n * is a ZIP with a binary header, we slice from the ZIP signature before unzipping. Reuses an\n * existing extraction (so you can pre-place the extension to skip the download).\n */\nexport const downloadAndExtractExtension = async (options: {\n cacheDir: string;\n kind: \"zip\" | \"crx\";\n name: string;\n /**\n * Expected sha256 of the downloaded bytes. Required, and `undefined` only where the bytes genuinely\n * cannot be pinned, so adding a download site forces a decision instead of quietly trusting it.\n */\n sha256: string | undefined;\n url: string;\n}): Promise<string> => {\n const { cacheDir, kind, name, sha256, url } = options;\n const cacheRoot = path.resolve(cacheDir);\n const outDir = path.resolve(cacheDir, name);\n if (outDir === cacheRoot || !outDir.startsWith(cacheRoot + path.sep)) {\n throw new Error(`[walletwright] invalid extension name: ${name}`);\n }\n if (existsSync(path.join(outDir, \"manifest.json\"))) {\n return outDir;\n }\n\n await mkdir(cacheDir, { recursive: true });\n\n const response = await fetch(url, { redirect: \"follow\" });\n if (!response.ok) {\n throw new Error(\n `[walletwright] failed to download ${url}: ${response.status} ${response.statusText}`,\n );\n }\n const bytes = Buffer.from(await response.arrayBuffer());\n\n if (sha256 !== undefined) {\n const actual = createHash(\"sha256\").update(bytes).digest(\"hex\");\n if (actual !== sha256.toLowerCase()) {\n throw new Error(\n `[walletwright] ${name} failed integrity check: expected ${sha256}, got ${actual}`,\n );\n }\n }\n\n let zipBytes = bytes;\n if (kind === \"crx\") {\n const start = bytes.indexOf(ZIP_SIGNATURE);\n if (start === -1) {\n throw new Error(`[walletwright] ${url} is not a valid CRX (no ZIP header found)`);\n }\n zipBytes = bytes.subarray(start);\n }\n\n await rm(outDir, { force: true, recursive: true });\n const zip = new AdmZip(zipBytes);\n const root = path.resolve(outDir);\n for (const entry of zip.getEntries()) {\n const target = path.resolve(root, entry.entryName);\n if (target !== root && !target.startsWith(root + path.sep)) {\n throw new Error(`[walletwright] refusing to extract ${entry.entryName}: escapes ${outDir}`);\n }\n }\n zip.extractAllTo(outDir, /* overwrite */ true);\n\n if (!existsSync(path.join(outDir, \"manifest.json\"))) {\n throw new Error(`[walletwright] extracted ${name} but no manifest.json found in ${outDir}`);\n }\n return outDir;\n};\n\n/** Build the Chrome Web Store CRX download URL for an extension id. */\nexport const chromeWebStoreCrxUrl = (extensionId: string): string =>\n `https://clients2.google.com/service/update2/crx?response=redirect&prodversion=130.0&acceptformat=crx2,crx3&x=id%3D${extensionId}%26uc`;\n\n/** Download and extract a Chrome Web Store extension (latest) into `<cacheDir>/<name>`. */\nexport const prepareWebStoreExtension = (options: {\n cacheDir: string;\n extensionId: string;\n name: string;\n}): Promise<string> =>\n downloadAndExtractExtension({\n cacheDir: options.cacheDir,\n kind: \"crx\",\n name: options.name,\n sha256: undefined,\n url: chromeWebStoreCrxUrl(options.extensionId),\n });\n","import type { Page } from \"@playwright/test\";\n\nimport type { AccountActions, WalletActionContext } from \"../../../types\";\n\nconst openAccountMenu = async (home: Page): Promise<void> => {\n await home.getByTestId(\"account-menu-icon\").click();\n await home.getByTestId(\"add-multichain-account-button\").waitFor({ state: \"visible\" });\n};\n\n/** The menu is a popover with no close affordance; going home is the reliable way out. */\nconst closeAccountMenu = async (home: Page): Promise<void> => {\n await home.goto(`${home.url().split(\"#\")[0]}#/`);\n await home.getByTestId(\"account-menu-icon\").waitFor({ state: \"visible\" });\n};\n\nconst add = async ({ home }: WalletActionContext): Promise<void> => {\n await openAccountMenu(home);\n const cells = home.getByTestId(\"account-cell-avatar\");\n const before = await cells.count();\n await home.getByTestId(\"add-multichain-account-button\").click();\n await cells.nth(before).waitFor({ state: \"visible\", timeout: 15_000 });\n await closeAccountMenu(home);\n};\n\nconst importPrivateKey = async (\n { home }: WalletActionContext,\n privateKey: string,\n): Promise<void> => {\n await openAccountMenu(home);\n await home.getByTestId(\"account-list-add-wallet-button\").click();\n const chooseImport = home.getByTestId(\"choose-wallet-type-import-account\");\n await chooseImport.waitFor({ state: \"visible\" });\n await chooseImport.click();\n const confirm = home.getByTestId(\"import-account-confirm-button\");\n await confirm.waitFor({ state: \"visible\" });\n await home.locator(\"input\").first().fill(privateKey);\n await confirm.click();\n await confirm.waitFor({ state: \"hidden\", timeout: 15_000 });\n await closeAccountMenu(home);\n};\n\nconst rename = async (\n { home }: WalletActionContext,\n options: { index: number; name: string },\n): Promise<void> => {\n await openAccountMenu(home);\n await home.getByTestId(\"multichain-account-cell-end-accessory\").nth(options.index).click();\n await home.getByTestId(\"multichain-account-menu-item-rename\").click();\n await home.getByTestId(\"account-name-input\").locator(\"input\").fill(options.name);\n await home.getByRole(\"button\", { name: \"Confirm\" }).click();\n await closeAccountMenu(home);\n};\n\nconst switchTo = async ({ home }: WalletActionContext, index: number): Promise<void> => {\n await openAccountMenu(home);\n await home.getByTestId(\"account-cell-avatar\").nth(index).click();\n const menuClosed = await home\n .getByTestId(\"add-multichain-account-button\")\n .waitFor({ state: \"hidden\", timeout: 10_000 })\n .then(() => true)\n .catch(() => false);\n if (!menuClosed) {\n throw new Error(`[walletwright] accounts.switch(${index}) did not switch (menu stayed open)`);\n }\n};\n\nexport const accounts: AccountActions = {\n add,\n importPrivateKey,\n rename,\n switch: switchTo,\n};\n","import type { Page } from \"@playwright/test\";\n\nimport type { NetworkActions, NetworkConfig, WalletActionContext } from \"../../../types\";\n\nconst closeButton = (home: Page) =>\n home.getByTestId(\"modal-header-close-button\").filter({ visible: true }).first();\n\nconst openNetworkManager = async (home: Page): Promise<void> => {\n await home.getByTestId(\"sort-by-networks\").click();\n await closeButton(home).waitFor({ state: \"visible\" });\n};\n\nconst add = async ({ home }: WalletActionContext, config: NetworkConfig): Promise<void> => {\n await openNetworkManager(home);\n\n await home.getByText(\"Custom\", { exact: true }).first().click();\n await home.getByRole(\"button\", { name: \"Add custom network\" }).first().click();\n\n await home.getByTestId(\"network-form-network-name\").fill(config.name);\n await home.getByTestId(\"network-form-chain-id\").fill(String(config.chainId));\n await home.getByTestId(\"network-form-ticker-input\").fill(config.symbol);\n\n await home.getByTestId(\"test-add-rpc-drop-down\").click();\n await home.getByText(\"Add RPC URL\", { exact: true }).first().click();\n await home.getByTestId(\"rpc-url-input-test\").fill(config.rpcUrl);\n await home.getByTestId(\"page-container-footer-next\").click();\n\n await home.getByTestId(\"network-form-network-name\").waitFor();\n await home.getByTestId(\"page-container-footer-next\").click();\n\n await closeButton(home).click();\n await closeButton(home).waitFor({ state: \"hidden\" });\n};\n\nexport const network: NetworkActions = {\n add,\n};\n","import type { Page } from \"@playwright/test\";\n\nconst DEFAULT_INTERVAL_MS = 200;\nconst DEFAULT_NAVIGATION_INTERVAL_MS = 1000;\n\nexport type WaitOptions = {\n /** Gap between checks. */\n intervalMs?: number;\n /** Total budget. `check` runs at least once, and never again once the budget has elapsed. */\n timeoutMs: number;\n};\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/** Render a timeout for an error message, so what a failure claims matches what it waited. */\nexport const formatTimeout = (ms: number): string => `${Math.round(ms / 100) / 10}s`;\n\n/**\n * Poll `check` until it yields anything other than `undefined`/`false`, then hand that value back.\n * Returns `undefined` when the budget elapses, so only a caller that can genuinely continue without\n * the condition (an optional approval popup, say) branches on the result; everything else uses\n * `waitUntilOrThrow` and gets a named failure instead of a wallet that looks ready and isn't.\n */\nexport const waitUntil = async <T>(\n check: () => Promise<T | false | undefined> | T | false | undefined,\n { intervalMs = DEFAULT_INTERVAL_MS, timeoutMs }: WaitOptions,\n): Promise<T | undefined> => {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const result = await check();\n if (result !== undefined && result !== false) {\n return result;\n }\n if (Date.now() >= deadline) {\n return undefined;\n }\n await sleep(intervalMs);\n }\n};\n\nexport const waitUntilOrThrow = async <T>(\n check: () => Promise<T | false | undefined> | T | false | undefined,\n options: WaitOptions & { message: string },\n): Promise<T> => {\n const result = await waitUntil(check, options);\n if (result === undefined) {\n throw new Error(\n `[walletwright] ${options.message} (gave up after ${formatTimeout(options.timeoutMs)})`,\n );\n }\n return result;\n};\n\n/**\n * A `chrome-extension://` URL fails with `ERR_BLOCKED_BY_CLIENT` until the extension registers,\n * which takes seconds after launch, so reaching an extension page is a poll rather than a one-shot.\n */\nexport const gotoWithRetry = async (\n page: Page,\n url: string,\n {\n intervalMs = DEFAULT_NAVIGATION_INTERVAL_MS,\n label,\n timeoutMs,\n }: WaitOptions & { label: string },\n): Promise<void> => {\n await waitUntilOrThrow(\n () =>\n page\n .goto(url, { waitUntil: \"domcontentloaded\" })\n .then(() => true)\n .catch(() => undefined),\n { intervalMs, message: `${label} never loaded (${url})`, timeoutMs },\n );\n};\n","import type { BrowserContext, Locator, Page } from \"@playwright/test\";\n\nimport { formatTimeout, gotoWithRetry, waitUntil } from \"./wait\";\n\nconst PASSWORD_FIELD = 'input[type=\"password\"]';\n\nconst NAVIGATION_TIMEOUT_MS = 15_000;\nconst FIRST_SETTLE_TIMEOUT_MS = 20_000;\nconst RELOAD_ATTEMPTS = 5;\nconst RELOAD_SETTLE_TIMEOUT_MS = 5000;\nconst PROBE_INTERVAL_MS = 500;\nconst PASSWORD_CLEARED_TIMEOUT_MS = 15_000;\n\ntype UnlockScreenOptions = {\n /** Extension-relative page that renders the unlock screen (e.g. `home.html`). */\n entry: string;\n /**\n * Resolves true once the wallet's already-unlocked UI is up. Only wallets that can reopen unlocked\n * (Phantom, Slush) declare it: without a positive signal for that state, a warm launch is\n * indistinguishable from an extension page that rendered nothing at all, and the run continues\n * against a dead wallet until some later approval fails for no visible reason.\n */\n isUnlocked?: (page: Page) => Promise<boolean>;\n /** Submit the filled password. Defaults to pressing Enter in the field. */\n submit?: (page: Page, field: Locator) => Promise<void>;\n wallet: string;\n};\n\ntype UnlockScreen = {\n reachUnlockScreen: (context: BrowserContext, extensionId: string) => Promise<Page>;\n unlock: (page: Page, password: string) => Promise<void>;\n};\n\n/**\n * The shared unlock flow: open the wallet's own page, wait for it to settle into a state we can\n * name, and type the password. Every wallet differs only in its entry page, how it submits, and\n * whether it can come back already unlocked.\n */\nexport const createUnlockScreen = ({\n entry,\n isUnlocked,\n submit,\n wallet,\n}: UnlockScreenOptions): UnlockScreen => {\n const settled = async (page: Page): Promise<\"locked\" | \"unlocked\" | undefined> => {\n const locked = await page\n .locator(PASSWORD_FIELD)\n .isVisible()\n .catch(() => false);\n if (locked) {\n return \"locked\";\n }\n const unlocked = await isUnlocked?.(page).catch(() => false);\n return unlocked === true ? \"unlocked\" : undefined;\n };\n\n const settleWithin = (page: Page, timeoutMs: number) =>\n waitUntil(() => settled(page), { intervalMs: PROBE_INTERVAL_MS, timeoutMs });\n\n const reachUnlockScreen = async (context: BrowserContext, extensionId: string): Promise<Page> => {\n const page = await context.newPage();\n await gotoWithRetry(page, `chrome-extension://${extensionId}/${entry}`, {\n label: `${wallet} ${entry}`,\n timeoutMs: NAVIGATION_TIMEOUT_MS,\n });\n\n let state = await settleWithin(page, FIRST_SETTLE_TIMEOUT_MS);\n for (let attempt = 0; attempt < RELOAD_ATTEMPTS && state === undefined; attempt++) {\n await page.reload().catch(() => {});\n state = await settleWithin(page, RELOAD_SETTLE_TIMEOUT_MS);\n }\n if (state === undefined) {\n throw new Error(\n `[walletwright] ${wallet} never reached its unlock screen (gave up after ${formatTimeout(\n FIRST_SETTLE_TIMEOUT_MS + RELOAD_ATTEMPTS * RELOAD_SETTLE_TIMEOUT_MS,\n )})`,\n );\n }\n return page;\n };\n\n const unlock = async (page: Page, password: string): Promise<void> => {\n const field = page.locator(PASSWORD_FIELD);\n const locked = await field.isVisible().catch(() => false);\n if (!locked) {\n const unlocked = await isUnlocked?.(page).catch(() => false);\n if (unlocked === true) {\n return;\n }\n throw new Error(\n `[walletwright] ${wallet} showed neither an unlock screen nor an unlocked wallet`,\n );\n }\n\n await field.fill(password);\n await (submit === undefined ? field.press(\"Enter\") : submit(page, field));\n const cleared = await field\n .waitFor({ state: \"hidden\", timeout: PASSWORD_CLEARED_TIMEOUT_MS })\n .then(() => true)\n .catch(() => false);\n if (!cleared) {\n throw new Error(\n `[walletwright] ${wallet} unlock failed (password screen still visible after ${formatTimeout(\n PASSWORD_CLEARED_TIMEOUT_MS,\n )})`,\n );\n }\n };\n\n return { reachUnlockScreen, unlock };\n};\n","import type { Page } from \"@playwright/test\";\n\nimport { createUnlockScreen } from \"../../internal/unlock-screen\";\nimport { waitUntil } from \"../../internal/wait\";\n\nconst SRP_RETYPE_TIMEOUT_MS = 40_000;\nconst SRP_CONFIRM_TIMEOUT_MS = 5000;\n\nconst { reachUnlockScreen, unlock } = createUnlockScreen({\n entry: \"home.html\",\n wallet: \"MetaMask\",\n});\n\nexport const importWallet = async (\n page: Page,\n seedPhrase: string,\n password: string,\n): Promise<void> => {\n await page.getByTestId(\"onboarding-import-wallet\").click();\n await page.getByTestId(\"onboarding-import-with-srp-button\").click();\n\n const srpField = page.getByTestId(\"srp-input-import__srp-note\");\n const confirmEnabled = page.locator('[data-testid=\"import-srp-confirm\"]:not([disabled])');\n const accepted = await waitUntil(\n async () => {\n await srpField.click();\n await srpField.fill(\"\");\n await srpField.pressSequentially(seedPhrase, { delay: 50 });\n return confirmEnabled\n .waitFor({ state: \"visible\", timeout: SRP_CONFIRM_TIMEOUT_MS })\n .then(() => true)\n .catch(() => undefined);\n },\n { intervalMs: 0, timeoutMs: SRP_RETYPE_TIMEOUT_MS },\n );\n if (accepted === undefined) {\n throw new Error(\"[walletwright] MetaMask rejected the seed phrase (confirm stayed disabled)\");\n }\n await confirmEnabled.click();\n\n await page.getByTestId(\"create-password-new-input\").fill(password);\n await page.getByTestId(\"create-password-confirm-input\").fill(password);\n await page.getByTestId(\"create-password-terms\").click();\n await page.locator('[data-testid=\"create-password-submit\"]:not([disabled])').click();\n\n await page\n .getByTestId(\"passkey-maybe-later-button\")\n .click({ timeout: 10_000 })\n .catch(() => {});\n await page\n .getByTestId(\"metametrics-i-agree\")\n .click({ timeout: 10_000 })\n .catch(() => {});\n};\n\nexport { reachUnlockScreen, unlock };\n","import type { SettingsActions } from \"../../../types\";\nimport { unlock as unlockScreen } from \"../onboarding\";\n\nexport const settings: SettingsActions = {\n lock: async ({ home }) => {\n await home.getByTestId(\"account-options-menu-button\").click();\n await home.getByTestId(\"global-menu-lock\").filter({ visible: true }).first().click();\n await home.locator('input[type=\"password\"]').waitFor({ state: \"visible\", timeout: 15_000 });\n },\n\n unlock: async ({ home, password }) => {\n await unlockScreen(home, password);\n },\n};\n","import type { Locator, Page } from \"@playwright/test\";\n\n/**\n * How long a confirm or cancel button gets to become clickable, once the page is already showing\n * one. Generous because a loaded CI runner renders MetaMask's footer slowly.\n */\nconst BUTTON_TIMEOUT_MS = 45_000;\n/** The retry only covers a notice that rendered late, so it does not need the full budget again. */\nconst RETRY_TIMEOUT_MS = 10_000;\n\n/**\n * First-time requests that MetaMask routes through a protocol Snap (e.g. adding a custom chain)\n * open a \"Third-party software notice\" modal over the confirm footer, and every click is\n * intercepted until it is accepted. Its buttons carry no testids, only text, and Accept stays\n * disabled until the notice is scrolled to the bottom.\n */\nconst acceptThirdPartyNotice = async (popup: Page): Promise<void> => {\n const modal = popup.locator(\".snap-install-warning\");\n if ((await modal.count().catch(() => 0)) === 0) {\n return;\n }\n await modal\n .locator(\"p\")\n .last()\n .scrollIntoViewIfNeeded()\n .catch(() => {});\n await modal\n .getByRole(\"button\", { name: \"Accept\" })\n .click({ timeout: 3000 })\n .catch(() => {});\n};\n\n/**\n * Confirm buttons differ by request type: connect popups use `confirm-btn`, signature and\n * transaction popups use `confirm-footer-button`, older builds used `page-container-footer-next`,\n * and requests routed through a protocol Snap (e.g. Solana) use\n * `confirm-<type>-confirm-snap-footer-button`, matched by suffix so every snap confirmation type is\n * covered. A popup renders exactly one of these buttons; the `.or()` union resolves whichever one\n * is present by DOM order, not by the order it's written in below, so one locator covers every\n * popup.\n */\nconst confirmButton = (popup: Page): Locator =>\n popup\n .getByTestId(\"confirm-btn\")\n .or(popup.getByTestId(\"confirm-footer-button\"))\n .or(popup.locator('[data-testid=\"page-container-footer-next\"]'))\n .or(popup.locator('[data-testid$=\"-confirm-snap-footer-button\"]'))\n .first();\n\nconst cancelButton = (popup: Page): Locator =>\n popup\n .getByTestId(\"cancel-btn\")\n .or(popup.getByTestId(\"confirm-footer-cancel-button\"))\n .or(popup.locator('[data-testid=\"page-container-footer-cancel\"]'))\n .or(popup.locator('[data-testid$=\"-cancel-snap-footer-button\"]'))\n .first();\n\n/**\n * What \"this page is showing a request\" means for MetaMask, as opposed to its wallet home. The\n * distinction matters for the page walletwright opens itself: `notification.html` renders the home\n * screen (account header, Buy/Swap/Send, a news carousel) when nothing is pending, so buttons on\n * screen prove nothing. A CI run handed that home screen to `approve` and waited out the full click\n * budget against a confirm button that was never going to exist.\n */\nexport const approvalControls = (popup: Page): Locator =>\n confirmButton(popup).or(cancelButton(popup));\n\nexport const approve = async (popup: Page): Promise<void> => {\n const button = confirmButton(popup);\n await acceptThirdPartyNotice(popup);\n try {\n await button.click({ timeout: BUTTON_TIMEOUT_MS });\n } catch (error) {\n await acceptThirdPartyNotice(popup);\n await button.click({ timeout: RETRY_TIMEOUT_MS }).catch(() => {\n throw error;\n });\n }\n};\n\n/** The cancel counterparts of `approve`; same union-by-DOM-order resolution, not written order. */\nexport const reject = async (popup: Page): Promise<void> => {\n await cancelButton(popup).click({ timeout: BUTTON_TIMEOUT_MS });\n};\n","import { createHash } from \"node:crypto\";\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { BrowserContext, Locator, Page } from \"@playwright/test\";\n\nimport type { WalletSetup } from \"../types\";\n\nimport { waitUntil } from \"./wait\";\n\nexport const DEFAULT_CACHE_DIR = \".walletwright\";\n\n/** MetaMask/Phantom approval-popup URL token; Slush overrides it with `notificationMatch: \"isPopup=1\"`. */\nexport const DEFAULT_NOTIFICATION_MATCH = \"notification.html\";\n\n/** Stable per-setup profile directory name, so the same wallet+seed+password reuses one cache. */\nexport const profileKey = (setup: WalletSetup): string =>\n createHash(\"sha256\")\n .update(`${setup.wallet}:${setup.version ?? \"default\"}:${setup.seedPhrase}:${setup.password}`)\n .digest(\"hex\")\n .slice(0, 20);\n\n/**\n * Compute an extension's id without querying the browser (`chrome://extensions` is blocked headless,\n * and the service worker starts lazily). Chrome derives the id from the sha256 of either the\n * manifest's public `key` (if present, e.g. Phantom → its fixed Web Store id) or the absolute load\n * path (no key, e.g. MetaMask), taking the first 16 bytes and mapping each nibble 0-f → a-p.\n *\n * The path is resolved through symlinks (`realpathSync`) because Chrome hashes the *real* path, a\n * cache under a symlinked dir (e.g. macOS `/tmp` → `/private/tmp`) would otherwise yield a wrong id.\n */\nexport const extensionIdFromPath = (extensionPath: string): string => {\n const resolved = path.resolve(extensionPath);\n const abs = (() => {\n try {\n return realpathSync(resolved);\n } catch {\n return resolved; // path doesn't exist yet, fall back to the literal resolved path\n }\n })();\n let key: string | undefined;\n try {\n const manifest: unknown = JSON.parse(readFileSync(path.join(abs, \"manifest.json\"), \"utf8\"));\n if (typeof manifest === \"object\" && manifest !== null && \"key\" in manifest) {\n key = typeof manifest.key === \"string\" ? manifest.key : undefined;\n }\n } catch {\n // manifest not present yet, fall back to the path\n }\n const source =\n key === undefined || key === \"\" ? Buffer.from(abs, \"utf8\") : Buffer.from(key, \"base64\");\n const hex = createHash(\"sha256\").update(source).digest(\"hex\").slice(0, 32);\n return Array.from(hex, (nibble) => String.fromCodePoint(97 + Number.parseInt(nibble, 16))).join(\n \"\",\n );\n};\n\n/**\n * Approval popups open as `about:blank` and then navigate, so `waitForEvent('page', { predicate })`\n * misses them, poll the open pages instead. `match` is the URL token identifying the popup\n * (`notification.html` for MetaMask/Phantom; `isPopup=1` for Slush's single-page `index.html`).\n */\nexport const isApprovalPopup = (page: Page, extensionId: string, match: string): boolean =>\n page.url().startsWith(`chrome-extension://${extensionId}`) &&\n page.url().includes(match) &&\n !page.isClosed();\n\nconst isVisible = async (locator: Locator): Promise<boolean> => {\n try {\n return await locator.isVisible();\n } catch {\n return false; // the page can go while we ask\n }\n};\n\nconst hasVisibleButton = (page: Page): Promise<boolean> =>\n isVisible(page.locator(\"button\").first());\n\nexport const findNotificationPopup = ({\n approvalControls,\n context,\n extensionId,\n match = DEFAULT_NOTIFICATION_MATCH,\n timeoutMs = 10_000,\n}: {\n /** From `WalletDefinition.approvalControls`, for a wallet that can render its popup requestless. */\n approvalControls?: (page: Page) => Locator;\n context: BrowserContext;\n extensionId: string;\n match?: string;\n timeoutMs?: number;\n}): Promise<Page | undefined> =>\n waitUntil(\n async () => {\n const popup = context.pages().find((page) => isApprovalPopup(page, extensionId, match));\n if (!popup) {\n return undefined;\n }\n await popup.waitForLoadState(\"domcontentloaded\").catch(() => {});\n const ready =\n approvalControls === undefined\n ? await hasVisibleButton(popup)\n : await isVisible(approvalControls(popup).first());\n return ready ? popup : undefined;\n },\n { timeoutMs },\n );\n\n/** MetaMask's own popup dimensions, which its layout is built for. */\nconst APPROVAL_WINDOW = { height: 592, width: 360 };\n/** Far enough from the edge that a popup this size lands fully on any usable display. */\nconst APPROVAL_WINDOW_OFFSET = 50;\n\n/**\n * Put a wallet-spawned approval window somewhere it can be clicked. It can open partly off-screen\n * on a small or virtual display, and Playwright will not click what is out of view, so a confirm\n * button that is rendered and enabled still times out. Synpress carries the same workaround.\n *\n * Best-effort throughout: headless has no window to move, and nothing here is required for a window\n * that already sits on screen.\n */\nexport const placeApprovalWindow = async (page: Page): Promise<void> => {\n await page.setViewportSize(APPROVAL_WINDOW).catch(() => {});\n try {\n const session = await page.context().newCDPSession(page);\n const { targetInfo } = await session.send(\"Target.getTargetInfo\");\n const { windowId } = await session.send(\"Browser.getWindowForTarget\", {\n targetId: targetInfo.targetId,\n });\n await session.send(\"Browser.setWindowBounds\", {\n bounds: { left: APPROVAL_WINDOW_OFFSET, top: APPROVAL_WINDOW_OFFSET },\n windowId,\n });\n await session.detach();\n } catch {\n // no window to place (headless), or the page refused a CDP session\n }\n};\n\n/** Where Chrome persists an extension's `chrome.storage.local` inside a browser profile. */\nexport const extensionStateDir = (profileDir: string, extensionId: string): string =>\n path.join(profileDir, \"Default\", \"Local Extension Settings\", extensionId);\n\nexport const hasNotificationPopup = (\n context: BrowserContext,\n extensionId: string,\n match = DEFAULT_NOTIFICATION_MATCH,\n): boolean => context.pages().some((page) => isApprovalPopup(page, extensionId, match));\n","import { existsSync } from \"node:fs\";\n\nimport { ClassicLevel } from \"classic-level\";\n\nimport { extensionStateDir } from \"../../internal/utils\";\n\ntype OnboardingState = Record<string, unknown>;\n\nconst asState = (value: unknown): OnboardingState | undefined =>\n typeof value === \"object\" && value !== null ? { ...value } : undefined;\n\nconst markOnboarded = (onboarding: OnboardingState): OnboardingState => ({\n ...onboarding,\n completedOnboarding: true,\n firstTimeFlowType:\n typeof onboarding.firstTimeFlowType === \"string\" ? onboarding.firstTimeFlowType : \"import\",\n onboardingTabs: {},\n});\n\n/**\n * Force `completedOnboarding=true` in MetaMask's persisted state so the cached wallet boots straight\n * to the unlock screen instead of getting stuck on the \"wallet is ready\" screen (whose \"Open wallet\"\n * action goes through the MV3 service worker and is unreliable under automation).\n *\n * MetaMask 13.13.x stored all state under a single `data` key; 13.3x stores each controller under its\n * own key (`OnboardingController`). Both are handled. Must run while the browser is closed.\n *\n * Every branch that can't find the state it means to patch throws: leaving the cache unpatched means\n * a wallet that boots to \"wallet is ready\" and ignores every dapp request, which is far harder to\n * diagnose from a spec than a failed cache build.\n */\nexport const markMetaMaskOnboarded = async (\n profileDir: string,\n extensionId: string,\n): Promise<void> => {\n const dbDir = extensionStateDir(profileDir, extensionId);\n if (!existsSync(dbDir)) {\n throw new Error(`[walletwright] MetaMask wrote no extension state to ${dbDir}`);\n }\n\n const db = new ClassicLevel(dbDir, {\n createIfMissing: false,\n keyEncoding: \"utf8\",\n valueEncoding: \"utf8\",\n });\n try {\n await db.open();\n\n const perController = await db.get(\"OnboardingController\");\n if (perController !== undefined && perController !== \"\") {\n const onboarding = asState(JSON.parse(perController));\n if (onboarding === undefined) {\n throw new Error(`[walletwright] MetaMask's OnboardingController state is not an object`);\n }\n if (onboarding.completedOnboarding !== true) {\n await db.put(\"OnboardingController\", JSON.stringify(markOnboarded(onboarding)));\n }\n return;\n }\n\n const raw = await db.get(\"data\");\n if (raw === undefined || raw === \"\") {\n throw new Error(\n `[walletwright] MetaMask persisted no onboarding state in ${dbDir} (neither an OnboardingController key nor a data key)`,\n );\n }\n const state = asState(JSON.parse(raw));\n if (state === undefined) {\n throw new Error(`[walletwright] MetaMask's \"data\" state is not an object`);\n }\n const nested = asState(state.data);\n const controller = asState((nested ?? state).OnboardingController);\n if (controller === undefined) {\n throw new Error(`[walletwright] MetaMask's \"data\" state holds no OnboardingController`);\n }\n const patched = markOnboarded(controller);\n await db.put(\n \"data\",\n JSON.stringify(\n nested === undefined\n ? { ...state, OnboardingController: patched }\n : { ...state, data: { ...nested, OnboardingController: patched } },\n ),\n );\n } finally {\n await db.close();\n }\n};\n","import { downloadAndExtractExtension } from \"../internal/download\";\nimport type { WalletDefinition } from \"../types\";\n\nimport { accounts } from \"./metamask/actions/accounts\";\nimport { network } from \"./metamask/actions/network\";\nimport { settings } from \"./metamask/actions/settings\";\nimport { approvalControls, approve, reject } from \"./metamask/approve\";\nimport { importWallet, reachUnlockScreen, unlock } from \"./metamask/onboarding\";\nimport { markMetaMaskOnboarded } from \"./metamask/onboarding-patch\";\n\nconst DEFAULT_VERSION = \"13.35.1\";\n\n/**\n * sha256 of each pinned `metamask-chrome-<version>.zip` release asset, so a pinned download is\n * verified rather than trusted. This is the one place to fill in when bumping `DEFAULT_VERSION`:\n *\n * curl -sL https://github.com/MetaMask/metamask-extension/releases/download/v<v>/metamask-chrome-<v>.zip | shasum -a 256\n *\n * A version a caller pins through `setup.version` has no entry here and downloads unverified, and\n * the Web-Store wallets can't be pinned at all (that endpoint always serves the current version).\n */\nconst RELEASE_SHA256: Readonly<Record<string, string>> = {\n \"13.35.1\": \"4e0f8626df0ae9fb15f5f3ad6784a0b518f3ede067b2b0d4f539f9f457c5049c\",\n};\n\n/** Download inputs for a MetaMask release, split out so the integrity wiring is unit-testable. */\nexport const metamaskDownload = (cacheDir: string, version: string) => ({\n cacheDir,\n kind: \"zip\" as const,\n name: `metamask-chrome-${version}`,\n sha256: RELEASE_SHA256[version],\n url: `https://github.com/MetaMask/metamask-extension/releases/download/v${version}/metamask-chrome-${version}.zip`,\n});\n\n/**\n * MetaMask's backup-and-sync restores account names for whichever SRP a profile holds, and test\n * seeds are shared (the public `test test … junk` one by thousands), so a synced profile reports a\n * stranger's account names in place of the ones a test just set: CI runs showed accounts called\n * `dev1` and `personal`, holding a real balance, where `Account 2` was expected. It only happens\n * where the sync actually lands, which makes it look like a flake. MetaMask's own e2e suite mocks\n * its external services for the same reason.\n *\n * Just the one host that stores the names. Cutting the auth stack around it (`authentication`,\n * `oidc`) reaches further than intended: other features authenticate through it too, and a wallet\n * that cannot authenticate can leave a request's confirm button disabled with nothing to explain it.\n */\nconst ACCOUNT_SYNC_HOST = \"user-storage.api.cx.metamask.io\";\n\nexport const metamask: WalletDefinition = {\n actions: { accounts, network, settings },\n approvalControls,\n approve,\n ecosystems: [\"evm\", \"svm\"],\n extensionName: \"MetaMask\",\n\n finalizeCache: markMetaMaskOnboarded,\n\n importWallet,\n\n onboardingPage: \"home.html\",\n\n prepareContext: async (context) => {\n await context.route(`**://${ACCOUNT_SYNC_HOST}/**`, (route) => route.abort());\n },\n prepareExtension: (cacheDir, version = DEFAULT_VERSION) =>\n downloadAndExtractExtension(metamaskDownload(cacheDir, version)),\n reachUnlockScreen,\n reject,\n unlock,\n};\n","import { prepareWebStoreExtension } from \"../internal/download\";\nimport { createUnlockScreen } from \"../internal/unlock-screen\";\nimport { sleep } from \"../internal/wait\";\nimport type { WalletDefinition } from \"../types\";\n\nconst PHANTOM_EXTENSION_ID = \"bfnaelmomeimhlpmgjnjophhpkkoljpa\";\n\nconst { reachUnlockScreen, unlock } = createUnlockScreen({\n entry: \"popup.html\",\n isUnlocked: async (page) => {\n const rendered = await page\n .locator(\"button\")\n .first()\n .isVisible()\n .catch(() => false);\n return rendered;\n },\n submit: async (page, field) => {\n const unlockButton = page.getByRole(\"button\", { name: /unlock/iv });\n await ((await unlockButton.isVisible().catch(() => false))\n ? unlockButton.click()\n : field.press(\"Enter\"));\n },\n wallet: \"Phantom\",\n});\n\nexport const phantom: WalletDefinition = {\n approve: async (popup) => {\n await popup.getByTestId(\"primary-button\").click({ timeout: 15_000 });\n },\n ecosystems: [\"evm\", \"svm\"],\n\n extensionName: \"Phantom\",\n\n headlessApprovals: true,\n\n importWallet: async (page, seedPhrase, password) => {\n const testId = (id: string) => page.locator(`[data-testid=\"${id}\"]`);\n\n await page.locator(\"text=I already have a wallet\").click();\n await sleep(1000);\n await page.locator(\"text=Import Recovery Phrase\").click();\n await sleep(1500);\n\n const words = seedPhrase.trim().split(/\\s+/v);\n for (let i = 0; i < words.length; i++) {\n await testId(`secret-recovery-phrase-word-input-${i}`).fill(words[i] ?? \"\");\n }\n await testId(\"onboarding-form-submit-button\").click();\n\n await testId(\"onboarding-form-secondary-button\").waitFor({ state: \"visible\", timeout: 60_000 });\n await testId(\"onboarding-form-submit-button\").click();\n\n await testId(\"onboarding-form-password-input\").fill(password);\n await testId(\"onboarding-form-confirm-password-input\").fill(password);\n await testId(\"onboarding-form-terms-of-service-checkbox\").click();\n await testId(\"onboarding-form-submit-button\").click();\n await sleep(2500);\n\n const getStarted = page.locator(\"text=Get Started\");\n if (await getStarted.isVisible().catch(() => false)) {\n await getStarted.click();\n }\n await sleep(1500);\n },\n\n onboardingPage: \"onboarding.html\",\n\n prepareExtension: (cacheDir) =>\n prepareWebStoreExtension({\n cacheDir,\n extensionId: PHANTOM_EXTENSION_ID,\n name: \"phantom-chrome-latest\",\n }),\n\n reachUnlockScreen,\n\n reject: async (popup) => {\n await popup.getByTestId(\"secondary-button\").click({ timeout: 15_000 });\n },\n\n unlock,\n};\n","import type { Page } from \"@playwright/test\";\n\nimport { prepareWebStoreExtension } from \"../internal/download\";\nimport { createUnlockScreen } from \"../internal/unlock-screen\";\nimport { sleep, waitUntil, waitUntilOrThrow } from \"../internal/wait\";\nimport type { WalletDefinition } from \"../types\";\n\nconst ROUTE_TIMEOUT_MS = 30_000;\nconst SEED_PASTE_TIMEOUT_MS = 3000;\nconst APPROVAL_TIMEOUT_MS = 30_000;\n\nconst RABBY_EXTENSION_ID = \"acmacodkjbdgmoleebolmdjonilkdbch\";\n\nconst ONBOARDING_ROUTE = \"index.html#/new-user/guide\";\n\nconst { reachUnlockScreen, unlock } = createUnlockScreen({ entry: \"index.html\", wallet: \"Rabby\" });\n\nconst clickText = async (page: Page, text: string, timeoutMs = 30_000): Promise<void> => {\n const target = page.getByText(text, { exact: true }).first();\n await target.waitFor({ state: \"visible\", timeout: timeoutMs });\n await target.click();\n};\n\n/**\n * Rabby routes with a hash router, so `waitForURL` never settles (a hash change fires no navigation\n * event). Poll the URL instead. Anchoring each onboarding step on its route matters because several\n * screens share the same `input[type=\"password\"]` selector.\n */\nconst waitForRoute = async (\n page: Page,\n fragment: string,\n timeoutMs = ROUTE_TIMEOUT_MS,\n): Promise<void> => {\n await waitUntilOrThrow(() => page.url().includes(fragment), {\n message: `Rabby never reached ${fragment}`,\n timeoutMs,\n });\n};\n\nconst importWallet = async (page: Page, seedPhrase: string, password: string): Promise<void> => {\n await clickText(page, \"I already have an address\");\n await clickText(page, \"Seed Phrase or Private Key\");\n await waitForRoute(page, \"seed-or-key\");\n\n const words = page.locator('input[type=\"password\"]');\n await words.first().waitFor({ state: \"visible\", timeout: 30_000 });\n await page\n .context()\n .grantPermissions([\"clipboard-read\", \"clipboard-write\"])\n .catch(() => {});\n const list = seedPhrase.trim().split(/\\s+/v);\n const lastFilled = async (): Promise<boolean> => {\n const value = await words\n .nth(list.length - 1)\n .inputValue()\n .catch(() => \"\");\n return value.trim().length > 0;\n };\n\n await words.first().click();\n await page.evaluate((phrase) => navigator.clipboard.writeText(phrase), seedPhrase.trim());\n await page.keyboard.press(\"ControlOrMeta+v\");\n const pasted = await waitUntil(lastFilled, { timeoutMs: SEED_PASTE_TIMEOUT_MS });\n if (pasted === undefined) {\n for (let index = 0; index < list.length; index++) {\n const box = words.nth(index);\n await box.click();\n await box.fill(list[index] ?? \"\");\n }\n }\n await page.getByRole(\"button\", { exact: true, name: \"Next\" }).first().click();\n\n await waitForRoute(page, \"set-password\");\n const passwords = page.locator('input[type=\"password\"]');\n await passwords.nth(1).waitFor({ state: \"visible\", timeout: 30_000 });\n await passwords.nth(0).fill(password);\n await passwords.nth(1).fill(password);\n await page.getByRole(\"button\", { exact: true, name: \"Confirm\" }).first().click();\n\n await page\n .getByText(\"Address Imported\", { exact: false })\n .first()\n .waitFor({ state: \"visible\", timeout: 30_000 });\n await sleep(1000);\n};\n\nconst CONFIRM_LABELS = [\"Connect\", \"Sign\", \"Confirm\"];\nconst CANCEL_LABELS = [\"Cancel\", \"Reject\"];\n\n/**\n * Rabby's approval window is focus-fragile: it unmounts its contents a few seconds after losing\n * focus, and Playwright's click (which waits for actionability, then for the click to settle) loses\n * the window mid-action. Rabby reads the vanished window as a dismissal, so the dapp gets\n * \"User rejected the request\" even though the confirm button was visible and enabled. Dispatching\n * the click inside the page skips focus, actionability, and post-click bookkeeping entirely. Rabby\n * is a plain React app, so `evaluate` works here (unlike MetaMask, which scuttles the realm).\n *\n * Signing is a two-step footer: \"Sign\" swaps itself for \"Confirm\", which must be clicked too, and\n * both start disabled while Rabby analyses the request. So keep clicking whichever labelled button\n * is currently enabled until the window closes, which is the only signal the request was answered.\n */\nconst clickApprovalButton = async (\n popup: Page,\n labels: ReadonlyArray<string>,\n): Promise<boolean> => {\n let lastClicked = \"\";\n const answered = await waitUntil(\n async () => {\n if (popup.isClosed()) {\n return true;\n }\n const clicked = await popup\n .evaluate(\n (arg) => {\n const target = [...document.querySelectorAll(\"button\")].find((button) => {\n const text = (button.textContent ?? \"\").trim();\n return arg.names.includes(text) && !button.disabled && text !== arg.skip;\n });\n target?.click();\n return target ? (target.textContent ?? \"\").trim() : \"\";\n },\n { names: [...labels], skip: lastClicked },\n )\n .catch(() => \"\");\n if (clicked) {\n lastClicked = clicked;\n }\n return undefined;\n },\n { intervalMs: 300, timeoutMs: APPROVAL_TIMEOUT_MS },\n );\n return answered === true || popup.isClosed();\n};\n\nexport const rabby: WalletDefinition = {\n approve: async (popup) => {\n if (!(await clickApprovalButton(popup, CONFIRM_LABELS))) {\n throw new Error(\"[walletwright] Rabby approval: no confirm button became actionable\");\n }\n },\n\n ecosystems: [\"evm\"],\n\n extensionName: \"Rabby\",\n\n headlessApprovals: true,\n\n importWallet,\n\n onboardingPage: ONBOARDING_ROUTE,\n\n prepareExtension: (cacheDir) =>\n prepareWebStoreExtension({\n cacheDir,\n extensionId: RABBY_EXTENSION_ID,\n name: \"rabby-chrome-latest\",\n }),\n\n reachUnlockScreen,\n\n reject: async (popup) => {\n if (!(await clickApprovalButton(popup, CANCEL_LABELS))) {\n throw new Error(\"[walletwright] Rabby reject: no cancel button became actionable\");\n }\n },\n\n unlock,\n};\n","import type { Page } from \"@playwright/test\";\n\nimport { prepareWebStoreExtension } from \"../internal/download\";\nimport { createUnlockScreen } from \"../internal/unlock-screen\";\nimport { sleep, waitUntilOrThrow } from \"../internal/wait\";\nimport type { WalletDefinition } from \"../types\";\n\nconst SLUSH_EXTENSION_ID = \"opcgpfmipidbgpenhmajoajpbobppdil\";\n\nconst HOME_ROUTE = \"#/tokens\";\n\nconst IMPORT_HOME_TIMEOUT_MS = 30_000;\n\nconst atHome = async (page: Page): Promise<boolean> =>\n (await page.evaluate(() => globalThis.location.hash).catch(() => \"\")) === HOME_ROUTE;\n\nconst fclick = async (page: Page, text: string, timeoutMs = 15_000): Promise<boolean> => {\n const target = page.getByText(text, { exact: true }).first();\n const visible = await target\n .waitFor({ state: \"visible\", timeout: timeoutMs })\n .then(() => true)\n .catch(() => false);\n if (!visible) {\n return false;\n }\n await target.scrollIntoViewIfNeeded().catch(() => {});\n await target.click({ force: true, timeout: timeoutMs });\n return true;\n};\n\nconst { reachUnlockScreen, unlock } = createUnlockScreen({\n entry: \"index.html\",\n isUnlocked: atHome,\n submit: async (page) => {\n await fclick(page, \"Unlock\");\n },\n wallet: \"Slush\",\n});\n\nexport const slush: WalletDefinition = {\n approve: async (popup, password) => {\n await sleep(2000);\n const confirmed = (await fclick(popup, \"Approve\", 4000)) || (await fclick(popup, \"Sign\"));\n if (!confirmed) {\n throw new Error(\"[walletwright] Slush approval: neither Approve nor Sign was actionable\");\n }\n await sleep(1500);\n const input = popup.locator('input[type=\"password\"]');\n if (await input.isVisible().catch(() => false)) {\n await input.fill(password);\n await fclick(popup, \"Unlock\");\n }\n },\n\n ecosystems: [\"sui\"],\n\n extensionName: \"Slush\",\n\n importWallet: async (page, seedPhrase, password) => {\n await fclick(page, \"More options\");\n await sleep(1000);\n await fclick(page, \"Import existing from passphrase\");\n await sleep(1500);\n\n const words = seedPhrase.trim().split(/\\s+/v);\n for (let i = 0; i < words.length; i++) {\n await page.locator(`input[placeholder=\"Word ${i + 1}\"]`).fill(words[i] ?? \"\");\n }\n await page.getByRole(\"button\", { name: \"Next\" }).click();\n await sleep(2000);\n\n await page.locator('input[placeholder=\"Password\"]').fill(password);\n await page.locator('input[placeholder=\"Confirm Password\"]').fill(password);\n await page.getByRole(\"button\", { name: \"Next\" }).click();\n await sleep(2000);\n\n await page\n .getByRole(\"button\", { name: \"Next\" })\n .click()\n .catch(() => {});\n await waitUntilOrThrow(() => atHome(page), {\n intervalMs: 1000,\n message: `Slush import never reached the wallet home (${HOME_ROUTE})`,\n timeoutMs: IMPORT_HOME_TIMEOUT_MS,\n });\n },\n\n notificationMatch: \"isPopup=1\",\n\n onboardingPage: \"index.html\",\n\n prepareContext: async (context) => {\n await context.route(\"**://*.slush.app/**\", (route) =>\n route.fulfill({\n body: JSON.stringify({ data: {} }),\n contentType: \"application/json\",\n status: 200,\n }),\n );\n },\n\n prepareExtension: (cacheDir) =>\n prepareWebStoreExtension({\n cacheDir,\n extensionId: SLUSH_EXTENSION_ID,\n name: \"slush-chrome-latest\",\n }),\n\n reachUnlockScreen,\n\n reject: async (popup) => {\n await sleep(2000);\n if (!(await fclick(popup, \"Reject\"))) {\n throw new Error(\"[walletwright] Slush reject: no Reject control was actionable\");\n }\n },\n\n unlock,\n};\n","import type { Page } from \"@playwright/test\";\n\nimport { prepareWebStoreExtension } from \"../internal/download\";\nimport { createUnlockScreen } from \"../internal/unlock-screen\";\nimport type { WalletDefinition } from \"../types\";\n\nconst SOLFLARE_EXTENSION_ID = \"bhhhlbepdkbapadjdnnojkbgioiodbic\";\n\nconst { reachUnlockScreen, unlock } = createUnlockScreen({\n entry: \"wallet.html\",\n wallet: \"Solflare\",\n});\n\nconst importWallet = async (page: Page, seedPhrase: string, password: string): Promise<void> => {\n await page.getByTestId(\"btn-import-existing-wallet\").click({ timeout: 30_000 });\n await page.getByText(\"Recovery phrase\", { exact: true }).first().click({ timeout: 30_000 });\n\n const words = seedPhrase.trim().split(/\\s+/v);\n await page.getByTestId(\"input-recovery-phrase-1\").waitFor({ state: \"visible\", timeout: 30_000 });\n for (let index = 0; index < words.length; index++) {\n await page.getByTestId(`input-recovery-phrase-${index + 1}`).fill(words[index] ?? \"\");\n }\n await page.getByTestId(\"btn-continue\").click({ timeout: 30_000 });\n\n await page.getByTestId(\"input-new-password\").waitFor({ state: \"visible\", timeout: 30_000 });\n await page.getByTestId(\"input-new-password\").fill(password);\n await page.getByTestId(\"input-repeat-password\").fill(password);\n await page.getByTestId(\"btn-continue\").click({ timeout: 30_000 });\n\n await page.getByTestId(\"btn-quick-setup\").click({ timeout: 60_000 });\n\n await page.getByTestId(\"btn-explore\").click({ timeout: 60_000 });\n};\n\nexport const solflare: WalletDefinition = {\n approve: async (popup) => {\n await popup\n .getByTestId(\"btn-connect\")\n .or(popup.getByTestId(\"btn-approve\"))\n .first()\n .click({ timeout: 30_000 });\n },\n\n ecosystems: [\"svm\"],\n\n extensionName: \"Solflare\",\n\n importWallet,\n\n notificationMatch: \"confirm_popup.html\",\n\n onboardingPage: \"wallet.html\",\n\n prepareExtension: (cacheDir) =>\n prepareWebStoreExtension({\n cacheDir,\n extensionId: SOLFLARE_EXTENSION_ID,\n name: \"solflare-chrome-latest\",\n }),\n\n reachUnlockScreen,\n\n reject: async (popup) => {\n await popup\n .getByTestId(\"btn-cancel\")\n .or(popup.getByTestId(\"btn-reject\"))\n .first()\n .click({ timeout: 30_000 });\n },\n\n unlock,\n};\n","import type { Ecosystem, WalletDefinition, WalletKind } from \"../types\";\n\nimport { metamask } from \"./metamask\";\nimport { phantom } from \"./phantom\";\nimport { rabby } from \"./rabby\";\nimport { slush } from \"./slush\";\nimport { solflare } from \"./solflare\";\n\nexport const wallets: Record<WalletKind, WalletDefinition> = {\n metamask,\n phantom,\n rabby,\n slush,\n solflare,\n};\n\n/** Narrow an arbitrary string (CLI flag, setup file field) to a supported wallet. */\nexport const isWalletKind = (value: string): value is WalletKind => Object.hasOwn(wallets, value);\n\nconst walletKinds = Object.keys(wallets).filter(isWalletKind);\n\n/** Wallet kinds that can drive the given ecosystem (e.g. `\"evm\"` → MetaMask, Phantom). */\nexport const walletKindsByEcosystem = (ecosystem: Ecosystem): Array<WalletKind> =>\n walletKinds.filter((kind) => wallets[kind].ecosystems.includes(ecosystem));\n","/**\n * How walletwright launches Chromium with a wallet extension loaded. Shared by the cache build and\n * the test run so the two cannot drift: an extension that loads for onboarding but not for the\n * tests (or the reverse) fails far from its cause.\n */\nexport const extensionContextOptions = (\n extensionPath: string,\n headless: boolean,\n): { args: Array<string>; channel: string; headless: boolean } => ({\n args: [`--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`],\n channel: \"chromium\",\n headless,\n});\n","import { mkdir, readdir, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { chromium } from \"@playwright/test\";\n\nimport type { WalletSetup } from \"../types\";\nimport { wallets } from \"../wallets/index\";\n\nimport { extensionContextOptions } from \"./chromium\";\nimport { DEFAULT_CACHE_DIR, extensionIdFromPath, extensionStateDir, profileKey } from \"./utils\";\nimport { gotoWithRetry, sleep, waitUntilOrThrow } from \"./wait\";\n\nconst NAVIGATION_TIMEOUT_MS = 15_000;\nconst FLUSH_SETTLE_MS = 3000;\nconst STATE_WRITE_TIMEOUT_MS = 10_000;\n\n/**\n * Chrome stores an extension's `chrome.storage.local` under `Local Extension Settings/<id>` and its\n * IndexedDB (what some single-page wallets persist their vault to) under\n * `IndexedDB/chrome-extension_<id>_0…`. Bytes in either one mean onboarding actually landed in the\n * profile; bytes in neither mean the \"cache\" is empty, and every later run would fail at unlock with\n * no hint that the build was the thing that went wrong.\n */\nconst hasPersistedState = async (profileDir: string, extensionId: string): Promise<boolean> => {\n const stores = [\n extensionStateDir(profileDir, extensionId),\n path.join(\n profileDir,\n \"Default\",\n \"IndexedDB\",\n `chrome-extension_${extensionId}_0.indexeddb.leveldb`,\n ),\n ];\n const listings = await Promise.allSettled(stores.map((dir) => readdir(dir)));\n return listings.some((listing) => listing.status === \"fulfilled\" && listing.value.length > 0);\n};\n\n/**\n * Import the wallet once and persist an onboarded browser profile to disk (the \"cache\"), so tests\n * launch a ready-to-unlock wallet instead of re-running onboarding each time. Returns the profile\n * directory. Idempotent per (wallet, version, seed, password).\n *\n * `headless` only affects this build step; the tests choose their own mode via `launchWallet`.\n */\nexport const buildCache = async (\n setup: WalletSetup,\n options: { headless?: boolean } = {},\n): Promise<string> => {\n const definition = wallets[setup.wallet];\n const cacheDir = path.resolve(setup.cacheDir ?? DEFAULT_CACHE_DIR);\n const extensionPath = await definition.prepareExtension(cacheDir, setup.version);\n\n const profileDir = path.join(cacheDir, profileKey(setup));\n await rm(profileDir, { force: true, recursive: true });\n await mkdir(profileDir, { recursive: true });\n\n const context = await chromium.launchPersistentContext(\n profileDir,\n extensionContextOptions(extensionPath, options.headless === true),\n );\n try {\n await definition.prepareContext?.(context);\n const extensionId = extensionIdFromPath(extensionPath);\n\n const page =\n context.pages().find((candidate) => candidate.url() === \"about:blank\") ??\n (await context.newPage());\n await gotoWithRetry(page, `chrome-extension://${extensionId}/${definition.onboardingPage}`, {\n label: `${definition.extensionName} onboarding page`,\n timeoutMs: NAVIGATION_TIMEOUT_MS,\n });\n await sleep(2000);\n\n await definition.importWallet(page, setup.seedPhrase, setup.password);\n await sleep(FLUSH_SETTLE_MS); // let the wallet flush state to disk before we close\n await waitUntilOrThrow(() => hasPersistedState(profileDir, extensionId), {\n intervalMs: 500,\n message: `${definition.extensionName} onboarding wrote no wallet state into ${profileDir}`,\n timeoutMs: STATE_WRITE_TIMEOUT_MS,\n });\n await context.close();\n await definition.finalizeCache?.(profileDir, extensionId);\n return profileDir;\n } catch (error) {\n await context.close().catch(() => {});\n throw error;\n }\n};\n"],"mappings":"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"}
|