w3wallets 0.10.2 → 1.0.0-beta.10

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,21 +15,56 @@ 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 backpack polkadotJS
28
+ npx w3wallets metamask polkadotjs
30
29
  ```
31
30
 
32
- The unzipped files should be stored in the `.w3wallets/<wallet-name>` directory. Add them to `.gitignore`.
31
+ Short aliases are also supported:
32
+
33
+ ```sh
34
+ npx w3wallets mm pjs
35
+ ```
36
+
37
+ The unzipped files are stored in the `.w3wallets/<wallet-name>` directory. Add `.w3wallets` to `.gitignore`.
38
+
39
+ <details>
40
+ <summary>CLI Options</summary>
41
+
42
+ ```
43
+ USAGE:
44
+ npx w3wallets [OPTIONS] <targets...>
45
+
46
+ TARGETS:
47
+ Alias name Known wallet alias (metamask, polkadotjs)
48
+ Short alias Short form (mm, pjs)
49
+ Extension ID 32-character Chrome extension ID
50
+ URL Chrome Web Store URL
51
+
52
+ OPTIONS:
53
+ -h, --help Show help message
54
+ -l, --list List available wallet aliases
55
+ -o, --output Output directory (default: .w3wallets)
56
+ -f, --force Force re-download even if already exists
57
+ --debug Save raw .crx file for debugging
58
+
59
+ EXAMPLES:
60
+ npx w3wallets metamask # Download MetaMask
61
+ npx w3wallets mm pjs # Download all wallets (short)
62
+ npx w3wallets --list # List available aliases
63
+ npx w3wallets -o ./extensions metamask # Custom output directory
64
+ npx w3wallets --force mm # Force re-download
65
+ ```
66
+
67
+ </details>
33
68
 
34
69
  #### 2. Wrap your fixture with `withWallets`
35
70
 
@@ -37,20 +72,10 @@ Install the required wallets into Chromium using `withWallets`.
37
72
 
38
73
  ```ts
39
74
  // your-fixture.ts
40
- import { withWallets } from "w3wallets";
75
+ import { withWallets, metamask, polkadotJS } from "w3wallets";
41
76
  import { test as base } from "@playwright/test";
42
77
 
43
- export const test = withWallets(
44
- base,
45
- "backpack",
46
- "polkadotJS",
47
- ).extend<BaseFixture>({
48
- magic: (_, use) => use(42),
49
- });
50
-
51
- type BaseFixture = {
52
- magic: number;
53
- };
78
+ export const test = withWallets(base, metamask, polkadotJS);
54
79
 
55
80
  export { expect } from "@playwright/test";
56
81
  ```
@@ -64,12 +89,94 @@ Most commonly, you will use the following methods:
64
89
  3. `deny`: for actions that reject or cancel operations
65
90
 
66
91
  ```ts
67
- import { test } from "./your-fixture";
92
+ import { test, expect } from "./your-fixture";
93
+
94
+ test("Can connect MetaMask to dApp", async ({ page, metamask }) => {
95
+ const mnemonic =
96
+ "set your seed phrase test test test test test test test junk";
97
+
98
+ await metamask.onboard(mnemonic);
99
+ await page.goto("https://your-dapp.com");
100
+
101
+ await page.getByRole("button", { name: "Connect Wallet" }).click();
102
+ await metamask.approve();
103
+
104
+ await expect(page.getByText("Connected")).toBeVisible();
105
+ });
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";
68
156
 
