@walletwright/core 0.2.0

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.
@@ -0,0 +1,196 @@
1
+ import { BrowserContext, Locator, Page } from "@playwright/test";
2
+ //#region src/types.d.ts
3
+ /** Blockchain ecosystem a wallet operates in. A wallet may span several (e.g. Phantom = EVM + SVM). */
4
+ type Ecosystem = "evm" | "svm" | "sui" | "dot" | "btc";
5
+ /**
6
+ * Supported wallet extensions. Only wallets with a verified end-to-end (connect + sign) flow are
7
+ * listed. The roadmap (top 3 per ecosystem) is tracked in `AGENTS.md`; each lands here once driven.
8
+ */
9
+ type WalletKind = "metamask" | "phantom" | "rabby" | "slush" | "solflare";
10
+ /** A wallet to import and the credentials to unlock it. */
11
+ type WalletSetup = {
12
+ /** Cache directory for downloaded extensions and onboarded profiles. Defaults to `.walletwright`. */
13
+ cacheDir?: string;
14
+ password: string;
15
+ /** 12/24-word seed phrase. Avoid the famous public test seed for Phantom, it blocks connections. */
16
+ seedPhrase: string;
17
+ /** Pin a specific extension version (defaults to a known-good version per wallet). */
18
+ version?: string;
19
+ wallet: WalletKind;
20
+ };
21
+ /**
22
+ * Everything an action needs to drive the wallet's own UI (as opposed to an approval popup). Passed
23
+ * as one object so a new dependency doesn't churn every wallet's action signatures.
24
+ */
25
+ type WalletActionContext = {
26
+ context: BrowserContext;
27
+ extensionId: string;
28
+ /** The wallet's own extension page, kept open after unlock. */
29
+ home: Page;
30
+ password: string;
31
+ };
32
+ /**
33
+ * The wallet-side counterpart of one dapp-facing capability: the same arguments, with the action
34
+ * context threaded in front.
35
+ */
36
+ type WalletAction<Method> = Method extends ((...args: infer Args) => Promise<void>) ? (ctx: WalletActionContext, ...args: Args) => Promise<void> : never;
37
+ /**
38
+ * The wallet-side half of a dapp-facing capability group, derived from that group's `*Api` type on
39
+ * `Wallet` so a capability's name and arguments are written exactly once.
40
+ */
41
+ type WalletActionsFor<Api> = { [Name in keyof Api]?: WalletAction<Api[Name]>; };
42
+ /** Create, import, rename, and switch accounts from the wallet's own UI. */
43
+ type AccountActions = WalletActionsFor<AccountsApi>;
44
+ /** A custom EVM network, as the wallet's add-network form expects it. */
45
+ type NetworkConfig = {
46
+ chainId: number;
47
+ name: string;
48
+ rpcUrl: string;
49
+ symbol: string;
50
+ };
51
+ /** Add a custom network and switch the active one, from the wallet's own UI. */
52
+ type NetworkActions = WalletActionsFor<NetworkApi>;
53
+ /** Lock and unlock the wallet itself, from its own UI. */
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
+ */
61
+ type WalletActions = {
62
+ accounts?: AccountActions;
63
+ network?: NetworkActions;
64
+ settings?: SettingsActions;
65
+ };
66
+ /**
67
+ * Everything wallet-specific that the generic engine needs. One implementation per wallet lives in
68
+ * `src/wallets/*`.
69
+ */
70
+ type WalletDefinition = {
71
+ /** Optional capabilities beyond connect/sign. Omit a group the wallet can't (or doesn't) drive. */
72
+ 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
+ */
79
+ approvalControls?: (popup: Page) => Locator;
80
+ /**
81
+ * Click the approve/confirm button in an approval popup (connect or sign). `password` is provided
82
+ * because some wallets (e.g. Slush) re-prompt for it to authorize a signature.
83
+ */
84
+ approve: (popup: Page, password: string) => Promise<void>;
85
+ /** Ecosystems this wallet can drive (e.g. `["evm", "svm"]` for Phantom). */
86
+ ecosystems: ReadonlyArray<Ecosystem>;
87
+ /** Name as it appears in `chrome://extensions` (used to resolve the loaded extension id). */
88
+ extensionName: string;
89
+ /**
90
+ * Optional fix applied to the persisted profile *after* the build context closes (browser not
91
+ * holding the DB), e.g. forcing `completedOnboarding=true` in MetaMask's leveldb.
92
+ */
93
+ 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
+ */
100
+ headlessApprovals?: boolean;
101
+ /** Run the import-from-seed onboarding flow. */
102
+ importWallet: (page: Page, seedPhrase: string, password: string) => Promise<void>;
103
+ /**
104
+ * URL token that identifies this wallet's approval popup. Defaults to `notification.html`
105
+ * (MetaMask/Phantom). Single-page wallets differ, Slush routes approvals through `index.html` and
106
+ * marks them with `isPopup=1`.
107
+ */
108
+ notificationMatch?: string;
109
+ /** Extension-relative path of the first-run onboarding entry (e.g. `home.html`). */
110
+ onboardingPage: string;
111
+ /**
112
+ * Applied to every context this wallet runs in, before anything navigates: `buildCache`'s and
113
+ * `launchWallet`'s alike. For wallets that need the browser itself adjusted (routing, permissions)
114
+ * rather than a page driven.
115
+ */
116
+ prepareContext?: (context: BrowserContext) => Promise<void>;
117
+ /** Download + extract the unpacked extension into `cacheDir`; returns its absolute path. */
118
+ prepareExtension: (cacheDir: string, version?: string) => Promise<string>;
119
+ /**
120
+ * Open the wallet's home/unlock page and return it once it has settled into a known state (its
121
+ * password screen, or an already-unlocked wallet for the wallets that can reopen that way). Throws
122
+ * rather than returning a page that never rendered. The returned page stays open as `Wallet.home`.
123
+ */
124
+ reachUnlockScreen: (context: BrowserContext, extensionId: string) => Promise<Page>;
125
+ /**
126
+ * Click the cancel/reject button in an approval popup, the counterpart of `approve`. Optional:
127
+ * a wallet declares it only once it has been driven against the real extension.
128
+ */
129
+ reject?: (popup: Page) => Promise<void>;
130
+ /** Unlock the wallet on its (already-open) home page. */
131
+ unlock: (page: Page, password: string) => Promise<void>;
132
+ };
133
+ /** Lock and unlock the wallet from its own UI. Throws if the wallet doesn't declare support. */
134
+ type SettingsApi = {
135
+ lock: () => Promise<void>;
136
+ unlock: () => Promise<void>;
137
+ };
138
+ /** Add and switch networks from the wallet's own UI. Throws if the wallet doesn't declare support. */
139
+ type NetworkApi = {
140
+ 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
+ */
147
+ switch: (chainId: number) => Promise<void>;
148
+ };
149
+ /** Manage accounts from the wallet's own UI. Throws if the wallet doesn't declare support. */
150
+ type AccountsApi = {
151
+ /** Derive the next HD account from the seed. */
152
+ add: () => Promise<void>;
153
+ importPrivateKey: (privateKey: string) => Promise<void>;
154
+ rename: (options: {
155
+ index: number;
156
+ name: string;
157
+ }) => Promise<void>;
158
+ /** Make the account at `index` (order shown in the wallet's account list) the active one. */
159
+ switch: (index: number) => Promise<void>;
160
+ };
161
+ /** Drives an unlocked wallet against a dapp under test. */
162
+ type Wallet = {
163
+ accounts: AccountsApi;
164
+ /** Approve whatever approval popup is currently pending (connect, sign, tx…). */
165
+ approve: (options?: {
166
+ optional?: boolean;
167
+ }) => Promise<void>;
168
+ /** Approve a pending signature request popup. */
169
+ confirmSignature: () => Promise<void>;
170
+ /** Approve a pending transaction request popup. */
171
+ confirmTransaction: () => Promise<void>;
172
+ /** Approve a pending connection request popup. Resolves quietly if the wallet auto-approved. */
173
+ connectToDapp: () => Promise<void>;
174
+ /** The loaded extension id. */
175
+ readonly extensionId: string;
176
+ /**
177
+ * The wallet's own extension page, kept open after unlock. Named `home` rather than `page` because
178
+ * `page` already means the dapp under test in every spec.
179
+ */
180
+ readonly home: Page;
181
+ network: NetworkApi;
182
+ /** Reject whatever approval popup is currently pending (connect, sign, tx…). */
183
+ reject: (options?: {
184
+ optional?: boolean;
185
+ }) => Promise<void>;
186
+ /** Reject a pending connection request popup. */
187
+ rejectConnection: () => Promise<void>;
188
+ /** Reject a pending signature request popup. */
189
+ rejectSignature: () => Promise<void>;
190
+ /** Reject a pending transaction request popup. */
191
+ rejectTransaction: () => Promise<void>;
192
+ settings: SettingsApi;
193
+ };
194
+ //#endregion
195
+ export { WalletSetup as a, WalletKind as i, Wallet as n, WalletDefinition as r, Ecosystem as t };
196
+ //# sourceMappingURL=types-CWxz_dJe.d.mts.map
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@walletwright/core",
3
+ "version": "0.2.0",
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
+ "keywords": [
6
+ "e2e",
7
+ "ethereum",
8
+ "metamask",
9
+ "phantom",
10
+ "playwright",
11
+ "rabby",
12
+ "slush",
13
+ "solana",
14
+ "solflare",
15
+ "sui",
16
+ "testing",
17
+ "wallet",
18
+ "web3"
19
+ ],
20
+ "license": "MIT",
21
+ "author": "Pedro Filho <pedro@filho.me>",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/pedroapfilho/walletwright.git",
25
+ "directory": "packages/walletwright"
26
+ },
27
+ "bin": {
28
+ "walletwright": "dist/cli.mjs"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "CHANGELOG.md"
33
+ ],
34
+ "type": "module",
35
+ "types": "dist/index.d.mts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.mts",
39
+ "default": "./dist/index.mjs"
40
+ },
41
+ "./chain": {
42
+ "types": "./dist/chain.d.mts",
43
+ "default": "./dist/chain.mjs"
44
+ },
45
+ "./mock": {
46
+ "types": "./dist/mock.d.mts",
47
+ "default": "./dist/mock.mjs"
48
+ },
49
+ "./mock-standard": {
50
+ "types": "./dist/mock-standard.d.mts",
51
+ "default": "./dist/mock-standard.mjs"
52
+ }
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "dependencies": {
58
+ "adm-zip": "^0.6.0",
59
+ "classic-level": "^1.4.1"
60
+ },
61
+ "devDependencies": {
62
+ "@playwright/test": "1.62.1",
63
+ "@types/adm-zip": "^0.5.7",
64
+ "@types/node": "^25.9.1",
65
+ "prool": "^0.2.10",
66
+ "typescript": "^6.0.3",
67
+ "viem": "^2.21.0",
68
+ "vitest": "^4.1.8",
69
+ "@repo/config-vitest": "0.0.0",
70
+ "@repo/typescript-config": "0.0.0"
71
+ },
72
+ "peerDependencies": {
73
+ "@playwright/test": ">=1.48 <2",
74
+ "prool": ">=0.2 <1",
75
+ "viem": ">=2 <3"
76
+ },
77
+ "peerDependenciesMeta": {
78
+ "prool": {
79
+ "optional": true
80
+ },
81
+ "viem": {
82
+ "optional": true
83
+ }
84
+ },
85
+ "scripts": {
86
+ "build": "tsdown",
87
+ "clean": "rm -rf dist",
88
+ "dev": "tsdown --watch",
89
+ "lint": "oxlint src",
90
+ "test": "vitest run",
91
+ "test:coverage": "vitest run --coverage",
92
+ "typecheck": "tsc -p tsconfig.json --noEmit"
93
+ }
94
+ }