w3wallets 1.0.0-beta.1 → 1.0.0-beta.3

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,20 @@ test("Can connect MetaMask to dApp", async ({ page, metamask }) => {
105
104
  await expect(page.getByText("Connected")).toBeVisible();
106
105
  });
107
106
  ```
107
+
108
+ ## Configuration
109
+
110
+ Configure library behavior via environment variables:
111
+
112
+ | Variable | Description | Default |
113
+ | -------------------------- | ------------------------------------------------------------------------- | ----------- |
114
+ | `W3WALLETS_ACTION_TIMEOUT` | Timeout (ms) for all wallet actions (click, fill, navigation, assertions) | `undefined` |
115
+
116
+ Example:
117
+
118
+ ```sh
119
+ # In .env or CI environment
120
+ W3WALLETS_ACTION_TIMEOUT=60000
121
+ ```
122
+
123
+ This only affects w3wallets library code. Your own Playwright configuration remains independent.
package/dist/index.d.mts CHANGED
@@ -62,11 +62,11 @@ type Network = "Solana" | "Eclipse" | "Ethereum" | "Polygon" | "Base" | "Arbitru
62
62
  *
63
63
  * @example
64
64
  * ```ts
65
- * import { withWallets, metamask, backpack } from "w3wallets";
65
+ * import { withWallets, metamask, polkadotJS } from "w3wallets";
66
66
  *
67
- * const test = withWallets(base, metamask, backpack);
67
+ * const test = withWallets(base, metamask, polkadotJS);
68
68
  *