69
- test("Can use wallet", async ({ page, backpack }) => {
70
- const privateKey =
71
- "4wDJd9Ds5ueTdS95ReAZGSBVkjMcNKbgZk47xcmqzpUJjCt7VoB2Cs7hqwXWRnopzXqE4mCP6BEDHCYrFttEcBw2";
157
+ const test = withWallets(base, cachedMetamask);
72
158
 
73
- await backpack.onboard("Eclipse", privateKey);
159
+ test("wallet is ready", async ({ metamask }) => {
160
+ await metamask.unlock("YourPassword123!");
161
+ // wallet is already onboarded
74
162
  });
75
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
@@ -1,43 +1,159 @@
1
1
  import * as playwright_test from 'playwright/test';
2
2
  import { Page, test, BrowserContext } from '@playwright/test';
3
3
 
4
- /**
5
- * Represents the supported network types for the BackPack application.
6
- */
7
- type BackPackNetwork = "Solana" | "Eclipse" | "Ethereum" | "Polygon" | "Base" | "Arbitrum" | "Optimism";
8
-
9
- type WalletName = "backpack" | "polkadotJS" | "metamask";
10
- type NoDuplicates<T extends readonly unknown[], Acc extends readonly unknown[] = []> = T extends [infer Head, ...infer Tail] ? Head extends Acc[number] ? never : [Head, ...NoDuplicates<Tail, [...Acc, Head]>] : T;
11
4
  interface IWallet {
5
+ readonly page: Page;
12
6
  gotoOnboardPage(): Promise<void>;
7
+ approve(): Promise<void>;
8
+ deny(): Promise<void>;
9
+ }
10
+ /**
11
+ * Configuration object for a wallet.
12
+ */
13
+ interface WalletConfig<TName extends string = string, TWallet extends IWallet = IWallet> {
14
+ /** Unique wallet identifier, used as fixture name */
15
+ name: TName;
16
+ /** Directory name under .w3wallets/ where extension is stored */
17
+ extensionDir: string;
18
+ /** Wallet class constructor */
19
+ WalletClass: new (page: Page, extensionId: string) => TWallet;
20
+ /**
21
+ * Chrome extension ID. If not provided, it will be derived from
22
+ * the manifest.json `key` field. Required for custom extensions
23
+ * that don't have a `key` field in their manifest.
24
+ */
25
+ extensionId?: string;
26
+ /** Path to the extension's home page (e.g. "home.html"), used for cached wallets */
27
+ homeUrl?: string;
13
28
  }
29
+ /**
30
+ * Creates a wallet configuration object.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const myWallet = createWallet({
35
+ * name: "myWallet",
36
+ * extensionDir: "my-wallet",
37
+ * WalletClass: MyWalletClass,
38
+ * });
39
+ * ```
40
+ */
41
+ declare function createWallet<TName extends string, TWallet extends IWallet>(config: WalletConfig<TName, TWallet>): WalletConfig<TName, TWallet>;
42
+ /**
43
+ * Extracts fixture name from a wallet config.
44
+ */
45
+ type WalletConfigName<T> = T extends WalletConfig<infer N, IWallet> ? N : never;
46
+ /**
47
+ * Extracts wallet class instance type from a wallet config.
48
+ */
49
+ type WalletConfigInstance<T> = T extends WalletConfig<string, infer W> ? W : never;
50
+ /**
51
+ * Builds fixture types from an array of wallet configs.
52
+ */
53
+ type WalletFixturesFromConfigs<T extends readonly WalletConfig[]> = {
54
+ [K in T[number] as WalletConfigName<K>]: WalletConfigInstance<K>;
55
+ };
56
+ /**
57
+ * Supported blockchain networks.
58
+ * Common networks are listed for autocomplete, but any string is accepted.
59
+ */
60
+ type Network = "Solana" | "Eclipse" | "Ethereum" | "Polygon" | "Base" | "Arbitrum" | "Optimism" | (string & {});
61
+
62
+ /**
63
+ * Extends Playwright test with wallet fixtures.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { withWallets, metamask, polkadotJS } from "w3wallets";
68
+ *
69
+ * const test = withWallets(base, metamask, polkadotJS);
70
+ *
71
+ * test("can connect", async ({ metamask, polkadotJS }) => {
72
+ * await metamask.onboard(mnemonic);
73
+ * });
74
+ * ```
75
+ */
76
+ declare function withWallets<const T extends readonly WalletConfig[]>(test: typeof test, ...wallets: T): playwright_test.TestType<playwright_test.PlaywrightTestArgs & playwright_test.PlaywrightTestOptions & WalletFixturesFromConfigs<T> & {
77
+ context: BrowserContext;
78
+ }, playwright_test.PlaywrightWorkerArgs & playwright_test.PlaywrightWorkerOptions>;
14
79
 
15
80
  declare abstract class Wallet implements IWallet {
16
- page: Page;
17
- protected extensionId: string;
81
+ readonly page: Page;
82
+ readonly extensionId: string;
18
83
  constructor(page: Page, extensionId: string);
19
84
  abstract gotoOnboardPage(): Promise<void>;
85
+ abstract approve(): Promise<void>;
86
+ abstract deny(): Promise<void>;
87
+ }
88
+
89
+ interface NetworkSettings {
90
+ name: string;
91
+ rpc: string;
92
+ chainId: number;
93
+ currencySymbol: string;
20
94
  }
21
95
 
22
- declare class Backpack extends Wallet {
96
+ declare class Metamask extends Wallet {
23
97
  private defaultPassword;
24
- private currentAccountId;
25
- private maxAccountId;
26
98
  gotoOnboardPage(): Promise<void>;
27
- onboard(network: BackPackNetwork, privateKey: string): Promise<void>;
28
- addAccount(network: BackPackNetwork, privateKey: string): Promise<void>;
29
99
  /**
30
- * Switch account
31
- * @param id The first added account has id 1, the second – 2, and so on
100
+ * Onboard MetaMask with a mnemonic phrase
101
+ * @param mnemonic - 12 or 24 word recovery phrase
102
+ * @param password - Optional password (defaults to TestPassword123!)
103
+ */
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
+ * After unlock, MetaMask may show onboarding screens, queued
113
+ * notifications, or go straight to the wallet UI. Race all possible
114
+ * states in a single wait to avoid sequential timeout penalties.
115
+ */
116
+ private stabilizePostUnlock;
117
+ /**
118
+ * Dismiss all queued MetaMask notifications (e.g., Solana/Tron account
119
+ * removal). These use the templated confirmation flow at #/confirmation/...
120
+ * If multiple are queued, "Reject all" appears; if only one, just
121
+ * confirmation-cancel-button is available.
32
122
  */
33
- switchAccount(id: number): Promise<void>;
34
- unlock(): Promise<void>;
35
- setRPC(network: BackPackNetwork, rpc: string): Promise<void>;
36
- ignoreAndProceed(): Promise<void>;
123
+ private dismissQueuedNotifications;
124
+ /**
125
+ * Wait for a target button while handling the Transaction Shield popup.
126
+ * Always navigates to sidepanel.html fresh so MetaMask's
127
+ * ConfirmationHandler can route to the pending approval.
128
+ */
129
+ private waitAndClickButton;
37
130
  approve(): Promise<void>;
38
131
  deny(): Promise<void>;
39
- private _clickOnAccount;
40
- private _importAccount;
132
+ /**
133
+ * Lock the MetaMask wallet
134
+ */
135
+ lock(): Promise<void>;
136
+ /**
137
+ * Unlock MetaMask with password.
138
+ * After unlocking, stabilizes the wallet UI by handling post-unlock
139
+ * screens (metametrics, onboarding completion) and dismissing queued
140
+ * notifications. Ends on home.html with the wallet UI ready.
141
+ */
142
+ unlock(password?: string): Promise<void>;
143
+ /**
144
+ * Switch to an existing network in MetaMask
145
+ * @param networkName - Name of the network to switch to (e.g., "Ethereum Mainnet", "Sepolia")
146
+ */
147
+ switchNetwork(networkName: string, networkType?: "Popular" | "Custom"): Promise<void>;
148
+ switchAccount(accountName: string): Promise<void>;
149
+ /**
150
+ * Add a custom network to MetaMask
151
+ */
152
+ addNetwork(network: NetworkSettings): Promise<void>;
153
+ addCustomNetwork(settings: NetworkSettings): Promise<void>;
154
+ enableTestNetworks(): Promise<void>;
155
+ importAccount(privateKey: string): Promise<void>;
156
+ accountNameIs(accountName: string): Promise<void>;
41
157
  }
42
158
 
43
159
  declare class PolkadotJS extends Wallet {
@@ -52,41 +168,43 @@ declare class PolkadotJS extends Wallet {
52
168
  private _getLabeledInput;
53
169
  }
54
170
 
55
- type NetworkSettings = {
56
- name: string;
57
- rpc: string;
58
- chainId: number;
59
- currencySymbol: string;
60
- };
171
+ /**
172
+ * Pre-built MetaMask wallet configuration.
173
+ */
174
+ declare const metamask: WalletConfig<"metamask", Metamask>;
175
+ /**
176
+ * Pre-built Polkadot.js wallet configuration.
177
+ */
178
+ declare const polkadotJS: WalletConfig<"polkadotJS", PolkadotJS>;
61
179
 
62
- declare class Metamask extends Wallet {
63
- private defaultPassword;
64
- gotoOnboardPage(): Promise<void>;
180
+ /**
181
+ * Configuration for w3wallets library.
182
+ * Values can be overridden via environment variables.
183
+ */
184
+ declare const config: {
65
185
  /**
66
- *
67
- * @param mnemonic 12-word mnemonic seed phrase
186
+ * Timeout for actions like click, fill, waitFor, goto.
187
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
188
+ * @default 30000 (30 seconds)
68
189
  */
69
- onboard(mnemonic: string, password?: string): Promise<void>;
70
- switchAccount(accountName: {
71
- name: string;
72
- }): Promise<void>;
73
- importAccount(privateKey: string): Promise<void>;
74
- addAccount(accountName?: string): Promise<void>;
75
- getAccountName(): Promise<string>;
76
- connectToNetwork(networkName: string, networkType?: "Popular" | "Custom"): Promise<void>;
77
- addCustomNetwork(settings: NetworkSettings): Promise<void>;
78
- enableTestNetworks(): Promise<void>;
79
- approve(): Promise<void>;
80
- deny(): Promise<void>;
81
- private usingNotificationPage;
82
- private clickTopRightCornerToCloseAllTheMarketingBullshit;
190
+ readonly actionTimeout: number | undefined;
191
+ /**
192
+ * Timeout for expect assertions like toBeVisible, toContainText.
193
+ * Set via W3WALLETS_EXPECT_TIMEOUT env variable.
194
+ * @default undefined (uses Playwright's default of 5000ms)
195
+ */
196
+ readonly expectTimeout: number | undefined;
197
+ };
198
+
199
+ declare function debug(message: string): void;
200
+
201
+ type SetupFn<TWallet extends IWallet> = (wallet: TWallet, page: Page) => Promise<void>;
202
+ interface CachedWalletConfig<TName extends string = string, TWallet extends IWallet = IWallet> extends WalletConfig<TName, TWallet> {
203
+ setupFn: SetupFn<TWallet>;
204
+ __cached: true;
83
205
  }
206
+ declare function isCachedConfig(config: WalletConfig): config is CachedWalletConfig;
84
207
 
85
- declare function withWallets<T extends readonly WalletName[]>(test: typeof test, ...config: NoDuplicates<T>): playwright_test.TestType<playwright_test.PlaywrightTestArgs & playwright_test.PlaywrightTestOptions & {
86
- context: BrowserContext;
87
- backpack: Backpack;
88
- polkadotJS: PolkadotJS;
89
- metamask: Metamask;
90
- }, playwright_test.PlaywrightWorkerArgs & playwright_test.PlaywrightWorkerOptions>;
208
+ declare function prepareWallet<TName extends string, TWallet extends IWallet>(walletConfig: WalletConfig<TName, TWallet>, setupFn: SetupFn<TWallet>): CachedWalletConfig<TName, TWallet>;
91
209
 
92
- export { withWallets };
210
+ export { type CachedWalletConfig, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type SetupFn, type WalletConfig, config, createWallet, debug, isCachedConfig, metamask, polkadotJS, prepareWallet, withWallets };