w3wallets 1.0.0-beta.1 → 1.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,24 +15,23 @@ npm install -D w3wallets
15
15
 
16
16
  ## Getting Started
17
17
 
18
- `MetaMask`, `Backpack`, and `Polkadot{.js}` wallets are currently supported.
18
+ `MetaMask` and `Polkadot{.js}` wallets are currently supported.
19
19
 
20
20
  <p align="center">
21
21
  <img src="https://images.ctfassets.net/clixtyxoaeas/1ezuBGezqfIeifWdVtwU4c/d970d4cdf13b163efddddd5709164d2e/MetaMask-icon-Fox.svg" alt="Metamask Logo" width="60"/>
22
- <img src="https://raw.githubusercontent.com/coral-xyz/backpack/refs/heads/master/assets/backpack.png" alt="Backpack Logo" width="60"/>
23
22
  <img src="https://polkadot.js.org/logo.svg" alt="Polkadot JS Logo" width="60"/>
24
23
  </p>
25
24
 
26
25
  #### 1. Download wallets
27
26
 
28
27
  ```sh
29
- npx w3wallets metamask backpack polkadotjs
28
+ npx w3wallets metamask polkadotjs
30
29
  ```
31
30
 
32
31
  Short aliases are also supported:
33
32
 
34
33
  ```sh
35
- npx w3wallets mm bp pjs
34
+ npx w3wallets mm pjs
36
35
  ```
37
36
 
38
37
  The unzipped files are stored in the `.w3wallets/<wallet-name>` directory. Add `.w3wallets` to `.gitignore`.
@@ -45,8 +44,8 @@ USAGE:
45
44
  npx w3wallets [OPTIONS] <targets...>
46
45
 
47
46
  TARGETS:
48
- Alias name Known wallet alias (metamask, backpack, polkadotjs)
49
- Short alias Short form (mm, bp, pjs)
47
+ Alias name Known wallet alias (metamask, polkadotjs)
48
+ Short alias Short form (mm, pjs)
50
49
  Extension ID 32-character Chrome extension ID
51
50
  URL Chrome Web Store URL
52
51
 
@@ -59,7 +58,7 @@ OPTIONS:
59
58
 
60
59
  EXAMPLES:
61
60
  npx w3wallets metamask # Download MetaMask
62
- npx w3wallets mm bp pjs # Download all wallets (short)
61
+ npx w3wallets mm pjs # Download all wallets (short)
63
62
  npx w3wallets --list # List available aliases
64
63
  npx w3wallets -o ./extensions metamask # Custom output directory
65
64
  npx w3wallets --force mm # Force re-download
@@ -73,10 +72,10 @@ Install the required wallets into Chromium using `withWallets`.
73
72
 
74
73
  ```ts
75
74
  // your-fixture.ts
76
- import { withWallets, metamask, backpack, polkadotJS } from "w3wallets";
75
+ import { withWallets, metamask, polkadotJS } from "w3wallets";
77
76
  import { test as base } from "@playwright/test";
78
77
 
79
- export const test = withWallets(base, metamask, backpack, polkadotJS);
78
+ export const test = withWallets(base, metamask, polkadotJS);
80
79
 
81
80
  export { expect } from "@playwright/test";
82
81
  ```
@@ -94,7 +93,7 @@ import { test, expect } from "./your-fixture";
94
93
 