69
- * test("can connect", async ({ metamask, backpack }) => {
69
+ * test("can connect", async ({ metamask, polkadotJS }) => {
70
70
  * await metamask.onboard(mnemonic);
71
71
  * });
72
72
  * ```
@@ -84,27 +84,6 @@ declare abstract class Wallet implements IWallet {
84
84
  abstract deny(): Promise<void>;
85
85
  }
86
86
 
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
87
  type NetworkSettings = {
109
88
  name: string;
110
89
  rpc: string;
@@ -159,10 +138,6 @@ declare class PolkadotJS extends Wallet {
159
138
  private _getLabeledInput;
160
139
  }
161
140
 
162
- /**
163
- * Pre-built Backpack wallet configuration.
164
- */
165
- declare const backpack: WalletConfig<"backpack", Backpack>;
166
141
  /**
167
142
  * Pre-built MetaMask wallet configuration.
168
143
  */
@@ -172,4 +147,17 @@ declare const metamask: WalletConfig<"metamask", Metamask>;
172
147
  */
173
148
  declare const polkadotJS: WalletConfig<"polkadotJS", PolkadotJS>;
174
149
 
175
- export { Backpack, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type WalletConfig, backpack, createWallet, metamask, polkadotJS, withWallets };
150
+ /**
151
+ * Configuration for w3wallets library.
152
+ * Values can be overridden via environment variables.
153
+ */
154
+ declare const config: {
155
+ /**
156
+ * Timeout for actions like click, fill, waitFor, goto.
157
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
158
+ * @default 30000 (30 seconds)
159
+ */
160
+ readonly actionTimeout: number | undefined;
161
+ };
162
+
163
+ export { type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type WalletConfig, config, createWallet, metamask, polkadotJS, withWallets };
package/dist/index.d.ts CHANGED
@@ -62,11 +62,11 @@ type Network = "Solana" | "Eclipse" | "Ethereum" | "Polygon" | "Base" | "Arbitru
62
62
  *
63
63
  * @example
64
64
  * ```ts
65
- * import { withWallets, metamask, backpack } from "w3wallets";
65
+ * import { withWallets, metamask, polkadotJS } from "w3wallets";
66
66
  *
67
- * const test = withWallets(base, metamask, backpack);
67
+ * const test = withWallets(base, metamask, polkadotJS);
68
68
  *
69
- * test("can connect", async ({ metamask, backpack }) => {
69
+ * test("can connect", async ({ metamask, polkadotJS }) => {
70
70
  * await metamask.onboard(mnemonic);
71
71
  * });
72
72
  * ```
@@ -84,27 +84,6 @@ declare abstract class Wallet implements IWallet {
84
84
  abstract deny(): Promise<void>;
85
85
  }
86
86
 
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
87
  type NetworkSettings = {
109
88
  name: string;
110
89
  rpc: string;
@@ -159,10 +138,6 @@ declare class PolkadotJS extends Wallet {
159
138
  private _getLabeledInput;
160
139
  }
161
140
 
162
- /**
163
- * Pre-built Backpack wallet configuration.
164
- */
165
- declare const backpack: WalletConfig<"backpack", Backpack>;
166
141
  /**
167
142
  * Pre-built MetaMask wallet configuration.
168
143
  */
@@ -172,4 +147,17 @@ declare const metamask: WalletConfig<"metamask", Metamask>;
172
147
  */
173
148
  declare const polkadotJS: WalletConfig<"polkadotJS", PolkadotJS>;
174
149
 
175
- export { Backpack, type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type WalletConfig, backpack, createWallet, metamask, polkadotJS, withWallets };
150
+ /**
151
+ * Configuration for w3wallets library.
152
+ * Values can be overridden via environment variables.
153
+ */
154
+ declare const config: {
155
+ /**
156
+ * Timeout for actions like click, fill, waitFor, goto.
157
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
158
+ * @default 30000 (30 seconds)
159
+ */
160
+ readonly actionTimeout: number | undefined;
161
+ };
162
+
163
+ export { type IWallet, Metamask, type Network, type NetworkSettings, PolkadotJS, type WalletConfig, config, createWallet, metamask, polkadotJS, withWallets };
package/dist/index.js CHANGED
@@ -30,10 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Backpack: () => Backpack,
34
33
  Metamask: () => Metamask,
35
34
  PolkadotJS: () => PolkadotJS,
36
- backpack: () => backpack,
35
+ config: () => config,
37
36
  createWallet: () => createWallet,
38
37
  metamask: () => metamask,
39
38
  polkadotJS: () => polkadotJS,
@@ -78,7 +77,6 @@ function withWallets(test, ...wallets) {
78
77
  while (context.serviceWorkers().length < extensionPaths.length) {
79
78
  await sleep(1e3);
80
79
  }
81
- await Promise.all(context.pages().map((page) => page.close()));
82
80
  await use(context);
83
81
  await context.close();
84
82
  }
@@ -142,119 +140,47 @@ async function initializeExtension(context, ExtensionClass, expectedExtensionId,
142
140
  }
143
141
  const page = await context.newPage();
144
142
  const extension = new ExtensionClass(page, expectedExtensionId);
145
- await extension.gotoOnboardPage();
146
143
  return extension;
147
144
  }
148
145
 
149
146
  // src/core/types.ts
150
- function createWallet(config) {
151
- return config;
147
+ function createWallet(config2) {
148
+ return config2;
152
149
  }
153
150
 
154
- // src/wallets/backpack/backpack.ts
151
+ // src/wallets/metamask/metamask.ts
155
152
  var import_test2 = require("@playwright/test");
156
153
 
154
+ // src/config.ts
155
+ var config = {
156
+ /**
157
+ * Timeout for actions like click, fill, waitFor, goto.
158
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
159
+ * @default 30000 (30 seconds)
160
+ */
161
+ get actionTimeout() {
162
+ const value = process.env.W3WALLETS_ACTION_TIMEOUT;
163
+ return value ? parseInt(value, 10) : void 0;
164
+ }
165
+ };
166
+
157
167
  // src/core/wallet.ts
158
168
  var Wallet = class {
159
169
  constructor(page, extensionId) {
160
170
  this.page = page;
161
171
  this.extensionId = extensionId;
162
- }
163
- };
164
-
165
- // src/wallets/backpack/backpack.ts
166
- var Backpack = class extends Wallet {
167
- defaultPassword = "11111111";
168
- currentAccountId = 0;
169
- maxAccountId = 0;
170
- async gotoOnboardPage() {
171
- await this.page.goto(
172
- `chrome-extension://${this.extensionId}/onboarding.html`
173
- );
174
- await (0, import_test2.expect)(this.page.getByText("Welcome to Backpack")).toBeVisible();
175
- }
176
- async onboard(network, privateKey) {
177
- this.currentAccountId++;
178
- this.maxAccountId++;
179
- return this._importAccount(network, privateKey, true);
180
- }
181
- async addAccount(network, privateKey) {
182
- this.maxAccountId++;
183
- this.currentAccountId = this.maxAccountId;
184
- await this.page.goto(
185
- `chrome-extension://${this.extensionId}/onboarding.html?add-user-account=true`
186
- );
187
- await this._importAccount(network, privateKey, false);
188
- }
189
- /**
190
- * Switch account
191
- * @param id The first added account has id 1, the second – 2, and so on
192
- */
193
- async switchAccount(id) {
194
- await this.page.getByRole("button", { name: `A${this.currentAccountId}` }).click();
195
- await this.page.getByRole("button", { name: `Account ${id}` }).click();
196
- this.currentAccountId = id;
197
- }
198
- async unlock() {
199
- await this.page.getByPlaceholder("Password").fill(this.defaultPassword);
200
- await this.page.getByRole("button", { name: "Unlock" }).click();
201
- }
202
- async setRPC(network, rpc) {
203
- await this._clickOnAccount();
204
- await this.page.getByRole("button", { name: "Settings" }).click();
205
- await this.page.getByRole("button", { name: network }).click();
206
- await this.page.getByRole("button", { name: "RPC Connection" }).click();
207
- await this.page.getByRole("button", { name: "Custom" }).click();
208
- await this.page.getByPlaceholder("RPC URL").fill(rpc);
209
- await this.page.keyboard.press("Enter");
210
- }
211
- async ignoreAndProceed() {
212
- const ignoreButton = this.page.getByText("Ignore and proceed anyway.");
213
- await ignoreButton.click();
214
- }
215
- async approve() {
216
- await this.page.getByText("Approve", { exact: true }).click();
217
- }
218
- async deny() {
219
- await this.page.getByText("Deny", { exact: true }).click();
220
- }
221
- async _clickOnAccount() {
222
- return this.page.getByRole("button", { name: `A${this.currentAccountId}`, exact: true }).click();
223
- }
224
- async _importAccount(network, privateKey, isOnboard) {
225
- {
226
- await this.page.waitForTimeout(2e3);
227
- if (await this.page.getByText("You're all good!").isVisible()) {
228
- await this.page.getByLabel("Go back").click();
229
- }
172
+ if (config.actionTimeout) {
173
+ page.setDefaultTimeout(config.actionTimeout);
230
174
  }
231
- await this.page.getByTestId("terms-of-service-checkbox").click();
232
- await this.page.getByText("I already have a wallet").click();
233
- await this.page.getByText(network).click();
234
- await this.page.getByText("Private key").click();
235
- await this.page.getByPlaceholder("Private key").fill(privateKey);
236
- await this.page.waitForTimeout(1e3);
237
- await this.page.getByText("Import", { exact: true }).click();
238
- if (isOnboard) {
239
- await this.page.getByRole("textbox").nth(0).fill(this.defaultPassword);
240
- await this.page.getByRole("textbox").nth(1).fill(this.defaultPassword);
241
- await this.page.getByText("Next", { exact: true }).click();
242
- await (0, import_test2.expect)(this.page.getByText("You're all good!")).toBeVisible();
243
- }
244
- await this.page.goto(`chrome-extension://${this.extensionId}/popup.html`);
245
- await this.page.getByTestId("__CAROUSEL_ITEM_0__").waitFor({ state: "visible" });
246
175
  }
247
176
  };
248
177
 
249
178
  // src/wallets/metamask/metamask.ts
250
- var import_test3 = require("@playwright/test");
251
179
  var Metamask = class extends Wallet {
252
180
  defaultPassword = "TestPassword123!";
253
181
  async gotoOnboardPage() {
254
- await this.page.goto(
255
- `chrome-extension://${this.extensionId}/home.html#onboarding/welcome`
256
- );
257
- await (0, import_test3.expect)(
182
+ await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
183
+ await (0, import_test2.expect)(
258
184
  this.page.getByRole("button", { name: "I have an existing wallet" })
259
185
  ).toBeVisible();
260
186
  }
@@ -265,11 +191,16 @@ var Metamask = class extends Wallet {
265
191
  */
266
192
  async onboard(mnemonic, password) {
267
193
  const pwd = password ?? this.defaultPassword;
194
+ await this.gotoOnboardPage();
268
195
  await this.page.getByRole("button", { name: "I have an existing wallet" }).click();
269
196
  await this.page.getByRole("button", { name: "Import using Secret Recovery Phrase" }).click();
270
197
  const textbox = this.page.getByRole("textbox");
271
198
  await textbox.click();
272
- await this.page.keyboard.type(mnemonic, { delay: 5 });
199
+ for (const word of mnemonic.split(" ")) {
200
+ await this.page.keyboard.type(word);
201
+ await this.page.keyboard.type(" ");
202
+ await this.page.waitForTimeout(30);
203
+ }
273
204
  const continueBtn = this.page.getByTestId("import-srp-confirm");
274
205
  await continueBtn.click();
275
206
  const passwordInputs = this.page.locator('input[type="password"]');
@@ -278,21 +209,17 @@ var Metamask = class extends Wallet {
278
209
  await this.page.getByRole("checkbox").click();
279
210
  await this.page.getByRole("button", { name: "Create password" }).click();
280
211
  const metametricsBtn = this.page.getByTestId("metametrics-i-agree");
281
- await metametricsBtn.waitFor({ state: "visible", timeout: 3e4 });
282
212
  await metametricsBtn.click();
283
213
  const openWalletBtn = this.page.getByRole("button", {
284
214
  name: /open wallet/i
285
215
  });
286
- await openWalletBtn.waitFor({ state: "visible", timeout: 3e4 });
287
216
  await openWalletBtn.click();
288
217
  await this.page.goto(
289
218
  `chrome-extension://${this.extensionId}/sidepanel.html`
290
219
  );
291
- await this.page.getByTestId("account-options-menu-button").waitFor({ state: "visible", timeout: 3e4 });
292
220
  }
293
221
  async approve() {
294
222
  await this.page.getByTestId("confirm-btn").or(this.page.getByTestId("confirm-footer-button")).or(this.page.getByTestId("page-container-footer-next")).or(this.page.getByRole("button", { name: /confirm/i })).click();
295
- await this.page.getByTestId("account-options-menu-button").waitFor({ state: "visible", timeout: 3e4 });
296
223
  }
297
224
  async deny() {
298
225
  const cancelBtn = this.page.getByTestId("cancel-btn").or(this.page.getByTestId("confirm-footer-cancel-button")).or(this.page.getByTestId("page-container-footer-cancel")).or(this.page.getByRole("button", { name: /cancel|reject/i }));
@@ -324,7 +251,7 @@ var Metamask = class extends Wallet {
324
251
  await this.page.getByRole("tab", { name: "Custom" }).click();
325
252
  }
326
253
  await this.page.getByText(networkName).click();
327
- await (0, import_test3.expect)(this.page.getByTestId("sort-by-networks")).toHaveText(
254
+ await (0, import_test2.expect)(this.page.getByTestId("sort-by-networks")).toHaveText(
328
255
  networkName
329
256
  );
330
257
  }
@@ -373,23 +300,24 @@ var Metamask = class extends Wallet {
373
300
  await this.page.getByRole("button", { name: "Back" }).click();
374
301
  }
375
302
  async accountNameIs(accountName) {
376
- await (0, import_test3.expect)(this.page.getByTestId("account-menu-icon")).toContainText(
303
+ await (0, import_test2.expect)(this.page.getByTestId("account-menu-icon")).toContainText(
377
304
  accountName
378
305
  );
379
306
  }
380
307
  };
381
308
 
382
309
  // src/wallets/polkadot-js/polkadot-js.ts
383
- var import_test4 = require("@playwright/test");
310
+ var import_test3 = require("@playwright/test");
384
311
  var PolkadotJS = class extends Wallet {
385
312
  defaultPassword = "11111111";
386
313
  async gotoOnboardPage() {
387
314
  await this.page.goto(`chrome-extension://${this.extensionId}/index.html`);
388
- await (0, import_test4.expect)(
315
+ await (0, import_test3.expect)(
389
316
  this.page.getByText("Before we start, just a couple of notes")
390
317
  ).toBeVisible();
391
318
  }
392
319
  async onboard(seed, password, name) {
320
+ await this.gotoOnboardPage();
393
321
  await this.page.getByRole("button", { name: "Understood, let me continue" }).click();
394
322
  await this.page.getByRole("button", { name: "I Understand" }).click();
395
323
  await this.page.locator(".popupToggle").first().click();
@@ -411,7 +339,8 @@ var PolkadotJS = class extends Wallet {
411
339
  await this.page.getByText("Select all").click();
412
340
  }
413
341
  async selectAccount(accountId) {
414
- await this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").locator("span").check();
342
+ const cb = this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").locator("span");
343
+ await cb.check().catch(() => cb.check());
415
344
  }
416
345
  async enterPassword(password) {
417
346
  await this._getLabeledInput("Password for this account").fill(
@@ -438,11 +367,6 @@ var PolkadotJS = class extends Wallet {
438
367
  };
439
368
 
440
369
  // src/wallets/index.ts
441
- var backpack = createWallet({
442
- name: "backpack",
443
- extensionDir: "backpack",
444
- WalletClass: Backpack
445
- });
446
370
  var metamask = createWallet({
447
371
  name: "metamask",
448
372
  extensionDir: "metamask",
@@ -455,10 +379,9 @@ var polkadotJS = createWallet({
455
379
  });
456
380
  // Annotate the CommonJS export names for ESM import in node:
457
381
  0 && (module.exports = {
458
- Backpack,
459
382
  Metamask,
460
383
  PolkadotJS,
461
- backpack,
384
+ config,
462
385
  createWallet,
463
386
  metamask,
464
387
  polkadotJS,
package/dist/index.mjs CHANGED
@@ -37,7 +37,6 @@ function withWallets(test, ...wallets) {
37
37
  while (context.serviceWorkers().length < extensionPaths.length) {
38
38
  await sleep(1e3);
39
39
  }
40
- await Promise.all(context.pages().map((page) => page.close()));
41
40
  await use(context);
42
41
  await context.close();
43
42
  }
@@ -101,119 +100,47 @@ async function initializeExtension(context, ExtensionClass, expectedExtensionId,
101
100
  }
102
101
  const page = await context.newPage();
103
102
  const extension = new ExtensionClass(page, expectedExtensionId);
104
- await extension.gotoOnboardPage();
105
103
  return extension;
106
104
  }
107
105
 
108
106
  // src/core/types.ts
109
- function createWallet(config) {
110
- return config;
107
+ function createWallet(config2) {
108
+ return config2;
111
109
  }
112
110
 
113
- // src/wallets/backpack/backpack.ts
111
+ // src/wallets/metamask/metamask.ts
114
112
  import { expect } from "@playwright/test";
115
113
 
114
+ // src/config.ts
115
+ var config = {
116
+ /**
117
+ * Timeout for actions like click, fill, waitFor, goto.
118
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
119
+ * @default 30000 (30 seconds)
120
+ */
121
+ get actionTimeout() {
122
+ const value = process.env.W3WALLETS_ACTION_TIMEOUT;
123
+ return value ? parseInt(value, 10) : void 0;
124
+ }
125
+ };
126
+
116
127
  // src/core/wallet.ts
117
128
  var Wallet = class {
118
129
  constructor(page, extensionId) {
119
130
  this.page = page;
120
131
  this.extensionId = extensionId;
121
- }
122
- };
123
-
124
- // src/wallets/backpack/backpack.ts
125
- var Backpack = class extends Wallet {
126
- defaultPassword = "11111111";
127
- currentAccountId = 0;
128
- maxAccountId = 0;
129
- async gotoOnboardPage() {
130
- await this.page.goto(
131
- `chrome-extension://${this.extensionId}/onboarding.html`
132
- );
133
- await expect(this.page.getByText("Welcome to Backpack")).toBeVisible();
134
- }
135
- async onboard(network, privateKey) {
136
- this.currentAccountId++;
137
- this.maxAccountId++;
138
- return this._importAccount(network, privateKey, true);
139
- }
140
- async addAccount(network, privateKey) {
141
- this.maxAccountId++;
142
- this.currentAccountId = this.maxAccountId;
143
- await this.page.goto(
144
- `chrome-extension://${this.extensionId}/onboarding.html?add-user-account=true`
145
- );
146
- await this._importAccount(network, privateKey, false);
147
- }
148
- /**
149
- * Switch account
150
- * @param id The first added account has id 1, the second – 2, and so on
151
- */
152
- async switchAccount(id) {
153
- await this.page.getByRole("button", { name: `A${this.currentAccountId}` }).click();
154
- await this.page.getByRole("button", { name: `Account ${id}` }).click();
155
- this.currentAccountId = id;
156
- }
157
- async unlock() {
158
- await this.page.getByPlaceholder("Password").fill(this.defaultPassword);
159
- await this.page.getByRole("button", { name: "Unlock" }).click();
160
- }
161
- async setRPC(network, rpc) {
162
- await this._clickOnAccount();
163
- await this.page.getByRole("button", { name: "Settings" }).click();
164
- await this.page.getByRole("button", { name: network }).click();
165
- await this.page.getByRole("button", { name: "RPC Connection" }).click();
166
- await this.page.getByRole("button", { name: "Custom" }).click();
167
- await this.page.getByPlaceholder("RPC URL").fill(rpc);
168
- await this.page.keyboard.press("Enter");
169
- }
170
- async ignoreAndProceed() {
171
- const ignoreButton = this.page.getByText("Ignore and proceed anyway.");
172
- await ignoreButton.click();
173
- }
174
- async approve() {
175
- await this.page.getByText("Approve", { exact: true }).click();
176
- }
177
- async deny() {
178
- await this.page.getByText("Deny", { exact: true }).click();
179
- }
180
- async _clickOnAccount() {
181
- return this.page.getByRole("button", { name: `A${this.currentAccountId}`, exact: true }).click();
182
- }
183
- async _importAccount(network, privateKey, isOnboard) {
184
- {
185
- await this.page.waitForTimeout(2e3);
186
- if (await this.page.getByText("You're all good!").isVisible()) {
187
- await this.page.getByLabel("Go back").click();
188
- }
132
+ if (config.actionTimeout) {
133
+ page.setDefaultTimeout(config.actionTimeout);
189
134
  }
190
- await this.page.getByTestId("terms-of-service-checkbox").click();
191
- await this.page.getByText("I already have a wallet").click();
192
- await this.page.getByText(network).click();
193
- await this.page.getByText("Private key").click();
194
- await this.page.getByPlaceholder("Private key").fill(privateKey);
195
- await this.page.waitForTimeout(1e3);
196
- await this.page.getByText("Import", { exact: true }).click();
197
- if (isOnboard) {
198
- await this.page.getByRole("textbox").nth(0).fill(this.defaultPassword);
199
- await this.page.getByRole("textbox").nth(1).fill(this.defaultPassword);
200
- await this.page.getByText("Next", { exact: true }).click();
201
- await expect(this.page.getByText("You're all good!")).toBeVisible();
202
- }
203
- await this.page.goto(`chrome-extension://${this.extensionId}/popup.html`);
204
- await this.page.getByTestId("__CAROUSEL_ITEM_0__").waitFor({ state: "visible" });
205
135
  }
206
136
  };
207
137
 
208
138
  // src/wallets/metamask/metamask.ts
209
- import { expect as expect2 } from "@playwright/test";
210
139
  var Metamask = class extends Wallet {
211
140
  defaultPassword = "TestPassword123!";
212
141
  async gotoOnboardPage() {
213
- await this.page.goto(
214
- `chrome-extension://${this.extensionId}/home.html#onboarding/welcome`
215
- );
216
- await expect2(
142
+ await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
143
+ await expect(
217
144
  this.page.getByRole("button", { name: "I have an existing wallet" })
218
145
  ).toBeVisible();
219
146
  }
@@ -224,11 +151,16 @@ var Metamask = class extends Wallet {
224
151
  */
225
152
  async onboard(mnemonic, password) {
226
153
  const pwd = password ?? this.defaultPassword;
154
+ await this.gotoOnboardPage();
227
155
  await this.page.getByRole("button", { name: "I have an existing wallet" }).click();
228
156
  await this.page.getByRole("button", { name: "Import using Secret Recovery Phrase" }).click();
229
157
  const textbox = this.page.getByRole("textbox");
230
158
  await textbox.click();
231
- await this.page.keyboard.type(mnemonic, { delay: 5 });
159
+ for (const word of mnemonic.split(" ")) {
160
+ await this.page.keyboard.type(word);
161
+ await this.page.keyboard.type(" ");
162
+ await this.page.waitForTimeout(30);
163
+ }
232
164
  const continueBtn = this.page.getByTestId("import-srp-confirm");
233
165
  await continueBtn.click();
234
166
  const passwordInputs = this.page.locator('input[type="password"]');
@@ -237,21 +169,17 @@ var Metamask = class extends Wallet {
237
169
  await this.page.getByRole("checkbox").click();
238
170
  await this.page.getByRole("button", { name: "Create password" }).click();
239
171
  const metametricsBtn = this.page.getByTestId("metametrics-i-agree");
240
- await metametricsBtn.waitFor({ state: "visible", timeout: 3e4 });
241
172
  await metametricsBtn.click();
242
173
  const openWalletBtn = this.page.getByRole("button", {
243
174
  name: /open wallet/i
244
175
  });
245
- await openWalletBtn.waitFor({ state: "visible", timeout: 3e4 });
246
176
  await openWalletBtn.click();
247
177
  await this.page.goto(
248
178
  `chrome-extension://${this.extensionId}/sidepanel.html`
249
179
  );
250
- await this.page.getByTestId("account-options-menu-button").waitFor({ state: "visible", timeout: 3e4 });
251
180
  }
252
181
  async approve() {
253
182
  await this.page.getByTestId("confirm-btn").or(this.page.getByTestId("confirm-footer-button")).or(this.page.getByTestId("page-container-footer-next")).or(this.page.getByRole("button", { name: /confirm/i })).click();
254
- await this.page.getByTestId("account-options-menu-button").waitFor({ state: "visible", timeout: 3e4 });
255
183
  }
256
184
  async deny() {
257
185
  const cancelBtn = this.page.getByTestId("cancel-btn").or(this.page.getByTestId("confirm-footer-cancel-button")).or(this.page.getByTestId("page-container-footer-cancel")).or(this.page.getByRole("button", { name: /cancel|reject/i }));
@@ -283,7 +211,7 @@ var Metamask = class extends Wallet {
283
211
  await this.page.getByRole("tab", { name: "Custom" }).click();
284
212
  }
285
213
  await this.page.getByText(networkName).click();
286
- await expect2(this.page.getByTestId("sort-by-networks")).toHaveText(
214
+ await expect(this.page.getByTestId("sort-by-networks")).toHaveText(
287
215
  networkName
288
216
  );
289
217
  }
@@ -332,23 +260,24 @@ var Metamask = class extends Wallet {
332
260
  await this.page.getByRole("button", { name: "Back" }).click();
333
261
  }
334
262
  async accountNameIs(accountName) {
335
- await expect2(this.page.getByTestId("account-menu-icon")).toContainText(
263
+ await expect(this.page.getByTestId("account-menu-icon")).toContainText(
336
264
  accountName
337
265
  );
338
266
  }
339
267
  };
340
268
 
341
269
  // src/wallets/polkadot-js/polkadot-js.ts
342
- import { expect as expect3 } from "@playwright/test";
270
+ import { expect as expect2 } from "@playwright/test";
343
271
  var PolkadotJS = class extends Wallet {
344
272
  defaultPassword = "11111111";
345
273
  async gotoOnboardPage() {
346
274
  await this.page.goto(`chrome-extension://${this.extensionId}/index.html`);
347
- await expect3(
275
+ await expect2(
348
276
  this.page.getByText("Before we start, just a couple of notes")
349
277
  ).toBeVisible();
350
278
  }
351
279
  async onboard(seed, password, name) {
280
+ await this.gotoOnboardPage();
352
281
  await this.page.getByRole("button", { name: "Understood, let me continue" }).click();
353
282
  await this.page.getByRole("button", { name: "I Understand" }).click();
354
283
  await this.page.locator(".popupToggle").first().click();
@@ -370,7 +299,8 @@ var PolkadotJS = class extends Wallet {
370
299
  await this.page.getByText("Select all").click();
371
300
  }
372
301
  async selectAccount(accountId) {
373
- await this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").locator("span").check();
302
+ const cb = this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").locator("span");
303
+ await cb.check().catch(() => cb.check());
374
304
  }
375
305
  async enterPassword(password) {
376
306
  await this._getLabeledInput("Password for this account").fill(
@@ -397,11 +327,6 @@ var PolkadotJS = class extends Wallet {
397
327
  };
398
328
 
399
329
  // src/wallets/index.ts
400
- var backpack = createWallet({
401
- name: "backpack",
402
- extensionDir: "backpack",
403
- WalletClass: Backpack
404
- });
405
330
  var metamask = createWallet({
406
331
  name: "metamask",
407
332
  extensionDir: "metamask",
@@ -413,10 +338,9 @@ var polkadotJS = createWallet({
413
338
  WalletClass: PolkadotJS
414
339
  });
415
340
  export {
416
- Backpack,
417
341
  Metamask,
418
342
  PolkadotJS,
419
- backpack,
343
+ config,
420
344
  createWallet,
421
345
  metamask,
422
346
  polkadotJS,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "w3wallets",
3
3
  "description": "browser wallets for playwright",
4
- "version": "1.0.0-beta.1",
4
+ "version": "1.0.0-beta.3",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "homepage": "https://github.com/Maksandre/w3wallets",
@@ -16,12 +16,10 @@
16
16
  "keywords": [
17
17
  "e2e",
18
18
  "playwright",
19
- "backpack",
20
19
  "metamask",
21
20
  "testing",
22
21
  "ethereum",
23
- "polkadot",
24
- "eclipse"
22
+ "polkadot"
25
23
  ],
26
24
  "license": "MIT",
27
25
  "publishConfig": {
@@ -32,8 +30,7 @@
32
30
  ],
33
31
  "bin": "./src/scripts/download.js",
34
32
  "scripts": {
35
- "start:dapp": "yarn workspace @w3wallets/test-app start:all",
36
- "download-wallets": "npx w3wallets backpack polkadotJS metamask",
33
+ "download-wallets": "npx w3wallets pjs mm",
37
34
  "test": "npx playwright test --project=local --workers=2",
38
35
  "test:ci": "npx playwright test --project=ci",
39
36
  "build": "tsup",
@@ -49,7 +46,7 @@
49
46
  "devDependencies": {
50
47
  "@arethetypeswrong/cli": "^0.17.2",
51
48
  "@changesets/cli": "^2.27.11",
52
- "@playwright/test": "^1.50.1",
49
+ "@playwright/test": "1.57.0",
53
50
  "@types/node": "^22.10.5",
54
51
  "dotenv": "^16.4.7",
55
52
  "prettier": "^3.4.2",
@@ -58,6 +55,6 @@
58
55
  "typescript": "^5.7.2"
59
56
  },
60
57
  "peerDependencies": {
61
- "@playwright/test": "^1.50.1"
58
+ "@playwright/test": "^1.57.0"
62
59
  }
63
60
  }
@@ -4,8 +4,8 @@
4
4
  * Downloads and extracts Chrome extensions from the Chrome Web Store.
5
5
  *
6
6
  * Usage:
7
- * npx w3wallets metamask backpack # Download by alias
8
- * npx w3wallets mm bp pjs # Short aliases
7
+ * npx w3wallets metamask polkadotjs # Download by alias
8
+ * npx w3wallets mm pjs # Short aliases
9
9
  * npx w3wallets <extension-id> # Download by extension ID
10
10
  * npx w3wallets --help # Show help
11
11
  */
@@ -19,10 +19,6 @@ const zlib = require("zlib");
19
19
  // 1. Known aliases -> extension IDs (case-insensitive lookup)
20
20
  // ---------------------------------------------------------------------
21
21
  const EXTENSION_REGISTRY = {
22
- // Backpack wallet
23
- backpack: "aflkmfhebedbjioipglgcbcmnbpgliof",
24
- bp: "aflkmfhebedbjioipglgcbcmnbpgliof",
25
-
26
22
  // MetaMask wallet
27
23
  metamask: "nkbihfbeogaeaoehlefnkodbefgpgknn",
28
24
  mm: "nkbihfbeogaeaoehlefnkodbefgpgknn",
@@ -34,14 +30,12 @@ const EXTENSION_REGISTRY = {
34
30
 
35
31
  // Human-readable names for display
36
32
  const EXTENSION_NAMES = {
37
- aflkmfhebedbjioipglgcbcmnbpgliof: "Backpack",
38
33
  nkbihfbeogaeaoehlefnkodbefgpgknn: "MetaMask",
39
34
  mopnmbcafieddcagagdcbnhejhlodfdd: "Polkadot.js",
40
35
  };
41
36
 
42
37
  // Canonical aliases for listing
43
38
  const CANONICAL_ALIASES = [
44
- { name: "backpack", short: "bp", id: "aflkmfhebedbjioipglgcbcmnbpgliof" },
45
39
  { name: "metamask", short: "mm", id: "nkbihfbeogaeaoehlefnkodbefgpgknn" },
46
40
  { name: "polkadotjs", short: "pjs", id: "mopnmbcafieddcagagdcbnhejhlodfdd" },
47
41
  ];
@@ -95,8 +89,8 @@ USAGE:
95
89
  npx w3wallets [OPTIONS] <targets...>
96
90
 
97
91
  TARGETS:
98
- Alias name Known wallet alias (e.g., metamask, backpack)
99
- Short alias Short form (e.g., mm, bp, pjs)
92
+ Alias name Known wallet alias (e.g., metamask, polkadotjs)
93
+ Short alias Short form (e.g., mm, pjs)
100
94
  Extension ID 32-character Chrome extension ID
101
95
  URL Chrome Web Store URL
102
96
 
@@ -109,7 +103,7 @@ OPTIONS:
109
103
 
110
104
  EXAMPLES:
111
105
  npx w3wallets metamask # Download MetaMask
112
- npx w3wallets mm bp # Download using short aliases
106
+ npx w3wallets mm pjs # Download using short aliases
113
107
  npx w3wallets --list # List available aliases
114
108
  npx w3wallets -o ./extensions metamask # Custom output directory
115
109
  npx w3wallets --force mm # Force re-download