w3wallets 1.0.0-beta.1 → 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 +85 -10
- package/dist/index.d.mts +68 -33
- package/dist/index.d.ts +68 -33
- package/dist/index.js +506 -164
- package/dist/index.mjs +515 -168
- package/dist/scripts/cache.js +317 -0
- package/package.json +17 -12
- package/src/scripts/download.js +75 -13
package/dist/index.js
CHANGED
|
@@ -30,29 +30,148 @@ 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
|
-
|
|
35
|
+
config: () => config,
|
|
37
36
|
createWallet: () => createWallet,
|
|
37
|
+
debug: () => debug,
|
|
38
|
+
isCachedConfig: () => isCachedConfig,
|
|
38
39
|
metamask: () => metamask,
|
|
39
40
|
polkadotJS: () => polkadotJS,
|
|
41
|
+
prepareWallet: () => prepareWallet,
|
|
40
42
|
withWallets: () => withWallets
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(index_exports);
|
|
43
45
|
|
|
44
46
|
// src/withWallets.ts
|
|
47
|
+
var import_path3 = __toESM(require("path"));
|
|
48
|
+
var import_fs3 = __toESM(require("fs"));
|
|
49
|
+
var import_test2 = require("@playwright/test");
|
|
50
|
+
|
|
51
|
+
// src/cache/types.ts
|
|
52
|
+
function isCachedConfig(config2) {
|
|
53
|
+
return "__cached" in config2 && config2.__cached === true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/cache/buildCache.ts
|
|
57
|
+
var import_path2 = __toESM(require("path"));
|
|
58
|
+
var import_fs2 = __toESM(require("fs"));
|
|
59
|
+
var import_test = require("@playwright/test");
|
|
60
|
+
|
|
61
|
+
// src/cache/constants.ts
|
|
62
|
+
var CACHE_DIR = ".w3wallets/cache";
|
|
63
|
+
|
|
64
|
+
// src/core/utils.ts
|
|
45
65
|
var import_path = __toESM(require("path"));
|
|
46
66
|
var import_fs = __toESM(require("fs"));
|
|
47
67
|
var import_crypto = __toESM(require("crypto"));
|
|
48
|
-
var import_test = require("@playwright/test");
|
|
49
|
-
var W3WALLETS_DIR = ".w3wallets";
|
|
50
68
|
function sleep(ms) {
|
|
51
69
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
70
|
}
|
|
71
|
+
function getExtensionId(extensionPath) {
|
|
72
|
+
const absolutePath = import_path.default.resolve(extensionPath);
|
|
73
|
+
const manifestPath = import_path.default.join(absolutePath, "manifest.json");
|
|
74
|
+
const manifest = JSON.parse(import_fs.default.readFileSync(manifestPath, "utf-8"));
|
|
75
|
+
let dataToHash;
|
|
76
|
+
if (manifest.key) {
|
|
77
|
+
dataToHash = Buffer.from(manifest.key, "base64");
|
|
78
|
+
} else {
|
|
79
|
+
dataToHash = Buffer.from(absolutePath);
|
|
80
|
+
}
|
|
81
|
+
const hash = import_crypto.default.createHash("sha256").update(dataToHash).digest();
|
|
82
|
+
const ALPHABET = "abcdefghijklmnop";
|
|
83
|
+
let extensionId = "";
|
|
84
|
+
for (let i = 0; i < 16; i++) {
|
|
85
|
+
const byte = hash[i];
|
|
86
|
+
extensionId += ALPHABET[byte >> 4 & 15];
|
|
87
|
+
extensionId += ALPHABET[byte & 15];
|
|
88
|
+
}
|
|
89
|
+
return extensionId;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/timeouts.ts
|
|
93
|
+
var SERVICE_WORKER_TIMEOUT = 3e4;
|
|
94
|
+
var SERVICE_WORKER_POLL_INTERVAL = 500;
|
|
95
|
+
var POPUP_VISIBILITY_TIMEOUT = 2e3;
|
|
96
|
+
var SHIELD_CLOSE_TIMEOUT = 1e3;
|
|
97
|
+
var ARIA_CLOSE_TIMEOUT = 500;
|
|
98
|
+
var POPUP_HIDDEN_TIMEOUT = 3e3;
|
|
99
|
+
var POST_UNLOCK_TIMEOUT = 3e4;
|
|
100
|
+
var NOTIFICATION_CHECK_TIMEOUT = 5e3;
|
|
101
|
+
var POST_CLICK_TIMEOUT = 1e4;
|
|
102
|
+
var BUTTON_OR_POPUP_TIMEOUT = 3e4;
|
|
103
|
+
var LAST_RESORT_CLICK_TIMEOUT = 1e4;
|
|
104
|
+
var LOCK_SCREEN_TIMEOUT = 3e4;
|
|
105
|
+
var MENU_BUTTON_TIMEOUT = 3e4;
|
|
106
|
+
var ONBOARD_VISIBLE_TIMEOUT = 3e4;
|
|
107
|
+
var ROUTE_RETRY_TIMEOUT = 5e3;
|
|
108
|
+
var MAX_ROUTE_ATTEMPTS = 5;
|
|
109
|
+
var MNEMONIC_KEY_DELAY = 5;
|
|
110
|
+
var MNEMONIC_WORD_DELAY = 100;
|
|
111
|
+
|
|
112
|
+
// src/debug.ts
|
|
113
|
+
var isDebug = () => process.env.W3WALLETS_DEBUG === "true";
|
|
114
|
+
function debug(message) {
|
|
115
|
+
if (!isDebug()) return;
|
|
116
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
117
|
+
console.log(`[w3wallets ${ts}] ${message}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/cache/buildCache.ts
|
|
121
|
+
function findCacheDir(walletName) {
|
|
122
|
+
const cacheRoot = import_path2.default.join(process.cwd(), CACHE_DIR);
|
|
123
|
+
if (!import_fs2.default.existsSync(cacheRoot)) return null;
|
|
124
|
+
const entries = import_fs2.default.readdirSync(cacheRoot, { withFileTypes: true });
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
127
|
+
const metaPath = import_path2.default.join(cacheRoot, entry.name, ".meta.json");
|
|
128
|
+
if (!import_fs2.default.existsSync(metaPath)) continue;
|
|
129
|
+
try {
|
|
130
|
+
const meta = JSON.parse(import_fs2.default.readFileSync(metaPath, "utf-8"));
|
|
131
|
+
if (meta.name === walletName) {
|
|
132
|
+
return import_path2.default.join(cacheRoot, entry.name);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/withWallets.ts
|
|
142
|
+
var W3WALLETS_DIR = ".w3wallets";
|
|
143
|
+
var MIN_PLAYWRIGHT_VERSION = "1.57.0";
|
|
144
|
+
function checkPlaywrightVersion() {
|
|
145
|
+
try {
|
|
146
|
+
const pkgPath = require.resolve("@playwright/test/package.json");
|
|
147
|
+
const { version } = require(pkgPath);
|
|
148
|
+
const [minMajor, minMinor, minPatch] = MIN_PLAYWRIGHT_VERSION.split(".").map(Number);
|
|
149
|
+
const [curMajor, curMinor, curPatch] = version.split(".").map(Number);
|
|
150
|
+
const isBelow = curMajor < minMajor || curMajor === minMajor && curMinor < minMinor || curMajor === minMajor && curMinor === minMinor && curPatch < minPatch;
|
|
151
|
+
if (isBelow) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`w3wallets requires @playwright/test >= ${MIN_PLAYWRIGHT_VERSION}, but found ${version}.
|
|
154
|
+
Upgrade: npm install -D @playwright/test@latest`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
if (err instanceof Error && err.message.startsWith("w3wallets requires")) {
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
53
163
|
function withWallets(test, ...wallets) {
|
|
164
|
+
checkPlaywrightVersion();
|
|
165
|
+
const cachedCount = wallets.filter((w) => isCachedConfig(w)).length;
|
|
166
|
+
if (cachedCount > 0 && cachedCount < wallets.length) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
"Mixing cached and non-cached wallet configs is not supported. All wallets must be either cached (via prepareWallet) or non-cached."
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const useCachedContext = cachedCount > 0;
|
|
172
|
+
debug(`withWallets: ${wallets.length} wallet(s), cached=${useCachedContext}`);
|
|
54
173
|
const extensionInfo = wallets.map((w) => {
|
|
55
|
-
const extPath =
|
|
174
|
+
const extPath = import_path3.default.join(process.cwd(), W3WALLETS_DIR, w.extensionDir);
|
|
56
175
|
ensureWalletExtensionExists(extPath, w.name);
|
|
57
176
|
const extensionId = w.extensionId ?? getExtensionId(extPath);
|
|
58
177
|
return { path: extPath, id: extensionId, name: w.name };
|
|
@@ -60,14 +179,28 @@ function withWallets(test, ...wallets) {
|
|
|
60
179
|
const extensionPaths = extensionInfo.map((e) => e.path);
|
|
61
180
|
const fixtures = {
|
|
62
181
|
context: async ({}, use, testInfo) => {
|
|
63
|
-
const userDataDir =
|
|
182
|
+
const userDataDir = import_path3.default.join(
|
|
64
183
|
process.cwd(),
|
|
65
184
|
W3WALLETS_DIR,
|
|
66
185
|
".context",
|
|
67
186
|
testInfo.testId
|
|
68
187
|
);
|
|
69
188
|
cleanUserDataDir(userDataDir);
|
|
70
|
-
|
|
189
|
+
if (useCachedContext) {
|
|
190
|
+
const wallet = wallets[0];
|
|
191
|
+
const cacheDir = findCacheDir(wallet.name);
|
|
192
|
+
if (!cacheDir) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Cache not found for wallet "${wallet.name}".
|
|
195
|
+
Searched: ${import_path3.default.join(process.cwd(), CACHE_DIR)}/
|
|
196
|
+
Rebuild: npx w3wallets cache --force <your-cache-dir>
|
|
197
|
+
Ensure your *.cache.ts setup file exports prepareWallet(...).`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
import_fs3.default.cpSync(cacheDir, userDataDir, { recursive: true });
|
|
201
|
+
}
|
|
202
|
+
debug(`Launching persistent context: ${userDataDir}`);
|
|
203
|
+
const context = await import_test2.chromium.launchPersistentContext(userDataDir, {
|
|
71
204
|
headless: testInfo.project.use.headless ?? true,
|
|
72
205
|
channel: "chromium",
|
|
73
206
|
args: [
|
|
@@ -75,10 +208,29 @@ function withWallets(test, ...wallets) {
|
|
|
75
208
|
`--load-extension=${extensionPaths.join(",")}`
|
|
76
209
|
]
|
|
77
210
|
});
|
|
211
|
+
debug(`Waiting for ${extensionPaths.length} service worker(s)...`);
|
|
212
|
+
const swDeadline = Date.now() + SERVICE_WORKER_TIMEOUT;
|
|
78
213
|
while (context.serviceWorkers().length < extensionPaths.length) {
|
|
79
|
-
|
|
214
|
+
if (Date.now() > swDeadline) {
|
|
215
|
+
const found = context.serviceWorkers().length;
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Service worker initialization timed out after ${SERVICE_WORKER_TIMEOUT / 1e3}s.
|
|
218
|
+
Expected: ${extensionPaths.length} extension(s), found: ${found} service worker(s).
|
|
219
|
+
Extension paths: ${extensionPaths.map((p) => import_path3.default.relative(process.cwd(), p)).join(", ")}
|
|
220
|
+
Suggestions:
|
|
221
|
+
- Check extension path exists and contains manifest.json
|
|
222
|
+
- Try headed mode to see what's happening: headless: false
|
|
223
|
+
- Ensure extension is compatible with the installed Chromium version`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
await Promise.race([
|
|
227
|
+
context.waitForEvent("serviceworker", {
|
|
228
|
+
timeout: SERVICE_WORKER_TIMEOUT
|
|
229
|
+
}),
|
|
230
|
+
sleep(SERVICE_WORKER_POLL_INTERVAL)
|
|
231
|
+
]);
|
|
80
232
|
}
|
|
81
|
-
|
|
233
|
+
debug(`All ${extensionPaths.length} service worker(s) detected`);
|
|
82
234
|
await use(context);
|
|
83
235
|
await context.close();
|
|
84
236
|
}
|
|
@@ -87,49 +239,65 @@ function withWallets(test, ...wallets) {
|
|
|
87
239
|
const wallet = wallets[i];
|
|
88
240
|
const info = extensionInfo[i];
|
|
89
241
|
fixtures[wallet.name] = async ({ context }, use) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
242
|
+
if (isCachedConfig(wallet)) {
|
|
243
|
+
debug(`Initializing cached wallet: ${wallet.name} (ID: ${info.id})`);
|
|
244
|
+
const instance = await findCachedExtension(
|
|
245
|
+
context,
|
|
246
|
+
wallet.WalletClass,
|
|
247
|
+
info.id,
|
|
248
|
+
wallet.name,
|
|
249
|
+
wallet.homeUrl
|
|
250
|
+
);
|
|
251
|
+
await use(instance);
|
|
252
|
+
} else {
|
|
253
|
+
debug(`Initializing fresh wallet: ${wallet.name} (ID: ${info.id})`);
|
|
254
|
+
const instance = await initializeExtension(
|
|
255
|
+
context,
|
|
256
|
+
wallet.WalletClass,
|
|
257
|
+
info.id,
|
|
258
|
+
wallet.name
|
|
259
|
+
);
|
|
260
|
+
await use(instance);
|
|
261
|
+
}
|
|
97
262
|
};
|
|
98
263
|
}
|
|
99
264
|
return test.extend(fixtures);
|
|
100
265
|
}
|
|
101
266
|
function cleanUserDataDir(userDataDir) {
|
|
102
|
-
if (
|
|
103
|
-
|
|
267
|
+
if (import_fs3.default.existsSync(userDataDir)) {
|
|
268
|
+
import_fs3.default.rmSync(userDataDir, { recursive: true });
|
|
104
269
|
}
|
|
105
270
|
}
|
|
106
271
|
function ensureWalletExtensionExists(walletPath, walletName) {
|
|
107
|
-
|
|
272
|
+
const manifestPath = import_path3.default.join(walletPath, "manifest.json");
|
|
273
|
+
if (!import_fs3.default.existsSync(manifestPath)) {
|
|
108
274
|
const cliAlias = walletName.toLowerCase();
|
|
109
275
|
throw new Error(
|
|
110
|
-
`Cannot find ${walletName}.
|
|
276
|
+
`Cannot find ${walletName} extension.
|
|
277
|
+
Checked: ${manifestPath}
|
|
278
|
+
Download it: npx w3wallets ${cliAlias}
|
|
279
|
+
Custom dir: npx w3wallets -o <dir> ${cliAlias}`
|
|
111
280
|
);
|
|
112
281
|
}
|
|
113
282
|
}
|
|
114
|
-
function
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
283
|
+
async function findCachedExtension(context, ExtensionClass, expectedExtensionId, walletName, homeUrl) {
|
|
284
|
+
const expectedUrl = `chrome-extension://${expectedExtensionId}/`;
|
|
285
|
+
const worker = context.serviceWorkers().find((w) => w.url().startsWith(expectedUrl));
|
|
286
|
+
if (!worker) {
|
|
287
|
+
const availableIds = context.serviceWorkers().map((w) => w.url().split("/")[2]).filter(Boolean);
|
|
288
|
+
throw new Error(
|
|
289
|
+
`Service worker for ${walletName} (ID: ${expectedExtensionId}) not found in cached context.
|
|
290
|
+
Available IDs: [${availableIds.join(", ")}]
|
|
291
|
+
The cache may be stale. Rebuild: npx w3wallets cache --force
|
|
292
|
+
Also check extension path: ${W3WALLETS_DIR}/${walletName}/`
|
|
293
|
+
);
|
|
123
294
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
for (let i = 0; i < 16; i++) {
|
|
128
|
-
const byte = hash[i];
|
|
129
|
-
extensionId += ALPHABET[byte >> 4 & 15];
|
|
130
|
-
extensionId += ALPHABET[byte & 15];
|
|
295
|
+
const page = await context.newPage();
|
|
296
|
+
if (homeUrl) {
|
|
297
|
+
await page.goto(`chrome-extension://${expectedExtensionId}/${homeUrl}`);
|
|
131
298
|
}
|
|
132
|
-
|
|
299
|
+
const extension = new ExtensionClass(page, expectedExtensionId);
|
|
300
|
+
return extension;
|
|
133
301
|
}
|
|
134
302
|
async function initializeExtension(context, ExtensionClass, expectedExtensionId, walletName) {
|
|
135
303
|
const expectedUrl = `chrome-extension://${expectedExtensionId}/`;
|
|
@@ -142,121 +310,58 @@ async function initializeExtension(context, ExtensionClass, expectedExtensionId,
|
|
|
142
310
|
}
|
|
143
311
|
const page = await context.newPage();
|
|
144
312
|
const extension = new ExtensionClass(page, expectedExtensionId);
|
|
145
|
-
await extension.gotoOnboardPage();
|
|
146
313
|
return extension;
|
|
147
314
|
}
|
|
148
315
|
|
|
149
316
|
// src/core/types.ts
|
|
150
|
-
function createWallet(
|
|
151
|
-
return
|
|
317
|
+
function createWallet(config2) {
|
|
318
|
+
return config2;
|
|
152
319
|
}
|
|
153
320
|
|
|
154
|
-
// src/wallets/
|
|
155
|
-
var
|
|
321
|
+
// src/wallets/metamask/metamask.ts
|
|
322
|
+
var import_test3 = require("@playwright/test");
|
|
323
|
+
|
|
324
|
+
// src/config.ts
|
|
325
|
+
var config = {
|
|
326
|
+
/**
|
|
327
|
+
* Timeout for actions like click, fill, waitFor, goto.
|
|
328
|
+
* Set via W3WALLETS_ACTION_TIMEOUT env variable.
|
|
329
|
+
* @default 30000 (30 seconds)
|
|
330
|
+
*/
|
|
331
|
+
get actionTimeout() {
|
|
332
|
+
const value = process.env.W3WALLETS_ACTION_TIMEOUT;
|
|
333
|
+
return value ? parseInt(value, 10) : void 0;
|
|
334
|
+
},
|
|
335
|
+
/**
|
|
336
|
+
* Timeout for expect assertions like toBeVisible, toContainText.
|
|
337
|
+
* Set via W3WALLETS_EXPECT_TIMEOUT env variable.
|
|
338
|
+
* @default undefined (uses Playwright's default of 5000ms)
|
|
339
|
+
*/
|
|
340
|
+
get expectTimeout() {
|
|
341
|
+
const value = process.env.W3WALLETS_EXPECT_TIMEOUT;
|
|
342
|
+
return value ? parseInt(value, 10) : void 0;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
156
345
|
|
|
157
346
|
// src/core/wallet.ts
|
|
158
347
|
var Wallet = class {
|
|
159
348
|
constructor(page, extensionId) {
|
|
160
349
|
this.page = page;
|
|
161
350
|
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
|
-
}
|
|
351
|
+
if (config.actionTimeout) {
|
|
352
|
+
page.setDefaultTimeout(config.actionTimeout);
|
|
230
353
|
}
|
|
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
354
|
}
|
|
247
355
|
};
|
|
248
356
|
|
|
249
357
|
// src/wallets/metamask/metamask.ts
|
|
250
|
-
var import_test3 = require("@playwright/test");
|
|
251
358
|
var Metamask = class extends Wallet {
|
|
252
359
|
defaultPassword = "TestPassword123!";
|
|
253
360
|
async gotoOnboardPage() {
|
|
254
|
-
await this.page.goto(
|
|
255
|
-
`chrome-extension://${this.extensionId}/home.html#onboarding/welcome`
|
|
256
|
-
);
|
|
361
|
+
await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
|
|
257
362
|
await (0, import_test3.expect)(
|
|
258
363
|
this.page.getByRole("button", { name: "I have an existing wallet" })
|
|
259
|
-
).toBeVisible();
|
|
364
|
+
).toBeVisible({ timeout: ONBOARD_VISIBLE_TIMEOUT });
|
|
260
365
|
}
|
|
261
366
|
/**
|
|
262
367
|
* Onboard MetaMask with a mnemonic phrase
|
|
@@ -264,68 +369,283 @@ var Metamask = class extends Wallet {
|
|
|
264
369
|
* @param password - Optional password (defaults to TestPassword123!)
|
|
265
370
|
*/
|
|
266
371
|
async onboard(mnemonic, password) {
|
|
372
|
+
debug("metamask.onboard: starting");
|
|
267
373
|
const pwd = password ?? this.defaultPassword;
|
|
374
|
+
await this.gotoOnboardPage();
|
|
268
375
|
await this.page.getByRole("button", { name: "I have an existing wallet" }).click();
|
|
269
376
|
await this.page.getByRole("button", { name: "Import using Secret Recovery Phrase" }).click();
|
|
270
|
-
const
|
|
271
|
-
await
|
|
272
|
-
|
|
377
|
+
const srpTextarea = this.page.getByTestId("srp-input-import__srp-note");
|
|
378
|
+
await srpTextarea.click();
|
|
379
|
+
const words = mnemonic.split(" ");
|
|
380
|
+
for (let i = 0; i < words.length; i++) {
|
|
381
|
+
await this.page.keyboard.type(words[i], { delay: MNEMONIC_KEY_DELAY });
|
|
382
|
+
if (i < words.length - 1) {
|
|
383
|
+
await this.page.keyboard.press("Space");
|
|
384
|
+
await this.page.waitForTimeout(MNEMONIC_WORD_DELAY);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
273
387
|
const continueBtn = this.page.getByTestId("import-srp-confirm");
|
|
388
|
+
await (0, import_test3.expect)(continueBtn).toBeEnabled({ timeout: config.expectTimeout });
|
|
274
389
|
await continueBtn.click();
|
|
275
390
|
const passwordInputs = this.page.locator('input[type="password"]');
|
|
276
391
|
await passwordInputs.nth(0).fill(pwd);
|
|
277
392
|
await passwordInputs.nth(1).fill(pwd);
|
|
278
|
-
await this.page.getByRole("checkbox").click();
|
|
393
|
+
await this.page.getByRole("checkbox").click({ force: true });
|
|
279
394
|
await this.page.getByRole("button", { name: "Create password" }).click();
|
|
280
395
|
const metametricsBtn = this.page.getByTestId("metametrics-i-agree");
|
|
281
|
-
await metametricsBtn.waitFor({ state: "visible", timeout: 3e4 });
|
|
282
396
|
await metametricsBtn.click();
|
|
283
397
|
const openWalletBtn = this.page.getByRole("button", {
|
|
284
398
|
name: /open wallet/i
|
|
285
399
|
});
|
|
286
|
-
await openWalletBtn.waitFor({ state: "visible", timeout: 3e4 });
|
|
287
400
|
await openWalletBtn.click();
|
|
401
|
+
await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
|
|
288
402
|
await this.page.goto(
|
|
289
403
|
`chrome-extension://${this.extensionId}/sidepanel.html`
|
|
290
404
|
);
|
|
291
|
-
|
|
405
|
+
debug("metamask.onboard: complete");
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Dismiss MetaMask promotional popups (e.g., "Transaction Shield")
|
|
409
|
+
* that may overlay the confirmation UI.
|
|
410
|
+
*/
|
|
411
|
+
async dismissPopups() {
|
|
412
|
+
debug("metamask.dismissPopups: checking for popups");
|
|
413
|
+
const popup = this.page.getByText(/Transaction Shield|free trial/i);
|
|
414
|
+
if (!await popup.first().isVisible({ timeout: POPUP_VISIBILITY_TIMEOUT }).catch(() => false)) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const shieldClose = this.page.getByTestId(
|
|
418
|
+
"shield-entry-modal-close-button"
|
|
419
|
+
);
|
|
420
|
+
if (await shieldClose.isVisible({ timeout: SHIELD_CLOSE_TIMEOUT }).catch(() => false)) {
|
|
421
|
+
await shieldClose.click();
|
|
422
|
+
if (await this.waitForPopupHidden(popup)) return;
|
|
423
|
+
}
|
|
424
|
+
const closeByAria = this.page.locator('button[aria-label="close"]').first();
|
|
425
|
+
if (await closeByAria.isVisible({ timeout: ARIA_CLOSE_TIMEOUT }).catch(() => false)) {
|
|
426
|
+
await closeByAria.click();
|
|
427
|
+
if (await this.waitForPopupHidden(popup)) return;
|
|
428
|
+
}
|
|
429
|
+
await this.page.keyboard.press("Escape");
|
|
430
|
+
await this.waitForPopupHidden(popup);
|
|
431
|
+
}
|
|
432
|
+
async waitForPopupHidden(popup) {
|
|
433
|
+
try {
|
|
434
|
+
await popup.first().waitFor({ state: "hidden", timeout: POPUP_HIDDEN_TIMEOUT });
|
|
435
|
+
return true;
|
|
436
|
+
} catch {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* After unlock, MetaMask may show onboarding screens, queued
|
|
442
|
+
* notifications, or go straight to the wallet UI. Race all possible
|
|
443
|
+
* states in a single wait to avoid sequential timeout penalties.
|
|
444
|
+
*/
|
|
445
|
+
async stabilizePostUnlock() {
|
|
446
|
+
debug("metamask.stabilizePostUnlock: racing post-unlock states");
|
|
447
|
+
const metametricsBtn = this.page.getByTestId("metametrics-i-agree");
|
|
448
|
+
const openWalletBtn = this.page.getByRole("button", {
|
|
449
|
+
name: /open wallet/i
|
|
450
|
+
});
|
|
451
|
+
const readyIndicator = this.page.getByTestId("account-options-menu-button");
|
|
452
|
+
const rejectAllBtn = this.page.getByText("Reject all");
|
|
453
|
+
const notificationCancelBtn = this.page.getByTestId(
|
|
454
|
+
"confirmation-cancel-button"
|
|
455
|
+
);
|
|
456
|
+
const state = await Promise.race([
|
|
457
|
+
metametricsBtn.waitFor({ state: "visible", timeout: POST_UNLOCK_TIMEOUT }).then(() => "metametrics"),
|
|
458
|
+
openWalletBtn.waitFor({ state: "visible", timeout: POST_UNLOCK_TIMEOUT }).then(() => "openWallet"),
|
|
459
|
+
rejectAllBtn.waitFor({ state: "visible", timeout: POST_UNLOCK_TIMEOUT }).then(() => "rejectAll"),
|
|
460
|
+
notificationCancelBtn.waitFor({ state: "visible", timeout: POST_UNLOCK_TIMEOUT }).then(() => "notification"),
|
|
461
|
+
readyIndicator.waitFor({ state: "visible", timeout: POST_UNLOCK_TIMEOUT }).then(() => "ready")
|
|
462
|
+
]).catch(() => "timeout");
|
|
463
|
+
debug(`metamask.stabilizePostUnlock: state=${state}`);
|
|
464
|
+
if (state === "timeout") {
|
|
465
|
+
debug(
|
|
466
|
+
`metamask.stabilizePostUnlock: timeout after ${POST_UNLOCK_TIMEOUT}ms. URL: ${this.page.url()}. Checked: metametrics-i-agree, open-wallet button, Reject all, confirmation-cancel-button, account-options-menu-button`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
if (state === "metametrics") {
|
|
470
|
+
await metametricsBtn.click();
|
|
471
|
+
if (await openWalletBtn.isVisible({ timeout: POPUP_HIDDEN_TIMEOUT }).catch(() => false)) {
|
|
472
|
+
await openWalletBtn.click();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (state === "openWallet") {
|
|
476
|
+
await openWalletBtn.click();
|
|
477
|
+
}
|
|
478
|
+
if (state !== "ready") {
|
|
479
|
+
await this.dismissQueuedNotifications();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Dismiss all queued MetaMask notifications (e.g., Solana/Tron account
|
|
484
|
+
* removal). These use the templated confirmation flow at #/confirmation/...
|
|
485
|
+
* If multiple are queued, "Reject all" appears; if only one, just
|
|
486
|
+
* confirmation-cancel-button is available.
|
|
487
|
+
*/
|
|
488
|
+
async dismissQueuedNotifications() {
|
|
489
|
+
const homeUrl = `chrome-extension://${this.extensionId}/home.html`;
|
|
490
|
+
const readyIndicator = this.page.getByTestId("account-options-menu-button");
|
|
491
|
+
const rejectAllBtn = this.page.getByText("Reject all");
|
|
492
|
+
const notificationCancelBtn = this.page.getByTestId(
|
|
493
|
+
"confirmation-cancel-button"
|
|
494
|
+
);
|
|
495
|
+
await this.page.goto(homeUrl);
|
|
496
|
+
for (let i = 0; i < 10; i++) {
|
|
497
|
+
const state = await Promise.race([
|
|
498
|
+
readyIndicator.waitFor({ state: "visible", timeout: NOTIFICATION_CHECK_TIMEOUT }).then(() => "ready"),
|
|
499
|
+
rejectAllBtn.waitFor({ state: "visible", timeout: NOTIFICATION_CHECK_TIMEOUT }).then(() => "rejectAll"),
|
|
500
|
+
notificationCancelBtn.waitFor({ state: "visible", timeout: NOTIFICATION_CHECK_TIMEOUT }).then(() => "notification")
|
|
501
|
+
]).catch(() => "timeout");
|
|
502
|
+
if (state === "ready") return;
|
|
503
|
+
if (state === "rejectAll") {
|
|
504
|
+
await rejectAllBtn.click();
|
|
505
|
+
await this.page.goto(homeUrl);
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (state === "notification") {
|
|
509
|
+
await notificationCancelBtn.click();
|
|
510
|
+
await this.page.goto(homeUrl);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
debug(
|
|
514
|
+
`metamask.dismissQueuedNotifications: timeout at iteration ${i}. URL: ${this.page.url()}`
|
|
515
|
+
);
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
await (0, import_test3.expect)(readyIndicator).toBeVisible({ timeout: POST_UNLOCK_TIMEOUT });
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Wait for a target button while handling the Transaction Shield popup.
|
|
522
|
+
* Always navigates to sidepanel.html fresh so MetaMask's
|
|
523
|
+
* ConfirmationHandler can route to the pending approval.
|
|
524
|
+
*/
|
|
525
|
+
async waitAndClickButton(btnLocator) {
|
|
526
|
+
debug(`metamask.waitAndClickButton: navigating to sidepanel`);
|
|
527
|
+
const popup = this.page.getByText(/Transaction Shield|free trial/i);
|
|
528
|
+
const sidepanelUrl = `chrome-extension://${this.extensionId}/sidepanel.html`;
|
|
529
|
+
const confirmRoutePattern = /#\/(confirm-transaction|connect|confirmation)\b/;
|
|
530
|
+
const waitForButtonOrPopup = (timeout) => Promise.race([
|
|
531
|
+
btnLocator.first().waitFor({ state: "visible", timeout }).then(() => "button"),
|
|
532
|
+
popup.first().waitFor({ state: "visible", timeout }).then(() => "popup")
|
|
533
|
+
]).catch(() => "timeout");
|
|
534
|
+
const handlePopupAndClick = async () => {
|
|
535
|
+
await this.dismissPopups();
|
|
536
|
+
await btnLocator.first().waitFor({ state: "visible", timeout: BUTTON_OR_POPUP_TIMEOUT });
|
|
537
|
+
await btnLocator.first().click();
|
|
538
|
+
};
|
|
539
|
+
let routeFound = false;
|
|
540
|
+
for (let attempt = 0; attempt < MAX_ROUTE_ATTEMPTS; attempt++) {
|
|
541
|
+
await this.page.goto(sidepanelUrl);
|
|
542
|
+
try {
|
|
543
|
+
await this.page.waitForURL(confirmRoutePattern, {
|
|
544
|
+
timeout: ROUTE_RETRY_TIMEOUT
|
|
545
|
+
});
|
|
546
|
+
routeFound = true;
|
|
547
|
+
break;
|
|
548
|
+
} catch {
|
|
549
|
+
debug(
|
|
550
|
+
`metamask.waitAndClickButton: route attempt ${attempt + 1}/${MAX_ROUTE_ATTEMPTS} failed. URL: ${this.page.url()}`
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (!routeFound) {
|
|
555
|
+
console.warn(
|
|
556
|
+
`[w3wallets] confirmation route not found after ${MAX_ROUTE_ATTEMPTS} attempts. URL: ${this.page.url()}`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
const result = await waitForButtonOrPopup(BUTTON_OR_POPUP_TIMEOUT);
|
|
560
|
+
debug(
|
|
561
|
+
`metamask.waitAndClickButton: result=${result}, URL=${this.page.url()}`
|
|
562
|
+
);
|
|
563
|
+
if (result === "button") {
|
|
564
|
+
await btnLocator.first().click();
|
|
565
|
+
await this.page.waitForURL((url) => !confirmRoutePattern.test(url.toString()), {
|
|
566
|
+
timeout: POST_CLICK_TIMEOUT
|
|
567
|
+
}).catch(() => {
|
|
568
|
+
console.warn(
|
|
569
|
+
`[w3wallets] still on confirmation route after click. URL: ${this.page.url()}`
|
|
570
|
+
);
|
|
571
|
+
});
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (result === "popup") {
|
|
575
|
+
await handlePopupAndClick();
|
|
576
|
+
await this.page.waitForURL((url) => !confirmRoutePattern.test(url.toString()), {
|
|
577
|
+
timeout: POST_CLICK_TIMEOUT
|
|
578
|
+
}).catch(() => {
|
|
579
|
+
console.warn(
|
|
580
|
+
`[w3wallets] still on confirmation route after popup dismiss. URL: ${this.page.url()}`
|
|
581
|
+
);
|
|
582
|
+
});
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const isOnConfirmRoute = confirmRoutePattern.test(this.page.url());
|
|
586
|
+
debug(
|
|
587
|
+
`metamask.waitAndClickButton: timeout after ${BUTTON_OR_POPUP_TIMEOUT}ms. URL: ${this.page.url()}, onConfirmRoute: ${isOnConfirmRoute}`
|
|
588
|
+
);
|
|
589
|
+
console.warn(
|
|
590
|
+
`[w3wallets] no button or popup found after ${BUTTON_OR_POPUP_TIMEOUT / 1e3}s. URL: ${this.page.url()}`
|
|
591
|
+
);
|
|
592
|
+
await btnLocator.first().click({ timeout: LAST_RESORT_CLICK_TIMEOUT });
|
|
292
593
|
}
|
|
293
594
|
async approve() {
|
|
294
|
-
|
|
295
|
-
|
|
595
|
+
debug("metamask.approve: starting");
|
|
596
|
+
const confirmBtn = 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 }));
|
|
597
|
+
await this.waitAndClickButton(confirmBtn);
|
|
296
598
|
}
|
|
297
599
|
async deny() {
|
|
298
|
-
|
|
299
|
-
|
|
600
|
+
debug("metamask.deny: starting");
|
|
601
|
+
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$/i })).or(this.page.getByRole("button", { name: /^reject$/i }));
|
|
602
|
+
await this.waitAndClickButton(cancelBtn);
|
|
300
603
|
}
|
|
301
604
|
/**
|
|
302
605
|
* Lock the MetaMask wallet
|
|
303
606
|
*/
|
|
304
607
|
async lock() {
|
|
305
|
-
|
|
306
|
-
await this.page.
|
|
608
|
+
debug("metamask.lock: starting");
|
|
609
|
+
await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
|
|
610
|
+
const menuBtn = this.page.getByTestId("account-options-menu-button");
|
|
611
|
+
await menuBtn.waitFor({ state: "visible", timeout: MENU_BUTTON_TIMEOUT });
|
|
612
|
+
await menuBtn.click({ force: true });
|
|
613
|
+
await this.page.locator("text=Log out").click();
|
|
307
614
|
}
|
|
308
615
|
/**
|
|
309
|
-
* Unlock MetaMask with password
|
|
616
|
+
* Unlock MetaMask with password.
|
|
617
|
+
* After unlocking, stabilizes the wallet UI by handling post-unlock
|
|
618
|
+
* screens (metametrics, onboarding completion) and dismissing queued
|
|
619
|
+
* notifications. Ends on home.html with the wallet UI ready.
|
|
310
620
|
*/
|
|
311
621
|
async unlock(password) {
|
|
622
|
+
debug("metamask.unlock: starting");
|
|
312
623
|
const pwd = password ?? this.defaultPassword;
|
|
624
|
+
await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
|
|
313
625
|
const passwordInput = this.page.getByTestId("unlock-password");
|
|
314
626
|
await passwordInput.fill(pwd);
|
|
315
627
|
await this.page.getByTestId("unlock-submit").click();
|
|
628
|
+
await this.page.waitForSelector('[data-testid="unlock-password"]', {
|
|
629
|
+
state: "hidden",
|
|
630
|
+
timeout: LOCK_SCREEN_TIMEOUT
|
|
631
|
+
});
|
|
632
|
+
await this.stabilizePostUnlock();
|
|
633
|
+
debug("metamask.unlock: complete");
|
|
316
634
|
}
|
|
317
635
|
/**
|
|
318
636
|
* Switch to an existing network in MetaMask
|
|
319
637
|
* @param networkName - Name of the network to switch to (e.g., "Ethereum Mainnet", "Sepolia")
|
|
320
638
|
*/
|
|
321
639
|
async switchNetwork(networkName, networkType = "Popular") {
|
|
640
|
+
debug(`metamask.switchNetwork: ${networkName} (${networkType})`);
|
|
322
641
|
await this.page.getByTestId("sort-by-networks").click();
|
|
323
642
|
if (networkType === "Custom") {
|
|
324
|
-
await this.page.getByRole("tab", { name: "Custom" }).click();
|
|
643
|
+
await this.page.getByRole("tab", { name: "Custom" }).click({ force: true });
|
|
325
644
|
}
|
|
326
645
|
await this.page.getByText(networkName).click();
|
|
327
646
|
await (0, import_test3.expect)(this.page.getByTestId("sort-by-networks")).toHaveText(
|
|
328
|
-
networkName
|
|
647
|
+
networkName,
|
|
648
|
+
{ timeout: config.expectTimeout }
|
|
329
649
|
);
|
|
330
650
|
}
|
|
331
651
|
async switchAccount(accountName) {
|
|
@@ -346,7 +666,7 @@ var Metamask = class extends Wallet {
|
|
|
346
666
|
await this.page.getByRole("button", { name: /save/i }).click();
|
|
347
667
|
}
|
|
348
668
|
async addCustomNetwork(settings) {
|
|
349
|
-
await this.page.getByTestId("account-options-menu-button").click();
|
|
669
|
+
await this.page.getByTestId("account-options-menu-button").click({ force: true });
|
|
350
670
|
await this.page.getByTestId("global-menu-networks").click();
|
|
351
671
|
await this.page.getByRole("button", { name: "Add a custom network" }).click();
|
|
352
672
|
await this.page.getByTestId("network-form-network-name").fill(settings.name);
|
|
@@ -357,24 +677,33 @@ var Metamask = class extends Wallet {
|
|
|
357
677
|
await this.page.getByTestId("rpc-url-input-test").fill(settings.rpc);
|
|
358
678
|
await this.page.getByRole("button", { name: "Add URL" }).click();
|
|
359
679
|
await this.page.getByRole("button", { name: "Save" }).click();
|
|
680
|
+
await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
|
|
681
|
+
await this.page.waitForLoadState("domcontentloaded");
|
|
360
682
|
}
|
|
361
683
|
async enableTestNetworks() {
|
|
362
|
-
await this.page.getByTestId("account-options-menu-button").click();
|
|
684
|
+
await this.page.getByTestId("account-options-menu-button").click({ force: true });
|
|
363
685
|
await this.page.getByTestId("global-menu-networks").click();
|
|
364
|
-
|
|
365
|
-
|
|
686
|
+
const toggle = this.page.locator(
|
|
687
|
+
"text=Show test networks >> xpath=following-sibling::label"
|
|
688
|
+
);
|
|
689
|
+
await (0, import_test3.expect)(toggle).toBeVisible({ timeout: config.expectTimeout });
|
|
690
|
+
await toggle.click();
|
|
691
|
+
await this.page.goto(`chrome-extension://${this.extensionId}/home.html`);
|
|
366
692
|
}
|
|
367
693
|
async importAccount(privateKey) {
|
|
694
|
+
debug("metamask.importAccount: starting");
|
|
368
695
|
await this.page.getByTestId("account-menu-icon").click();
|
|
369
696
|
await this.page.getByTestId("account-list-add-wallet-button").click();
|
|
370
|
-
await this.page.getByTestId("
|
|
697
|
+
await this.page.getByTestId("choose-wallet-type-import-account").click();
|
|
371
698
|
await this.page.locator("#private-key-box").fill(privateKey);
|
|
372
699
|
await this.page.getByTestId("import-account-confirm-button").click();
|
|
373
|
-
await this.page.
|
|
700
|
+
await this.page.getByTestId("back-button").click();
|
|
701
|
+
await this.page.locator('[data-testid^="multichain-account-cell-keyring:"]').first().click();
|
|
374
702
|
}
|
|
375
703
|
async accountNameIs(accountName) {
|
|
376
704
|
await (0, import_test3.expect)(this.page.getByTestId("account-menu-icon")).toContainText(
|
|
377
|
-
accountName
|
|
705
|
+
accountName,
|
|
706
|
+
{ timeout: config.expectTimeout }
|
|
378
707
|
);
|
|
379
708
|
}
|
|
380
709
|
};
|
|
@@ -387,9 +716,11 @@ var PolkadotJS = class extends Wallet {
|
|
|
387
716
|
await this.page.goto(`chrome-extension://${this.extensionId}/index.html`);
|
|
388
717
|
await (0, import_test4.expect)(
|
|
389
718
|
this.page.getByText("Before we start, just a couple of notes")
|
|
390
|
-
).toBeVisible();
|
|
719
|
+
).toBeVisible({ timeout: config.expectTimeout });
|
|
391
720
|
}
|
|
392
721
|
async onboard(seed, password, name) {
|
|
722
|
+
debug("polkadotJS.onboard: starting");
|
|
723
|
+
await this.gotoOnboardPage();
|
|
393
724
|
await this.page.getByRole("button", { name: "Understood, let me continue" }).click();
|
|
394
725
|
await this.page.getByRole("button", { name: "I Understand" }).click();
|
|
395
726
|
await this.page.locator(".popupToggle").first().click();
|
|
@@ -406,12 +737,13 @@ var PolkadotJS = class extends Wallet {
|
|
|
406
737
|
password ?? this.defaultPassword
|
|
407
738
|
);
|
|
408
739
|
await this.page.getByRole("button", { name: "Add the account with the supplied seed" }).click();
|
|
740
|
+
debug("polkadotJS.onboard: complete");
|
|
409
741
|
}
|
|
410
742
|
async selectAllAccounts() {
|
|
411
743
|
await this.page.getByText("Select all").click();
|
|
412
744
|
}
|
|
413
745
|
async selectAccount(accountId) {
|
|
414
|
-
await this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").
|
|
746
|
+
await this.page.locator(".accountWichCheckbox").filter({ hasText: accountId }).locator(".accountTree-checkbox").click();
|
|
415
747
|
}
|
|
416
748
|
async enterPassword(password) {
|
|
417
749
|
await this._getLabeledInput("Password for this account").fill(
|
|
@@ -419,6 +751,7 @@ var PolkadotJS = class extends Wallet {
|
|
|
419
751
|
);
|
|
420
752
|
}
|
|
421
753
|
async approve() {
|
|
754
|
+
debug("polkadotJS.approve: starting");
|
|
422
755
|
const connect = this.page.getByRole("button", { name: "Connect" });
|
|
423
756
|
const signTransaction = this.page.getByRole("button", {
|
|
424
757
|
name: "Sign the transaction"
|
|
@@ -426,6 +759,7 @@ var PolkadotJS = class extends Wallet {
|
|
|
426
759
|
await connect.or(signTransaction).click();
|
|
427
760
|
}
|
|
428
761
|
async deny() {
|
|
762
|
+
debug("polkadotJS.deny: starting");
|
|
429
763
|
const reject = this.page.getByRole("button", { name: "Reject" });
|
|
430
764
|
const cancel = this.page.getByRole("link", { name: "Cancel" });
|
|
431
765
|
await reject.or(cancel).click();
|
|
@@ -438,29 +772,37 @@ var PolkadotJS = class extends Wallet {
|
|
|
438
772
|
};
|
|
439
773
|
|
|
440
774
|
// src/wallets/index.ts
|
|
441
|
-
var backpack = createWallet({
|
|
442
|
-
name: "backpack",
|
|
443
|
-
extensionDir: "backpack",
|
|
444
|
-
WalletClass: Backpack
|
|
445
|
-
});
|
|
446
775
|
var metamask = createWallet({
|
|
447
776
|
name: "metamask",
|
|
448
777
|
extensionDir: "metamask",
|
|
449
|
-
WalletClass: Metamask
|
|
778
|
+
WalletClass: Metamask,
|
|
779
|
+
homeUrl: "home.html"
|
|
450
780
|
});
|
|
451
781
|
var polkadotJS = createWallet({
|
|
452
782
|
name: "polkadotJS",
|
|
453
783
|
extensionDir: "polkadotjs",
|
|
454
|
-
WalletClass: PolkadotJS
|
|
784
|
+
WalletClass: PolkadotJS,
|
|
785
|
+
homeUrl: "index.html"
|
|
455
786
|
});
|
|
787
|
+
|
|
788
|
+
// src/cache/prepareWallet.ts
|
|
789
|
+
function prepareWallet(walletConfig, setupFn) {
|
|
790
|
+
return {
|
|
791
|
+
...walletConfig,
|
|
792
|
+
setupFn,
|
|
793
|
+
__cached: true
|
|
794
|
+
};
|
|
795
|
+
}
|
|
456
796
|
// Annotate the CommonJS export names for ESM import in node:
|
|
457
797
|
0 && (module.exports = {
|
|
458
|
-
Backpack,
|
|
459
798
|
Metamask,
|
|
460
799
|
PolkadotJS,
|
|
461
|
-
|
|
800
|
+
config,
|
|
462
801
|
createWallet,
|
|
802
|
+
debug,
|
|
803
|
+
isCachedConfig,
|
|
463
804
|
metamask,
|
|
464
805
|
polkadotJS,
|
|
806
|
+
prepareWallet,
|
|
465
807
|
withWallets
|
|
466
808
|
});
|