95
94
  test("Can connect MetaMask to dApp", async ({ page, metamask }) => {
96
95
  const mnemonic =
97
- "test test test test test test test test test test test junk";
96
+ "set your seed phrase test test test test test test test junk";
98
97
 
99
98
  await metamask.onboard(mnemonic);
100
99
  await page.goto("https://your-dapp.com");
@@ -105,3 +104,79 @@ test("Can connect MetaMask to dApp", async ({ page, metamask }) => {
105
104
  await expect(page.getByText("Connected")).toBeVisible();
106
105
  });
107
106
  ```
107
+
108
+ ## Caching
109
+
110
+ Wallet onboarding can be slow. Caching lets you run the setup once and reuse the browser profile across tests.
111
+
112
+ #### 1. Create a setup file
113
+
114
+ Create a `*.cache.ts` file in a `wallets-cache/` directory (default):
115
+
116
+ ```ts
117
+ // wallets-cache/default.cache.ts
118
+ import { prepareWallet, metamask } from "w3wallets";
119
+
120
+ export default prepareWallet(metamask, async (wallet, page) => {
121
+ await wallet.onboard("your seed phrase ...", "YourPassword123!");
122
+ });
123
+ ```
124
+
125
+ #### 2. Build the cache
126
+
127
+ ```sh
128
+ npx w3wallets cache
129
+ ```
130
+
131
+ <details>
132
+ <summary>CLI Options</summary>
133
+
134
+ ```
135
+ USAGE:
136
+ npx w3wallets cache [OPTIONS] [directory]
137
+
138
+ OPTIONS:
139
+ -f, --force Force rebuild even if cache exists
140
+ --headed Run browser in headed mode
141
+ directory Directory containing *.cache.{ts,js} files (default: ./wallets-cache/)
142
+ ```
143
+
144
+ </details>
145
+
146
+ The cached profiles are stored in `.w3wallets/cache/`. The `.w3wallets` directory should already be in `.gitignore`.
147
+
148
+ #### 3. Use cached wallets in tests
149
+
150
+ Import the setup and pass it to `withWallets`:
151
+
152
+ ```ts
153
+ import { test as base, expect } from "@playwright/test";
154
+ import { withWallets } from "w3wallets";
155
+ import cachedMetamask from "./wallets-cache/default.cache";
156
+
157
+ const test = withWallets(base, cachedMetamask);
158
+
159
+ test("wallet is ready", async ({ metamask }) => {
160
+ await metamask.unlock("YourPassword123!");
161
+ // wallet is already onboarded
162
+ });
163
+ ```
164
+
165
+ > **Note:** All wallets in a test must be either all cached or all non-cached.
166
+
167
+ ## Configuration
168
+
169
+ Configure library behavior via environment variables:
170
+
171
+ | Variable | Description | Default |
172
+ | -------------------------- | ------------------------------------------------------------------------- | ----------- |
173
+ | `W3WALLETS_ACTION_TIMEOUT` | Timeout (ms) for all wallet actions (click, fill, navigation, assertions) | `undefined` |
174
+
175
+ Example:
176
+
177
+ ```sh
178
+ # In .env or CI environment
179
+ W3WALLETS_ACTION_TIMEOUT=60000
180
+ ```
181
+
182
+ This only affects w3wallets library code. Your own Playwright configuration remains independent.
package/dist/index.d.mts CHANGED
@@ -23,6 +23,8 @@ interface WalletConfig<TName extends string = string, TWallet extends IWallet =
23
23
  * that don't have a `key` field in their manifest.
24
24
  */
25
25
  extensionId?: string;
26
+ /** Path to the extension's home page (e.g. "home.html"), used for cached wallets */
27
+ homeUrl?: string;
26
28
  }
27
29
  /**
28
30
  * Creates a wallet configuration object.
@@ -62,11 +64,11 @@ type Network = "Solana" | "Eclipse" | "Ethereum" | "Polygon" | "Base" | "Arbitru
62
64
  *
63
65
  * @example
64
66
  * ```ts
65
- * import { withWallets, metamask, backpack } from "w3wallets";
67
+ * import { withWallets, metamask, polkadotJS } from "w3wallets";
66
68
  *
67
- * const test = withWallets(base, metamask, backpack);
69
+ * const test = withWallets(base, metamask, polkadotJS);
68
70
  *
69
- * test("can connect", async ({ metamask, backpack }) => {
71
+ * test("can connect", async ({ metamask, polkadotJS }) => {
70
72
  * await metamask.onboard(mnemonic);
71
73
  * });
72
74
  * ```
@@ -77,40 +79,19 @@ declare function withWallets<const T extends readonly WalletConfig[]>(test: type
77
79
 
78
80
  declare abstract class Wallet implements IWallet {
79
81
  readonly page: Page;
80
- protected readonly extensionId: string;
82
+ readonly extensionId: string;
81
83
  constructor(page: Page, extensionId: string);
82
84
  abstract gotoOnboardPage(): Promise<void>;
83
85
  abstract approve(): Promise<void>;
84
86
  abstract deny(): Promise<void>;
85
87
  }
86
88
 
87
- declare class Backpack extends Wallet {
88
- private defaultPassword;
89
- private currentAccountId;
90
- private maxAccountId;
91
- gotoOnboardPage(): Promise<void>;
92
- onboard(network: Network, privateKey: string): Promise<void>;
93
- addAccount(network: Network, privateKey: string): Promise<void>;
94
- /**
95
- * Switch account
96
- * @param id The first added account has id 1, the second – 2, and so on
97
- */
98
- switchAccount(id: number): Promise<void>;
99
- unlock(): Promise<void>;
100
- setRPC(network: Network, rpc: string): Promise<void>;
101
- ignoreAndProceed(): Promise<void>;
102
- approve(): Promise<void>;
103
- deny(): Promise<void>;
104
- private _clickOnAccount;
105
- private _importAccount;
106
- }
107
-
108
- type NetworkSettings = {
89
+ interface NetworkSettings {
109
90
  name: string;
110
91
  rpc: string;
111
92
  chainId: number;
112
93
  currencySymbol: string;
113
- };
94
+ }
114
95
 
115
96
  declare class Metamask extends Wallet {
116
97
  private defaultPassword;
@@ -121,6 +102,38 @@ declare class Metamask extends Wallet {
121
102
  * @param password - Optional password (defaults to TestPassword123!)
122
103
  */
123
104
  onboard(mnemonic: string, password?: string): Promise<void>;
105
+ /**
106
+ * Dismiss MetaMask promotional popups (e.g., "Transaction Shield")
107
+ * that may overlay the confirmation UI.
108
+ */
109
+ dismissPopups(): Promise<void>;
110
+ private waitForPopupHidden;
111
+ /**
112
+ * MetaMask's MV3 service worker can fail to come up on cold-start
113
+ * (especially in fresh-extension CI runs), showing an error screen
114
+ * with a "Restart MetaMask" button. Detect and click it so the
115
+ * normal post-unlock flow can proceed.
116
+ */
117
+ private recoverFromStartupError;
118
+ /**
119
+ * After unlock, MetaMask may show onboarding screens, queued
120
+ * notifications, or go straight to the wallet UI. Race all possible
121
+ * states in a single wait to avoid sequential timeout penalties.
122
+ */
123
+ private stabilizePostUnlock;
124
+ /**
125
+ * Dismiss all queued MetaMask notifications (e.g., Solana/Tron account
126
+ * removal). These use the templated confirmation flow at #/confirmation/...
127
+ * If multiple are queued, "Reject all" appears; if only one, just
128
+ * confirmation-cancel-button is available.
129
+ */
130
+ private dismissQueuedNotifications;
131
+ /**
132
+ * Wait for a target button while handling the Transaction Shield popup.
133
+ * Always navigates to sidepanel.html fresh so MetaMask's
134
+ * ConfirmationHandler can route to the pending approval.
135
+ */
136
+ private waitAndClickButton;
124
137
  approve(): Promise<void>;
125
138
  deny(): Promise<void>;
126
139
  /**
@@ -128,7 +141,10 @@ declare class Metamask extends Wallet {
128
141
  */
129
142
  lock(): Promise<void>;
130
143
  /**
131
- * Unlock MetaMask with password
144
+ * Unlock MetaMask with password.
145
+ * After unlocking, stabilizes the wallet UI by handling post-unlock
146
+ * screens (metametrics, onboarding completion) and dismissing queued
147
+ * notifications. Ends on home.html with the wallet UI ready.
132
148
  */
133
149
  unlock(password?: string): Promise<void>;
134
150
  /**
@@ -159,10 +175,6 @@ declare class PolkadotJS extends Wallet {
159
175
  private _getLabeledInput;
160
176
  }
161
177
 
162
- /**
163
- * Pre-built Backpack wallet configuration.
164
- */
165
- declare const backpack: WalletConfig<"backpack", Backpack>;
166
178
  /**
167
179
  * Pre-built MetaMask wallet configuration.
168
180
  */
@@ -172,4 +184,34 @@ declare const metamask: WalletConfig<"metamask", Metamask>;
172
184
  */
173
185
  declare const polkadotJS: WalletConfig<"polkadotJS", PolkadotJS>;
174
186
 
175
- export { Backpack, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type WalletConfig, backpack, createWallet, metamask, polkadotJS, withWallets };
187
+ /**
188
+ * Configuration for w3wallets library.
189
+ * Values can be overridden via environment variables.
190
+ */
191
+ declare const config: {
192
+ /**
193
+ * Timeout for actions like click, fill, waitFor, goto.
194
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
195
+ * @default 30000 (30 seconds)
196
+ */
197
+ readonly actionTimeout: number | undefined;
198
+ /**
199
+ * Timeout for expect assertions like toBeVisible, toContainText.
200
+ * Set via W3WALLETS_EXPECT_TIMEOUT env variable.
201
+ * @default undefined (uses Playwright's default of 5000ms)
202
+ */
203
+ readonly expectTimeout: number | undefined;
204
+ };
205
+
206
+ declare function debug(message: string): void;
207
+
208
+ type SetupFn<TWallet extends IWallet> = (wallet: TWallet, page: Page) => Promise<void>;
209
+ interface CachedWalletConfig<TName extends string = string, TWallet extends IWallet = IWallet> extends WalletConfig<TName, TWallet> {
210
+ setupFn: SetupFn<TWallet>;
211
+ __cached: true;
212
+ }
213
+ declare function isCachedConfig(config: WalletConfig): config is CachedWalletConfig;
214
+
215
+ declare function prepareWallet<TName extends string, TWallet extends IWallet>(walletConfig: WalletConfig<TName, TWallet>, setupFn: SetupFn<TWallet>): CachedWalletConfig<TName, TWallet>;
216
+
217
+ export { type CachedWalletConfig, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type SetupFn, type WalletConfig, config, createWallet, debug, isCachedConfig, metamask, polkadotJS, prepareWallet, withWallets };
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ interface WalletConfig<TName extends string = string, TWallet extends IWallet =
23
23
  * that don't have a `key` field in their manifest.
24
24
  */
25
25
  extensionId?: string;
26
+ /** Path to the extension's home page (e.g. "home.html"), used for cached wallets */
27
+ homeUrl?: string;
26
28
  }
27
29
  /**
28
30
  * Creates a wallet configuration object.
@@ -62,11 +64,11 @@ type Network = "Solana" | "Eclipse" | "Ethereum" | "Polygon" | "Base" | "Arbitru
62
64
  *
63
65
  * @example
64
66
  * ```ts
65
- * import { withWallets, metamask, backpack } from "w3wallets";
67
+ * import { withWallets, metamask, polkadotJS } from "w3wallets";
66
68
  *
67
- * const test = withWallets(base, metamask, backpack);
69
+ * const test = withWallets(base, metamask, polkadotJS);
68
70
  *
69
- * test("can connect", async ({ metamask, backpack }) => {
71
+ * test("can connect", async ({ metamask, polkadotJS }) => {
70
72
  * await metamask.onboard(mnemonic);
71
73
  * });
72
74
  * ```
@@ -77,40 +79,19 @@ declare function withWallets<const T extends readonly WalletConfig[]>(test: type
77
79
 
78
80
  declare abstract class Wallet implements IWallet {
79
81
  readonly page: Page;
80
- protected readonly extensionId: string;
82
+ readonly extensionId: string;
81
83
  constructor(page: Page, extensionId: string);
82
84
  abstract gotoOnboardPage(): Promise<void>;
83
85
  abstract approve(): Promise<void>;
84
86
  abstract deny(): Promise<void>;
85
87
  }
86
88
 
87
- declare class Backpack extends Wallet {
88
- private defaultPassword;
89
- private currentAccountId;
90
- private maxAccountId;
91
- gotoOnboardPage(): Promise<void>;
92
- onboard(network: Network, privateKey: string): Promise<void>;
93
- addAccount(network: Network, privateKey: string): Promise<void>;
94
- /**
95
- * Switch account
96
- * @param id The first added account has id 1, the second – 2, and so on
97
- */
98
- switchAccount(id: number): Promise<void>;
99
- unlock(): Promise<void>;
100
- setRPC(network: Network, rpc: string): Promise<void>;
101
- ignoreAndProceed(): Promise<void>;
102
- approve(): Promise<void>;
103
- deny(): Promise<void>;
104
- private _clickOnAccount;
105
- private _importAccount;
106
- }
107
-
108
- type NetworkSettings = {
89
+ interface NetworkSettings {
109
90
  name: string;
110
91
  rpc: string;
111
92
  chainId: number;
112
93
  currencySymbol: string;
113
- };
94
+ }
114
95
 
115
96
  declare class Metamask extends Wallet {
116
97
  private defaultPassword;
@@ -121,6 +102,38 @@ declare class Metamask extends Wallet {
121
102
  * @param password - Optional password (defaults to TestPassword123!)
122
103
  */
123
104
  onboard(mnemonic: string, password?: string): Promise<void>;
105
+ /**
106
+ * Dismiss MetaMask promotional popups (e.g., "Transaction Shield")
107
+ * that may overlay the confirmation UI.
108
+ */
109
+ dismissPopups(): Promise<void>;
110
+ private waitForPopupHidden;
111
+ /**
112
+ * MetaMask's MV3 service worker can fail to come up on cold-start
113
+ * (especially in fresh-extension CI runs), showing an error screen
114
+ * with a "Restart MetaMask" button. Detect and click it so the
115
+ * normal post-unlock flow can proceed.
116
+ */
117
+ private recoverFromStartupError;
118
+ /**
119
+ * After unlock, MetaMask may show onboarding screens, queued
120
+ * notifications, or go straight to the wallet UI. Race all possible
121
+ * states in a single wait to avoid sequential timeout penalties.
122
+ */
123
+ private stabilizePostUnlock;
124
+ /**
125
+ * Dismiss all queued MetaMask notifications (e.g., Solana/Tron account
126
+ * removal). These use the templated confirmation flow at #/confirmation/...
127
+ * If multiple are queued, "Reject all" appears; if only one, just
128
+ * confirmation-cancel-button is available.
129
+ */
130
+ private dismissQueuedNotifications;
131
+ /**
132
+ * Wait for a target button while handling the Transaction Shield popup.
133
+ * Always navigates to sidepanel.html fresh so MetaMask's
134
+ * ConfirmationHandler can route to the pending approval.
135
+ */
136
+ private waitAndClickButton;
124
137
  approve(): Promise<void>;
125
138
  deny(): Promise<void>;
126
139
  /**
@@ -128,7 +141,10 @@ declare class Metamask extends Wallet {
128
141
  */
129
142
  lock(): Promise<void>;
130
143
  /**
131
- * Unlock MetaMask with password
144
+ * Unlock MetaMask with password.
145
+ * After unlocking, stabilizes the wallet UI by handling post-unlock
146
+ * screens (metametrics, onboarding completion) and dismissing queued
147
+ * notifications. Ends on home.html with the wallet UI ready.
132
148
  */
133
149
  unlock(password?: string): Promise<void>;
134
150
  /**
@@ -159,10 +175,6 @@ declare class PolkadotJS extends Wallet {
159
175
  private _getLabeledInput;
160
176
  }
161
177
 
162
- /**
163
- * Pre-built Backpack wallet configuration.
164
- */
165
- declare const backpack: WalletConfig<"backpack", Backpack>;
166
178
  /**
167
179
  * Pre-built MetaMask wallet configuration.
168
180
  */
@@ -172,4 +184,34 @@ declare const metamask: WalletConfig<"metamask", Metamask>;
172
184
  */
173
185
  declare const polkadotJS: WalletConfig<"polkadotJS", PolkadotJS>;
174
186
 
175
- export { Backpack, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type WalletConfig, backpack, createWallet, metamask, polkadotJS, withWallets };
187
+ /**
188
+ * Configuration for w3wallets library.
189
+ * Values can be overridden via environment variables.
190
+ */
191
+ declare const config: {
192
+ /**
193
+ * Timeout for actions like click, fill, waitFor, goto.
194
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
195
+ * @default 30000 (30 seconds)
196
+ */
197
+ readonly actionTimeout: number | undefined;
198
+ /**
199
+ * Timeout for expect assertions like toBeVisible, toContainText.
200
+ * Set via W3WALLETS_EXPECT_TIMEOUT env variable.
201
+ * @default undefined (uses Playwright's default of 5000ms)
202
+ */
203
+ readonly expectTimeout: number | undefined;
204
+ };
205
+
206
+ declare function debug(message: string): void;
207
+
208
+ type SetupFn<TWallet extends IWallet> = (wallet: TWallet, page: Page) => Promise<void>;
209
+ interface CachedWalletConfig<TName extends string = string, TWallet extends IWallet = IWallet> extends WalletConfig<TName, TWallet> {
210
+ setupFn: SetupFn<TWallet>;
211
+ __cached: true;
212
+ }
213
+ declare function isCachedConfig(config: WalletConfig): config is CachedWalletConfig;
214
+
215
+ declare function prepareWallet<TName extends string, TWallet extends IWallet>(walletConfig: WalletConfig<TName, TWallet>, setupFn: SetupFn<TWallet>): CachedWalletConfig<TName, TWallet>;
216
+
217
+ export { type CachedWalletConfig, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type SetupFn, type WalletConfig, config, createWallet, debug, isCachedConfig, metamask, polkadotJS, prepareWallet, withWallets };