w3wallets 0.10.2 → 1.0.0-beta.2

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/dist/index.mjs CHANGED
@@ -1,19 +1,117 @@
1
1
  // src/withWallets.ts
2
2
  import path from "path";
3
3
  import fs from "fs";
4
-
5
- // tests/utils/sleep.ts
6
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
7
-
8
- // src/withWallets.ts
4
+ import crypto from "crypto";
9
5
  import {
10
6
  chromium
11
7
  } from "@playwright/test";
8
+ var W3WALLETS_DIR = ".w3wallets";
9
+ function sleep(ms) {
10
+ return new Promise((resolve) => setTimeout(resolve, ms));
11
+ }
12
+ function withWallets(test, ...wallets) {
13
+ const extensionInfo = wallets.map((w) => {
14
+ const extPath = path.join(process.cwd(), W3WALLETS_DIR, w.extensionDir);
15
+ ensureWalletExtensionExists(extPath, w.name);
16
+ const extensionId = w.extensionId ?? getExtensionId(extPath);
17
+ return { path: extPath, id: extensionId, name: w.name };
18
+ });
19
+ const extensionPaths = extensionInfo.map((e) => e.path);
20
+ const fixtures = {
21
+ context: async ({}, use, testInfo) => {
22
+ const userDataDir = path.join(
23
+ process.cwd(),
24
+ W3WALLETS_DIR,
25
+ ".context",
26
+ testInfo.testId
27
+ );
28
+ cleanUserDataDir(userDataDir);
29
+ const context = await chromium.launchPersistentContext(userDataDir, {
30
+ headless: testInfo.project.use.headless ?? true,
31
+ channel: "chromium",
32
+ args: [
33
+ `--disable-extensions-except=${extensionPaths.join(",")}`,
34
+ `--load-extension=${extensionPaths.join(",")}`
35
+ ]
36
+ });
37
+ while (context.serviceWorkers().length < extensionPaths.length) {
38
+ await sleep(1e3);
39
+ }
40
+ await use(context);
41
+ await context.close();
42
+ }
43
+ };
44
+ for (let i = 0; i < wallets.length; i++) {
45
+ const wallet = wallets[i];
46
+ const info = extensionInfo[i];
47
+ fixtures[wallet.name] = async ({ context }, use) => {
48
+ const instance = await initializeExtension(
49
+ context,
50
+ wallet.WalletClass,
51
+ info.id,
52
+ wallet.name
53
+ );
54
+ await use(instance);
55
+ };
56
+ }
57
+ return test.extend(fixtures);
58
+ }
59
+ function cleanUserDataDir(userDataDir) {
60
+ if (fs.existsSync(userDataDir)) {
61
+ fs.rmSync(userDataDir, { recursive: true });
62
+ }
63
+ }
64
+ function ensureWalletExtensionExists(walletPath, walletName) {
65
+ if (!fs.existsSync(path.join(walletPath, "manifest.json"))) {
66
+ const cliAlias = walletName.toLowerCase();
67
+ throw new Error(
68
+ `Cannot find ${walletName}. Please download it via 'npx w3wallets ${cliAlias}'.`
69
+ );
70
+ }
71
+ }
72
+ function getExtensionId(extensionPath) {
73
+ const absolutePath = path.resolve(extensionPath);
74
+ const manifestPath = path.join(absolutePath, "manifest.json");
75
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
76
+ let dataToHash;
77
+ if (manifest.key) {
78
+ dataToHash = Buffer.from(manifest.key, "base64");
79
+ } else {
80
+ dataToHash = Buffer.from(absolutePath);
81
+ }
82
+ const hash = crypto.createHash("sha256").update(dataToHash).digest();
83
+ const ALPHABET = "abcdefghijklmnop";
84
+ let extensionId = "";
85
+ for (let i = 0; i < 16; i++) {
86
+ const byte = hash[i];
87
+ extensionId += ALPHABET[byte >> 4 & 15];
88
+ extensionId += ALPHABET[byte & 15];
89
+ }
90
+ return extensionId;
91
+ }
92
+ async function initializeExtension(context, ExtensionClass, expectedExtensionId, walletName) {
93
+ const expectedUrl = `chrome-extension://${expectedExtensionId}/`;
94
+ const worker = context.serviceWorkers().find((w) => w.url().startsWith(expectedUrl));
95
+ if (!worker) {
96
+ const availableIds = context.serviceWorkers().map((w) => w.url().split("/")[2]).filter(Boolean);
97
+ throw new Error(
98
+ `Service worker for ${walletName} (ID: ${expectedExtensionId}) not found. Available extension IDs: [${availableIds.join(", ")}]`
99
+ );
100
+ }
101
+ const page = await context.newPage();
102
+ const extension = new ExtensionClass(page, expectedExtensionId);
103
+ return extension;
104
+ }
105
+
106
+ // src/core/types.ts
107
+ function createWallet(config2) {
108
+ return config2;
109
+ }
12
110
 
13
- // src/backpack/backpack.ts
111
+ // src/wallets/metamask/metamask.ts
14
112
  import { expect } from "@playwright/test";
15
113
 
16
- // src/wallet.ts
114
+ // src/core/wallet.ts
17
115
  var Wallet = class {
18
116
  constructor(page, extensionId) {
19
117
  this.page = page;
@@ -21,91 +119,162 @@ var Wallet = class {
21
119
  }
22
120
  };
23
121
 
24
- // src/backpack/backpack.ts
25
- var Backpack = class extends Wallet {
26
- defaultPassword = "11111111";
27
- currentAccountId = 0;
28
- maxAccountId = 0;
122
+ // src/config.ts
123
+ var config = {
124
+ /**
125
+ * Timeout for actions like click, fill, waitFor, goto.
126
+ * Set via W3WALLETS_ACTION_TIMEOUT env variable.
127
+ * @default 30000 (30 seconds)
128
+ */
129
+ get actionTimeout() {
130
+ const value = process.env.W3WALLETS_ACTION_TIMEOUT;
131
+ return value ? parseInt(value, 10) : void 0;
132
+ }
133
+ };
134
+
135
+ // src/wallets/metamask/metamask.ts
136
+ var Metamask = class extends Wallet {
137
+ defaultPassword = "TestPassword123!";
29
138
  async gotoOnboardPage() {
139
+ await this.page.goto(`chrome-extension://${this.extensionId}/home.html`, {
140
+ timeout: config.actionTimeout
141
+ });
142
+ await expect(
143
+ this.page.getByRole("button", { name: "I have an existing wallet" })
144
+ ).toBeVisible({ timeout: config.actionTimeout });
145
+ }
146
+ /**
147
+ * Onboard MetaMask with a mnemonic phrase
148
+ * @param mnemonic - 12 or 24 word recovery phrase
149
+ * @param password - Optional password (defaults to TestPassword123!)
150
+ */
151
+ async onboard(mnemonic, password) {
152
+ const pwd = password ?? this.defaultPassword;
153
+ await this.gotoOnboardPage();
154
+ await this.page.getByRole("button", { name: "I have an existing wallet" }).click({ timeout: config.actionTimeout });
155
+ await this.page.getByRole("button", { name: "Import using Secret Recovery Phrase" }).click({ timeout: config.actionTimeout });
156
+ const textbox = this.page.getByRole("textbox");
157
+ await textbox.click({ timeout: config.actionTimeout });
158
+ for (const word of mnemonic.split(" ")) {
159
+ await this.page.keyboard.type(word);
160
+ await this.page.keyboard.type(" ");
161
+ await this.page.waitForTimeout(30);
162
+ }
163
+ const continueBtn = this.page.getByTestId("import-srp-confirm");
164
+ await continueBtn.click({ timeout: config.actionTimeout });
165
+ const passwordInputs = this.page.locator('input[type="password"]');
166
+ await passwordInputs.nth(0).fill(pwd, { timeout: config.actionTimeout });
167
+ await passwordInputs.nth(1).fill(pwd, { timeout: config.actionTimeout });
168
+ await this.page.getByRole("checkbox").click({ timeout: config.actionTimeout });
169
+ await this.page.getByRole("button", { name: "Create password" }).click({ timeout: config.actionTimeout });
170
+ const metametricsBtn = this.page.getByTestId("metametrics-i-agree");
171
+ await metametricsBtn.click({ timeout: config.actionTimeout });
172
+ const openWalletBtn = this.page.getByRole("button", {
173
+ name: /open wallet/i
174
+ });
175
+ await openWalletBtn.click({ timeout: config.actionTimeout });
30
176
  await this.page.goto(
31
- `chrome-extension://${this.extensionId}/onboarding.html`
177
+ `chrome-extension://${this.extensionId}/sidepanel.html`,
178
+ { timeout: config.actionTimeout }
32
179
  );
33
- await expect(this.page.getByText("Welcome to Backpack")).toBeVisible();
180
+ await this._waitWalletStable();
34
181
  }
35
- async onboard(network, privateKey) {
36
- this.currentAccountId++;
37
- this.maxAccountId++;
38
- return this._importAccount(network, privateKey, true);
182
+ async approve() {
183
+ 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({ timeout: config.actionTimeout });
184
+ await this._waitWalletStable();
39
185
  }
40
- async addAccount(network, privateKey) {
41
- this.maxAccountId++;
42
- this.currentAccountId = this.maxAccountId;
43
- await this.page.goto(
44
- `chrome-extension://${this.extensionId}/onboarding.html?add-user-account=true`
45
- );
46
- await this._importAccount(network, privateKey, false);
186
+ async deny() {
187
+ 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 }));
188
+ await cancelBtn.first().click({ timeout: config.actionTimeout });
47
189
  }
48
190
  /**
49
- * Switch account
50
- * @param id The first added account has id 1, the second – 2, and so on
191
+ * Lock the MetaMask wallet
51
192
  */
52
- async switchAccount(id) {
53
- await this.page.getByRole("button", { name: `A${this.currentAccountId}` }).click();
54
- await this.page.getByRole("button", { name: `Account ${id}` }).click();
55
- this.currentAccountId = id;
193
+ async lock() {
194
+ await this.page.getByTestId("account-options-menu-button").click({ timeout: config.actionTimeout });
195
+ await this.page.locator("text=Lock MetaMask").click({ timeout: config.actionTimeout });
56
196
  }
57
- async unlock() {
58
- await this.page.getByPlaceholder("Password").fill(this.defaultPassword);
59
- await this.page.getByRole("button", { name: "Unlock" }).click();
197
+ /**
198
+ * Unlock MetaMask with password
199
+ */
200
+ async unlock(password) {
201
+ const pwd = password ?? this.defaultPassword;
202
+ const passwordInput = this.page.getByTestId("unlock-password");
203
+ await passwordInput.fill(pwd, { timeout: config.actionTimeout });
204
+ await this.page.getByTestId("unlock-submit").click({ timeout: config.actionTimeout });
60
205
  }
61
- async setRPC(network, rpc) {
62
- await this._clickOnAccount();
63
- await this.page.getByRole("button", { name: "Settings" }).click();
64
- await this.page.getByRole("button", { name: network }).click();
65
- await this.page.getByRole("button", { name: "RPC Connection" }).click();
66
- await this.page.getByRole("button", { name: "Custom" }).click();
67
- await this.page.getByPlaceholder("RPC URL").fill(rpc);
68
- await this.page.keyboard.press("Enter");
206
+ /**
207
+ * Switch to an existing network in MetaMask
208
+ * @param networkName - Name of the network to switch to (e.g., "Ethereum Mainnet", "Sepolia")
209
+ */
210
+ async switchNetwork(networkName, networkType = "Popular") {
211
+ await this.page.getByTestId("sort-by-networks").click({ timeout: config.actionTimeout });
212
+ if (networkType === "Custom") {
213
+ await this.page.getByRole("tab", { name: "Custom" }).click({ timeout: config.actionTimeout });
214
+ }
215
+ await this.page.getByText(networkName).click({ timeout: config.actionTimeout });
216
+ await expect(this.page.getByTestId("sort-by-networks")).toHaveText(
217
+ networkName,
218
+ { timeout: config.actionTimeout }
219
+ );
69
220
  }
70
- async ignoreAndProceed() {
71
- const ignoreButton = this.page.getByText("Ignore and proceed anyway.");
72
- await ignoreButton.click();
221
+ async switchAccount(accountName) {
222
+ await this.page.getByTestId("account-menu-icon").click({ timeout: config.actionTimeout });
223
+ await this.page.getByText(accountName, { exact: true }).click({ timeout: config.actionTimeout });
73
224
  }
74
- async approve() {
75
- await this.page.getByText("Approve", { exact: true }).click();
225
+ /**
226
+ * Add a custom network to MetaMask
227
+ */
228
+ async addNetwork(network) {
229
+ await this.page.goto(
230
+ `chrome-extension://${this.extensionId}/home.html#settings/networks/add-network`,
231
+ { timeout: config.actionTimeout }
232
+ );
233
+ await this.page.getByTestId("network-form-network-name").fill(network.name, { timeout: config.actionTimeout });
234
+ await this.page.getByTestId("network-form-rpc-url").fill(network.rpc, { timeout: config.actionTimeout });
235
+ await this.page.getByTestId("network-form-chain-id").fill(network.chainId.toString(), { timeout: config.actionTimeout });
236
+ await this.page.getByTestId("network-form-ticker-input").fill(network.currencySymbol, { timeout: config.actionTimeout });
237
+ await this.page.getByRole("button", { name: /save/i }).click({ timeout: config.actionTimeout });
76
238
  }
77
- async deny() {
78
- await this.page.getByText("Deny", { exact: true }).click();
239
+ async addCustomNetwork(settings) {
240
+ await this.page.getByTestId("account-options-menu-button").click({ timeout: config.actionTimeout });
241
+ await this.page.getByTestId("global-menu-networks").click({ timeout: config.actionTimeout });
242
+ await this.page.getByRole("button", { name: "Add a custom network" }).click({ timeout: config.actionTimeout });
243
+ await this.page.getByTestId("network-form-network-name").fill(settings.name, { timeout: config.actionTimeout });
244
+ await this.page.getByTestId("network-form-chain-id").fill(settings.chainId.toString(), { timeout: config.actionTimeout });
245
+ await this.page.getByTestId("network-form-ticker-input").fill(settings.currencySymbol, { timeout: config.actionTimeout });
246
+ await this.page.getByTestId("test-add-rpc-drop-down").click({ timeout: config.actionTimeout });
247
+ await this.page.getByRole("button", { name: "Add RPC URL" }).click({ timeout: config.actionTimeout });
248
+ await this.page.getByTestId("rpc-url-input-test").fill(settings.rpc, { timeout: config.actionTimeout });
249
+ await this.page.getByRole("button", { name: "Add URL" }).click({ timeout: config.actionTimeout });
250
+ await this.page.getByRole("button", { name: "Save" }).click({ timeout: config.actionTimeout });
79
251
  }
80
- async _clickOnAccount() {
81
- return this.page.getByRole("button", { name: `A${this.currentAccountId}`, exact: true }).click();
252
+ async enableTestNetworks() {
253
+ await this.page.getByTestId("account-options-menu-button").click({ timeout: config.actionTimeout });
254
+ await this.page.getByTestId("global-menu-networks").click({ timeout: config.actionTimeout });
255
+ await this.page.locator("text=Show test networks >> xpath=following-sibling::label").click({ timeout: config.actionTimeout });
256
+ await this.page.keyboard.press("Escape");
82
257
  }
83
- async _importAccount(network, privateKey, isOnboard) {
84
- {
85
- await this.page.waitForTimeout(2e3);
86
- if (await this.page.getByText("You're all good!").isVisible()) {
87
- await this.page.getByLabel("Go back").click();
88
- }
89
- }
90
- await this.page.getByTestId("terms-of-service-checkbox").click();
91
- await this.page.getByText("I already have a wallet").click();
92
- await this.page.getByText(network).click();
93
- await this.page.getByText("Private key").click();
94
- await this.page.getByPlaceholder("Private key").fill(privateKey);
95
- await this.page.waitForTimeout(1e3);
96
- await this.page.getByText("Import", { exact: true }).click();
97
- if (isOnboard) {
98
- await this.page.getByRole("textbox").nth(0).fill(this.defaultPassword);
99
- await this.page.getByRole("textbox").nth(1).fill(this.defaultPassword);
100
- await this.page.getByText("Next", { exact: true }).click();
101
- await expect(this.page.getByText("You're all good!")).toBeVisible();
102
- }
103
- await this.page.goto(`chrome-extension://${this.extensionId}/popup.html`);
104
- await this.page.getByTestId("__CAROUSEL_ITEM_0__").waitFor({ state: "visible" });
258
+ async importAccount(privateKey) {
259
+ await this.page.getByTestId("account-menu-icon").click({ timeout: config.actionTimeout });
260
+ await this.page.getByTestId("account-list-add-wallet-button").click({ timeout: config.actionTimeout });
261
+ await this.page.getByTestId("add-wallet-modal-import-account").click({ timeout: config.actionTimeout });
262
+ await this.page.locator("#private-key-box").fill(privateKey, { timeout: config.actionTimeout });
263
+ await this.page.getByTestId("import-account-confirm-button").click({ timeout: config.actionTimeout });
264
+ await this.page.getByRole("button", { name: "Back" }).click({ timeout: config.actionTimeout });
265
+ }
266
+ async accountNameIs(accountName) {
267
+ await expect(this.page.getByTestId("account-menu-icon")).toContainText(
268
+ accountName,
269
+ { timeout: config.actionTimeout }
270
+ );
271
+ }
272
+ async _waitWalletStable() {
273
+ await this.page.getByTestId("account-options-menu-button").waitFor({ state: "visible", timeout: config.actionTimeout });
105
274
  }
106
275
  };
107
276
 
108
- // src/polkadotJS/polkadotJS.ts
277
+ // src/wallets/polkadot-js/polkadot-js.ts
109
278
  import { expect as expect2 } from "@playwright/test";
110
279
  var PolkadotJS = class extends Wallet {
111
280
  defaultPassword = "11111111";
@@ -116,6 +285,7 @@ var PolkadotJS = class extends Wallet {
116
285
  ).toBeVisible();
117
286
  }
118
287
  async onboard(seed, password, name) {
288
+ await this.gotoOnboardPage();
119
289
  await this.page.getByRole("button", { name: "Understood, let me continue" }).click();
120
290
  await this.page.getByRole("button", { name: "I Understand" }).click();
121
291
  await this.page.locator(".popupToggle").first().click();
@@ -137,7 +307,8 @@ var PolkadotJS = class extends Wallet {
137
307
  await this.page.getByText("Select all").click();
138
308
  }
139
309
  async selectAccount(accountId) {
140
- await this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").locator("span").check();
310
+ const cb = this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").locator("span");
311
+ await cb.check().catch(() => cb.check());
141
312
  }
142
313
  async enterPassword(password) {
143
314
  await this._getLabeledInput("Password for this account").fill(
@@ -163,233 +334,23 @@ var PolkadotJS = class extends Wallet {
163
334
  }
164
335
  };
165
336
 
166
- // src/metamask/metamask.ts
167
- import { expect as expect3 } from "@playwright/test";
168
- var Metamask = class extends Wallet {
169
- defaultPassword = "11111111";
170
- async gotoOnboardPage() {
171
- await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
172
- }
173
- /**
174
- *
175
- * @param mnemonic 12-word mnemonic seed phrase
176
- */
177
- async onboard(mnemonic, password = this.defaultPassword) {
178
- await this.page.getByTestId("onboarding-import-wallet").click();
179
- await this.page.getByTestId("onboarding-import-with-srp-button").click();
180
- await this.page.getByTestId("srp-input-import__srp-note").pressSequentially(mnemonic, { delay: 5 });
181
- await this.page.getByRole("button", { name: "Continue" }).click();
182
- await this.page.getByTestId("create-password-new-input").fill(password);
183
- await this.page.getByTestId("create-password-confirm-input").fill(password);
184
- await this.page.getByTestId("create-password-terms").click();
185
- await this.page.getByTestId("create-password-submit").click();
186
- await this.page.getByTestId("metametrics-i-agree").click();
187
- await this.page.getByTestId("onboarding-complete-done").click();
188
- await this.clickTopRightCornerToCloseAllTheMarketingBullshit();
189
- }
190
- // async switchAccount(accountAddress: { address: string }): Promise<void>;
191
- async switchAccount(accountNameOrAddress) {
192
- await this.page.getByTestId("account-menu-icon").click();
193
- await this.page.getByText(accountNameOrAddress.name, { exact: true }).click();
194
- }
195
- async importAccount(privateKey) {
196
- await this.page.getByTestId("account-menu-icon").click();
197
- await this.page.getByTestId("account-list-add-wallet-button").click();
198
- await this.page.getByTestId("add-wallet-modal-import-account").click();
199
- await this.page.locator("#private-key-box").fill(privateKey);
200
- await this.page.getByTestId("import-account-confirm-button").click();
201
- await this.page.getByRole("button", { name: "Back" }).click();
202
- }
203
- async addAccount(accountName) {
204
- await this.page.getByTestId("account-menu-icon").click();
205
- await this.page.getByTestId("multichain-account-menu-popover-action-button").click();
206
- await this.page.getByTestId("multichain-account-menu-popover-add-account").click();
207
- if (accountName) {
208
- await this.page.locator("#account-name").fill(accountName);
209
- }
210
- await this.page.getByTestId("submit-add-account-with-name").click();
211
- }
212
- async getAccountName() {
213
- const accountSelect = this.page.getByTestId("account-menu-icon");
214
- await expect3(accountSelect).toBeVisible();
215
- const text = await accountSelect.textContent();
216
- if (!text) throw Error("Cannot get account name");
217
- return text;
218
- }
219
- async connectToNetwork(networkName, networkType = "Popular") {
220
- await this.page.getByTestId("sort-by-networks").click();
221
- await this.page.getByRole("tab", { name: networkType, exact: true }).click();
222
- const additionalNetwork = this.page.getByTestId("additional-network-item").getByText(networkName);
223
- await this.page.getByText(networkName).click();
224
- }
225
- async addCustomNetwork(settings) {
226
- await this.page.getByTestId("account-options-menu-button").click();
227
- await this.page.getByTestId("global-menu-networks").click();
228
- await this.page.getByRole("button", { name: "Add a custom network" }).click();
229
- await this.page.getByTestId("network-form-network-name").fill(settings.name);
230
- await this.page.getByTestId("network-form-chain-id").fill(settings.chainId.toString());
231
- await this.page.getByTestId("network-form-ticker-input").fill(settings.currencySymbol);
232
- await this.page.getByTestId("test-add-rpc-drop-down").click();
233
- await this.page.getByRole("button", { name: "Add RPC URL" }).click();
234
- await this.page.getByTestId("rpc-url-input-test").fill(settings.rpc);
235
- await this.page.getByRole("button", { name: "Add URL" }).click();
236
- await this.page.getByRole("button", { name: "Save" }).click();
237
- }
238
- async enableTestNetworks() {
239
- await this.page.getByTestId("account-options-menu-button").click();
240
- await this.page.getByTestId("global-menu-networks").click();
241
- await this.page.locator("text=Show test networks >> xpath=following-sibling::label").click();
242
- await this.page.keyboard.press("Escape");
243
- }
244
- async approve() {
245
- const p = await this.page.context().newPage();
246
- await p.goto(`chrome-extension://${this.extensionId}/notification.html`);
247
- await p.locator(
248
- '[data-testid="confirm-footer-button"], [data-testid="confirm-btn"], [data-testid="page-container-footer-next"], [data-testid="confirmation-submit-button"]'
249
- ).click();
250
- await p.waitForSelector(".multichain-app-header", {
251
- timeout: 1e4
252
- });
253
- await p.close();
254
- }
255
- async deny() {
256
- return this.usingNotificationPage(
257
- (p) => p.getByTestId("cancel-btn").click()
258
- );
259
- }
260
- async usingNotificationPage(action) {
261
- const p = await this.page.context().newPage();
262
- await p.goto(`chrome-extension://${this.extensionId}/notification.html`);
263
- await action(p);
264
- await p.close();
265
- }
266
- async clickTopRightCornerToCloseAllTheMarketingBullshit() {
267
- await this.page.waitForTimeout(500);
268
- await this.page.keyboard.press("Escape");
269
- await this.page.mouse.click(1e3, 10);
270
- }
271
- };
272
-
273
- // src/withWallets.ts
274
- var w3walletsDir = ".w3wallets";
275
- function withWallets(test, ...config) {
276
- const withBackpack = config.includes("backpack");
277
- const withPolkadotJS = config.includes("polkadotJS");
278
- const withMetamask = config.includes("metamask");
279
- const backpackPath = path.join(process.cwd(), w3walletsDir, "backpack");
280
- const polkadotJSPath = path.join(process.cwd(), w3walletsDir, "polkadotJS");
281
- const metamaskPath = path.join(process.cwd(), w3walletsDir, "metamask");
282
- return test.extend({
283
- /**
284
- * Sets up a persistent browser context with the requested extensions loaded.
285
- */
286
- context: async ({}, use, testInfo) => {
287
- const userDataDir = path.join(
288
- process.cwd(),
289
- ".w3wallets",
290
- ".context",
291
- testInfo.testId
292
- );
293
- cleanUserDataDir(userDataDir);
294
- const extensionPaths = [];
295
- if (withBackpack) {
296
- ensureWalletExtensionExists(backpackPath, "backpack");
297
- extensionPaths.push(backpackPath);
298
- }
299
- if (withPolkadotJS) {
300
- ensureWalletExtensionExists(polkadotJSPath, "polkadotJS");
301
- extensionPaths.push(polkadotJSPath);
302
- }
303
- if (withMetamask) {
304
- ensureWalletExtensionExists(metamaskPath, "metamask");
305
- extensionPaths.push(metamaskPath);
306
- }
307
- const context = await chromium.launchPersistentContext(userDataDir, {
308
- headless: testInfo.project.use.headless ?? true,
309
- channel: "chromium",
310
- args: [
311
- `--disable-extensions-except=${extensionPaths.join(",")}`,
312
- `--load-extension=${extensionPaths.join(",")}`
313
- ]
314
- });
315
- while (context.serviceWorkers().length < extensionPaths.length) {
316
- await sleep(1e3);
317
- }
318
- await use(context);
319
- await context.close();
320
- },
321
- backpack: async ({ context }, use) => {
322
- if (!withBackpack) {
323
- throw Error(
324
- "The Backpack wallet hasn't been loaded. Add it to the withWallets function."
325
- );
326
- }
327
- const backpack = await initializeExtension(
328
- context,
329
- Backpack,
330
- "Backpack is not initialized"
331
- );
332
- await use(backpack);
333
- },
334
- polkadotJS: async ({ context }, use) => {
335
- if (!withPolkadotJS) {
336
- throw Error(
337
- "The Polkadot{.js} wallet hasn't been loaded. Add it to the withWallets function."
338
- );
339
- }
340
- const polkadotJS = await initializeExtension(
341
- context,
342
- PolkadotJS,
343
- "Polkadot{.js} is not initialized"
344
- );
345
- await use(polkadotJS);
346
- },
347
- metamask: async ({ context }, use) => {
348
- if (!withMetamask) {
349
- throw Error(
350
- "The Metamask wallet hasn't been loaded. Add it to the withWallets function."
351
- );
352
- }
353
- const metamask = await initializeExtension(
354
- context,
355
- Metamask,
356
- "Metamask is not initialized"
357
- );
358
- await use(metamask);
359
- }
360
- });
361
- }
362
- function cleanUserDataDir(userDataDir) {
363
- if (fs.existsSync(userDataDir)) {
364
- fs.rmSync(userDataDir, { recursive: true });
365
- }
366
- }
367
- function ensureWalletExtensionExists(walletPath, walletName) {
368
- if (!fs.existsSync(path.join(walletPath, "manifest.json"))) {
369
- throw new Error(
370
- `Cannot find ${walletName}. Please download it via 'npx w3wallets ${walletName}'.`
371
- );
372
- }
373
- }
374
- async function initializeExtension(context, ExtensionClass, notInitializedErrorMessage) {
375
- const serviceWorkers = context.serviceWorkers();
376
- let page = await context.newPage();
377
- for (const worker of serviceWorkers) {
378
- const extensionId = worker.url().split("/")[2];
379
- if (!extensionId) {
380
- continue;
381
- }
382
- const extension = new ExtensionClass(page, extensionId);
383
- try {
384
- await extension.gotoOnboardPage();
385
- return extension;
386
- } catch {
387
- await page.close();
388
- page = await context.newPage();
389
- }
390
- }
391
- throw new Error(notInitializedErrorMessage);
392
- }
337
+ // src/wallets/index.ts
338
+ var metamask = createWallet({
339
+ name: "metamask",
340
+ extensionDir: "metamask",
341
+ WalletClass: Metamask
342
+ });
343
+ var polkadotJS = createWallet({
344
+ name: "polkadotJS",
345
+ extensionDir: "polkadotjs",
346
+ WalletClass: PolkadotJS
347
+ });
393
348
  export {
349
+ Metamask,
350
+ PolkadotJS,
351
+ config,
352
+ createWallet,
353
+ metamask,
354
+ polkadotJS,
394
355
  withWallets
395
356
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "w3wallets",
3
3
  "description": "browser wallets for playwright",
4
- "version": "0.10.2",
4
+ "version": "1.0.0-beta.2",
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:ui": "yarn workspace @w3wallets/test-app buildAndStart",
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
  }