@marigoldlabs/web3-tester 0.1.2 → 0.4.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.
Files changed (96) hide show
  1. package/.env.example +26 -17
  2. package/LICENSE +21 -0
  3. package/README.md +480 -48
  4. package/dist/anvil.d.ts +90 -2
  5. package/dist/anvil.d.ts.map +1 -1
  6. package/dist/anvil.js +215 -13
  7. package/dist/anvil.js.map +1 -1
  8. package/dist/benchmark.d.ts +55 -0
  9. package/dist/benchmark.d.ts.map +1 -0
  10. package/dist/benchmark.js +168 -0
  11. package/dist/benchmark.js.map +1 -0
  12. package/dist/contracts/test-erc20.d.ts +227 -0
  13. package/dist/contracts/test-erc20.d.ts.map +1 -0
  14. package/dist/contracts/test-erc20.js +8 -0
  15. package/dist/contracts/test-erc20.js.map +1 -0
  16. package/dist/erc20.d.ts +38 -0
  17. package/dist/erc20.d.ts.map +1 -0
  18. package/dist/erc20.js +229 -0
  19. package/dist/erc20.js.map +1 -0
  20. package/dist/fixtures.d.ts +44 -2
  21. package/dist/fixtures.d.ts.map +1 -1
  22. package/dist/fixtures.js +162 -17
  23. package/dist/fixtures.js.map +1 -1
  24. package/dist/index.d.ts +26 -5
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +11 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/injected-provider.d.ts.map +1 -1
  29. package/dist/injected-provider.js +863 -77
  30. package/dist/injected-provider.js.map +1 -1
  31. package/dist/live-fixtures.d.ts +32 -3
  32. package/dist/live-fixtures.d.ts.map +1 -1
  33. package/dist/live-fixtures.js +64 -27
  34. package/dist/live-fixtures.js.map +1 -1
  35. package/dist/matchers.d.ts +90 -0
  36. package/dist/matchers.d.ts.map +1 -0
  37. package/dist/matchers.js +268 -0
  38. package/dist/matchers.js.map +1 -0
  39. package/dist/metamask-extension.d.ts +34 -0
  40. package/dist/metamask-extension.d.ts.map +1 -0
  41. package/dist/metamask-extension.js +97 -0
  42. package/dist/metamask-extension.js.map +1 -0
  43. package/dist/mock-wallet-controller.d.ts +354 -4
  44. package/dist/mock-wallet-controller.d.ts.map +1 -1
  45. package/dist/mock-wallet-controller.js +1444 -58
  46. package/dist/mock-wallet-controller.js.map +1 -1
  47. package/dist/private-key-rpc-client.d.ts +1744 -2
  48. package/dist/private-key-rpc-client.d.ts.map +1 -1
  49. package/dist/private-key-rpc-client.js +245 -30
  50. package/dist/private-key-rpc-client.js.map +1 -1
  51. package/dist/real-wallet-cache.d.ts +103 -0
  52. package/dist/real-wallet-cache.d.ts.map +1 -0
  53. package/dist/real-wallet-cache.js +331 -0
  54. package/dist/real-wallet-cache.js.map +1 -0
  55. package/dist/real-wallet-extension-fixtures.d.ts +35 -0
  56. package/dist/real-wallet-extension-fixtures.d.ts.map +1 -0
  57. package/dist/real-wallet-extension-fixtures.js +96 -0
  58. package/dist/real-wallet-extension-fixtures.js.map +1 -0
  59. package/dist/real-wallet-extension.d.ts +86 -0
  60. package/dist/real-wallet-extension.d.ts.map +1 -0
  61. package/dist/real-wallet-extension.js +245 -0
  62. package/dist/real-wallet-extension.js.map +1 -0
  63. package/dist/real-wallet-fixtures.d.ts +52 -0
  64. package/dist/real-wallet-fixtures.d.ts.map +1 -0
  65. package/dist/real-wallet-fixtures.js +88 -0
  66. package/dist/real-wallet-fixtures.js.map +1 -0
  67. package/dist/real-wallet.d.ts +119 -19
  68. package/dist/real-wallet.d.ts.map +1 -1
  69. package/dist/real-wallet.js +1656 -144
  70. package/dist/real-wallet.js.map +1 -1
  71. package/dist/safe.d.ts +311 -0
  72. package/dist/safe.d.ts.map +1 -0
  73. package/dist/safe.js +743 -0
  74. package/dist/safe.js.map +1 -0
  75. package/dist/transactions.d.ts +118 -0
  76. package/dist/transactions.d.ts.map +1 -0
  77. package/dist/transactions.js +207 -0
  78. package/dist/transactions.js.map +1 -0
  79. package/dist/types.d.ts +36 -1
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/wallet-personas.d.ts +99 -0
  82. package/dist/wallet-personas.d.ts.map +1 -0
  83. package/dist/wallet-personas.js +666 -0
  84. package/dist/wallet-personas.js.map +1 -0
  85. package/dist/walletconnect.d.ts +373 -0
  86. package/dist/walletconnect.d.ts.map +1 -0
  87. package/dist/walletconnect.js +799 -0
  88. package/dist/walletconnect.js.map +1 -0
  89. package/examples/live-sepolia.spec.ts +20 -1
  90. package/examples/playwright.config.ts +8 -0
  91. package/package.json +90 -8
  92. package/docs/API.md +0 -223
  93. package/docs/ARCHITECTURE.md +0 -81
  94. package/docs/CONSUMING_FROM_FJORD.md +0 -123
  95. package/docs/FJORD_LIVE_QA.md +0 -87
  96. package/docs/RELEASE_CHECKLIST.md +0 -55
@@ -0,0 +1,331 @@
1
+ import { createHash } from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { mnemonicToAccount } from 'viem/accounts';
6
+ import { extensionManifestVersion } from './metamask-extension.js';
7
+ import { launchRealWalletExtension, readExtensionManifest, } from './real-wallet-extension.js';
8
+ import { passwordForSetup } from './real-wallet-setup.js';
9
+ import { launchRealWallet, resolveRealWalletProfile, } from './real-wallet.js';
10
+ const READY_MARKER = '.web3-tester-profile-ready';
11
+ const METAMASK_PROFILE_CACHE_SCHEMA = 2;
12
+ // Chromium singleton artifacts must never travel with a cloned profile.
13
+ const SINGLETON_FILES = ['SingletonLock', 'SingletonCookie', 'SingletonSocket'];
14
+ // Regenerable Chromium cache subtrees dominate a cached profile's size;
15
+ // pruning them before the copy makes clones ~3x faster and ~75% smaller.
16
+ // 'Local Extension Settings' (the MetaMask vault) and 'IndexedDB' (13.x
17
+ // debounced extension state) must always travel — never list them here.
18
+ const SKIP_CLONE_DIRS = new Set([
19
+ 'Cache',
20
+ 'Code Cache',
21
+ 'DawnGraphiteCache',
22
+ 'DawnWebGPUCache',
23
+ 'GPUCache',
24
+ 'GraphiteDawnCache',
25
+ 'GrShaderCache',
26
+ 'Service Worker',
27
+ 'ShaderCache',
28
+ 'component_crx_cache',
29
+ 'extensions_crx_cache',
30
+ ]);
31
+ /**
32
+ * Polls the profile's extension storage (IndexedDB leveldb + blob and Local
33
+ * Extension Settings) from Node until a write newer than `since` lands and
34
+ * the directories stay quiet for `quietMs` (default 1500ms — 500ms of margin
35
+ * above MetaMask 13.x's verified 1000ms OperationSafener debounce). Times
36
+ * out silently; the customize-persistence smoke test is the regression trap
37
+ * for a missed flush.
38
+ */
39
+ export async function waitForExtensionStatePersisted(profileDir, extensionId, options = {}) {
40
+ const since = options.since ?? Date.now();
41
+ const quietMs = options.quietMs ?? 1_500;
42
+ const timeoutMs = options.timeoutMs ?? 15_000;
43
+ // A consumer profileDir can itself be a Chrome "Default"/"Profile N"
44
+ // directory; resolve the actual profile root the same way launch does.
45
+ const profile = resolveRealWalletProfile(profileDir);
46
+ const root = profile.profileDirectory
47
+ ? path.join(profile.userDataDir, profile.profileDirectory)
48
+ : path.join(profile.userDataDir, 'Default');
49
+ const watched = [
50
+ path.join(root, 'IndexedDB', `chrome-extension_${extensionId}_0.indexeddb.leveldb`),
51
+ path.join(root, 'IndexedDB', `chrome-extension_${extensionId}_0.indexeddb.blob`),
52
+ path.join(root, 'Local Extension Settings', extensionId),
53
+ ];
54
+ const newestWrite = () => {
55
+ let newest = 0;
56
+ for (const dir of watched) {
57
+ let entries;
58
+ try {
59
+ entries = fs.readdirSync(dir);
60
+ }
61
+ catch {
62
+ continue;
63
+ }
64
+ for (const entry of entries) {
65
+ try {
66
+ const mtime = fs.statSync(path.join(dir, entry)).mtimeMs;
67
+ if (mtime > newest)
68
+ newest = mtime;
69
+ }
70
+ catch {
71
+ // File rotated away mid-scan.
72
+ }
73
+ }
74
+ }
75
+ return newest;
76
+ };
77
+ const deadline = Date.now() + timeoutMs;
78
+ let lastSeen = 0;
79
+ let quietSince = 0;
80
+ while (Date.now() < deadline) {
81
+ const newest = newestWrite();
82
+ if (newest > since) {
83
+ if (newest !== lastSeen) {
84
+ lastSeen = newest;
85
+ quietSince = Date.now();
86
+ }
87
+ else if (Date.now() - quietSince >= quietMs) {
88
+ return;
89
+ }
90
+ }
91
+ await sleep(250);
92
+ }
93
+ }
94
+ export const defaultProfileCacheDir = () => path.join(os.homedir(), '.cache', 'web3-tester', 'profiles');
95
+ const cacheKey = (options) => createHash('sha256')
96
+ .update(JSON.stringify({
97
+ cacheSchema: METAMASK_PROFILE_CACHE_SCHEMA,
98
+ seedPhrase: options.setup.seedPhrase,
99
+ password: passwordForSetup(options.setup),
100
+ extensionVersion: extensionManifestVersion(options.extensionPath),
101
+ customizeKey: options.customize?.key,
102
+ }))
103
+ .digest('hex')
104
+ .slice(0, 16);
105
+ const extensionProfileCacheKey = (options) => {
106
+ const manifest = readExtensionManifest(options.extensionPath);
107
+ const userKey = options.cacheKey.trim();
108
+ if (!userKey) {
109
+ throw new Error('buildWalletExtensionProfile requires a non-empty cacheKey.');
110
+ }
111
+ return createHash('sha256')
112
+ .update(JSON.stringify({
113
+ kind: 'real-wallet-extension',
114
+ extensionPath: path.resolve(options.extensionPath),
115
+ extensionId: options.extensionId,
116
+ extensionName: options.extensionName,
117
+ manifestName: manifest.name,
118
+ manifestVersion: manifest.version,
119
+ initialPage: options.initialPage,
120
+ userKey,
121
+ }))
122
+ .digest('hex')
123
+ .slice(0, 16);
124
+ };
125
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
126
+ const buildCachedProfile = async (options) => {
127
+ const cacheDir = options.cacheDir ?? defaultProfileCacheDir();
128
+ const profileDir = path.join(cacheDir, options.cacheKey);
129
+ const marker = path.join(profileDir, READY_MARKER);
130
+ if (options.force) {
131
+ fs.rmSync(profileDir, { recursive: true, force: true });
132
+ }
133
+ else if (fs.existsSync(marker)) {
134
+ return profileDir;
135
+ }
136
+ // mkdir is atomic: exactly one concurrent builder wins the lock; the rest
137
+ // wait for the ready marker. Locks left behind by killed builders are
138
+ // considered stale after 5 minutes and stolen.
139
+ const lockDir = `${profileDir}.lock`;
140
+ const lockIsStale = () => {
141
+ try {
142
+ return Date.now() - fs.statSync(lockDir).mtimeMs > 300_000;
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ };
148
+ fs.mkdirSync(cacheDir, { recursive: true });
149
+ try {
150
+ fs.mkdirSync(lockDir);
151
+ }
152
+ catch {
153
+ if (lockIsStale()) {
154
+ fs.rmSync(lockDir, { recursive: true, force: true });
155
+ return buildCachedProfile(options);
156
+ }
157
+ // Keep waiting as long as the builder's lock stays fresh — the builder
158
+ // heartbeats the lock every 60s precisely because setup/customize builds
159
+ // can run well past the 5-minute stale threshold. A fixed deadline
160
+ // shorter than that would abandon a healthy builder. The hard ceiling
161
+ // only backstops a hang that somehow keeps the lock fresh without
162
+ // finishing.
163
+ const deadline = Date.now() + 1_800_000;
164
+ while (Date.now() < deadline) {
165
+ if (fs.existsSync(marker)) {
166
+ return profileDir;
167
+ }
168
+ if (!fs.existsSync(lockDir) || lockIsStale()) {
169
+ fs.rmSync(lockDir, { recursive: true, force: true });
170
+ return buildCachedProfile(options);
171
+ }
172
+ await sleep(250);
173
+ }
174
+ throw new Error(`Timed out waiting for another process to finish building the ${options.label} at ${profileDir}.`);
175
+ }
176
+ const heartbeat = setInterval(() => {
177
+ try {
178
+ const now = new Date();
179
+ fs.utimesSync(lockDir, now, now);
180
+ }
181
+ catch {
182
+ // Lock already gone — the finally below is about to run anyway.
183
+ }
184
+ }, 60_000);
185
+ heartbeat.unref?.();
186
+ try {
187
+ fs.rmSync(profileDir, { recursive: true, force: true });
188
+ fs.mkdirSync(profileDir, { recursive: true });
189
+ await options.build(profileDir);
190
+ fs.writeFileSync(marker, JSON.stringify({ createdAt: new Date().toISOString() }));
191
+ return profileDir;
192
+ }
193
+ catch (error) {
194
+ fs.rmSync(profileDir, { recursive: true, force: true });
195
+ throw error;
196
+ }
197
+ finally {
198
+ clearInterval(heartbeat);
199
+ fs.rmSync(lockDir, { recursive: true, force: true });
200
+ }
201
+ };
202
+ /**
203
+ * Builds (once) and returns a cached, fully onboarded MetaMask profile
204
+ * directory keyed by (seed phrase, password, extension version). The first
205
+ * call walks MetaMask onboarding in a real browser; subsequent calls return
206
+ * instantly. Use cloneWalletProfile to obtain a disposable per-test copy —
207
+ * never launch the cached directory directly.
208
+ */
209
+ export async function buildWalletProfile(options) {
210
+ const seedPhrase = options.setup.seedPhrase;
211
+ if (!seedPhrase) {
212
+ throw new Error('buildWalletProfile requires setup.seedPhrase.');
213
+ }
214
+ return buildCachedProfile({
215
+ cacheDir: options.cacheDir,
216
+ cacheKey: cacheKey(options),
217
+ force: options.force,
218
+ label: 'wallet profile',
219
+ build: async (profileDir) => {
220
+ const onboardStartedAt = Date.now();
221
+ const session = await launchRealWallet({
222
+ extensionPath: options.extensionPath,
223
+ profileDir,
224
+ setup: options.setup,
225
+ headless: options.headless,
226
+ generation: options.generation,
227
+ // Verifying against the seed-derived address guarantees the build
228
+ // actually imported a wallet — a silently skipped onboarding would
229
+ // otherwise produce a cached profile with no vault.
230
+ expectedAddress: mnemonicToAccount(seedPhrase).address,
231
+ });
232
+ try {
233
+ // 13.x flushes extension state through a debounced write (safe-reload's
234
+ // OperationSafener, a 1000ms trailing-edge debounce around
235
+ // persistenceManager.set()); wait for the post-onboarding (and, when
236
+ // customizing, post-mutation) write to land and stay quiet before
237
+ // closing — a missed flush silently replays onboarding or loses the
238
+ // customization.
239
+ if (options.customize) {
240
+ const mutationsStartedAt = Date.now();
241
+ await options.customize.run(session);
242
+ await waitForExtensionStatePersisted(profileDir, session.extensionId, {
243
+ since: mutationsStartedAt,
244
+ timeoutMs: 20_000,
245
+ });
246
+ }
247
+ else {
248
+ await waitForExtensionStatePersisted(profileDir, session.extensionId, {
249
+ since: onboardStartedAt,
250
+ timeoutMs: 15_000,
251
+ });
252
+ }
253
+ }
254
+ finally {
255
+ await session.close();
256
+ }
257
+ },
258
+ });
259
+ }
260
+ /**
261
+ * Builds (once) and returns a cached profile for any unpacked Chromium wallet
262
+ * extension. The caller owns wallet-specific onboarding/unlock selectors in
263
+ * `setup.run`; this helper only supplies the persistent profile, extension
264
+ * launch, cache locking, ready marker, and optional extension-state flush.
265
+ * Use cloneWalletProfile to obtain disposable per-test copies.
266
+ */
267
+ export async function buildWalletExtensionProfile(options) {
268
+ return buildCachedProfile({
269
+ cacheDir: options.cacheDir,
270
+ cacheKey: extensionProfileCacheKey(options),
271
+ force: options.force,
272
+ label: 'wallet extension profile',
273
+ build: async (profileDir) => {
274
+ const session = await launchRealWalletExtension({
275
+ baseURL: options.baseURL,
276
+ extensionId: options.extensionId,
277
+ extensionName: options.extensionName,
278
+ extensionPath: options.extensionPath,
279
+ headless: options.headless,
280
+ initialPage: options.initialPage,
281
+ launchArgs: options.launchArgs,
282
+ locale: options.locale,
283
+ profileDir,
284
+ slowMo: options.slowMo,
285
+ });
286
+ try {
287
+ if (options.setup) {
288
+ const setupStartedAt = Date.now();
289
+ await options.setup.run(session);
290
+ const waitForState = options.waitForState ?? true;
291
+ if (waitForState) {
292
+ await waitForExtensionStatePersisted(profileDir, session.extensionId, {
293
+ since: setupStartedAt,
294
+ ...(typeof waitForState === 'object' ? waitForState : {}),
295
+ });
296
+ }
297
+ }
298
+ else if (options.waitForState && typeof options.waitForState === 'object') {
299
+ await waitForExtensionStatePersisted(profileDir, session.extensionId, options.waitForState);
300
+ }
301
+ else if (options.waitForState === true) {
302
+ await waitForExtensionStatePersisted(profileDir, session.extensionId);
303
+ }
304
+ }
305
+ finally {
306
+ await session.close();
307
+ }
308
+ },
309
+ });
310
+ }
311
+ /**
312
+ * Copies a cached profile into a disposable directory (per test or per
313
+ * worker) so parallel runs never collide on Chromium's profile singleton
314
+ * lock and tests cannot dirty the cache.
315
+ */
316
+ export async function cloneWalletProfile(cachedProfileDir, targetDir) {
317
+ if (!fs.existsSync(path.join(cachedProfileDir, READY_MARKER))) {
318
+ throw new Error(`${cachedProfileDir} is not a completed web3-tester profile cache (missing ${READY_MARKER}).`);
319
+ }
320
+ fs.rmSync(targetDir, { recursive: true, force: true });
321
+ fs.cpSync(cachedProfileDir, targetDir, {
322
+ recursive: true,
323
+ // Returning false for a directory prunes its whole subtree.
324
+ filter: (source) => !SKIP_CLONE_DIRS.has(path.basename(source)),
325
+ });
326
+ for (const file of [READY_MARKER, ...SINGLETON_FILES]) {
327
+ fs.rmSync(path.join(targetDir, file), { force: true });
328
+ }
329
+ return targetDir;
330
+ }
331
+ //# sourceMappingURL=real-wallet-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"real-wallet-cache.js","sourceRoot":"","sources":["../src/real-wallet-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EACL,yBAAyB,EACzB,qBAAqB,GAGtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GAIzB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAClD,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,wEAAwE;AACxE,MAAM,eAAe,GAAG,CAAC,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAEhF,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,wEAAwE;AACxE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,OAAO;IACP,YAAY;IACZ,mBAAmB;IACnB,iBAAiB;IACjB,UAAU;IACV,mBAAmB;IACnB,eAAe;IACf,gBAAgB;IAChB,aAAa;IACb,qBAAqB;IACrB,sBAAsB;CACvB,CAAC,CAAC;AA4DH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,UAAkB,EAClB,WAAmB,EACnB,UAAoE,EAAE;IAEtE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAE9C,qEAAqE;IACrE,uEAAuE;IACvE,MAAM,OAAO,GAAG,wBAAwB,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,gBAAgB;QACnC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,gBAAgB,CAAC;QAC1D,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAE9C,MAAM,OAAO,GAAG;QACd,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,oBAAoB,WAAW,sBAAsB,CAAC;QACnF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,oBAAoB,WAAW,mBAAmB,CAAC;QAChF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,0BAA0B,EAAE,WAAW,CAAC;KACzD,CAAC;IAEF,MAAM,WAAW,GAAG,GAAW,EAAE;QAC/B,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,OAAiB,CAAC;YACtB,IAAI,CAAC;gBACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;oBACzD,IAAI,KAAK,GAAG,MAAM;wBAAE,MAAM,GAAG,KAAK,CAAC;gBACrC,CAAC;gBAAC,MAAM,CAAC;oBACP,8BAA8B;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;QAC7B,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;YACnB,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACxB,QAAQ,GAAG,MAAM,CAAC;gBAClB,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC1B,CAAC;iBAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,IAAI,OAAO,EAAE,CAAC;gBAC9C,OAAO;YACT,CAAC;QACH,CAAC;QACD,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAW,EAAE,CACjD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;AAE/D,MAAM,QAAQ,GAAG,CAAC,OAAkC,EAAU,EAAE,CAC9D,UAAU,CAAC,QAAQ,CAAC;KACjB,MAAM,CACL,IAAI,CAAC,SAAS,CAAC;IACb,WAAW,EAAE,6BAA6B;IAC1C,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU;IACpC,QAAQ,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC;IACzC,gBAAgB,EAAE,wBAAwB,CAAC,OAAO,CAAC,aAAa,CAAC;IACjE,YAAY,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG;CACrC,CAAC,CACH;KACA,MAAM,CAAC,KAAK,CAAC;KACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAElB,MAAM,wBAAwB,GAAG,CAAC,OAA2C,EAAU,EAAE;IACvF,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CACL,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE,uBAAuB;QAC7B,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAClD,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,YAAY,EAAE,QAAQ,CAAC,IAAI;QAC3B,eAAe,EAAE,QAAQ,CAAC,OAAO;QACjC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO;KACR,CAAC,CACH;SACA,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhF,MAAM,kBAAkB,GAAG,KAAK,EAAE,OAMjC,EAAmB,EAAE;IACpB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,EAAE,CAAC;IAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAEnD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;SAAM,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,0EAA0E;IAC1E,sEAAsE;IACtE,+CAA+C;IAC/C,MAAM,OAAO,GAAG,GAAG,UAAU,OAAO,CAAC;IACrC,MAAM,WAAW,GAAG,GAAG,EAAE;QACvB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IAEF,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,WAAW,EAAE,EAAE,CAAC;YAClB,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QAED,uEAAuE;QACvE,yEAAyE;QACzE,mEAAmE;QACnE,sEAAsE;QACtE,kEAAkE;QAClE,aAAa;QACb,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC7B,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1B,OAAO,UAAU,CAAC;YACpB,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,WAAW,EAAE,EAAE,CAAC;gBAC7C,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrD,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;YACD,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,gEAAgE,OAAO,CAAC,KAAK,OAAO,UAAU,GAAG,CAClG,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC,EAAE,MAAM,CAAC,CAAC;IACX,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;IAEpB,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;QAClF,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,aAAa,CAAC,SAAS,CAAC,CAAC;QACzB,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAkC;IACzE,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;IAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,kBAAkB,CAAC;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC;QAC3B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;YAC1B,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC;gBACrC,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,UAAU;gBACV,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,kEAAkE;gBAClE,mEAAmE;gBACnE,oDAAoD;gBACpD,eAAe,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,OAAO;aACvD,CAAC,CAAC;YAEH,IAAI,CAAC;gBACH,wEAAwE;gBACxE,2DAA2D;gBAC3D,qEAAqE;gBACrE,kEAAkE;gBAClE,oEAAoE;gBACpE,iBAAiB;gBACjB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBACtC,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACrC,MAAM,8BAA8B,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,EAAE;wBACpE,KAAK,EAAE,kBAAkB;wBACzB,SAAS,EAAE,MAAM;qBAClB,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,8BAA8B,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,EAAE;wBACpE,KAAK,EAAE,gBAAgB;wBACvB,SAAS,EAAE,MAAM;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,OAA2C;IAE3C,OAAO,kBAAkB,CAAC;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE,wBAAwB,CAAC,OAAO,CAAC;QAC3C,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,0BAA0B;QACjC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;YAC1B,MAAM,OAAO,GAAG,MAAM,yBAAyB,CAAC;gBAC9C,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,UAAU;gBACV,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;YAEH,IAAI,CAAC;gBACH,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBAClC,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACjC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC;oBAClD,IAAI,YAAY,EAAE,CAAC;wBACjB,MAAM,8BAA8B,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,EAAE;4BACpE,KAAK,EAAE,cAAc;4BACrB,GAAG,CAAC,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;yBAC1D,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;qBAAM,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;oBAC5E,MAAM,8BAA8B,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;gBAC9F,CAAC;qBAAM,IAAI,OAAO,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;oBACzC,MAAM,8BAA8B,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;gBACxE,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,gBAAwB,EACxB,SAAiB;IAEjB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CACb,GAAG,gBAAgB,0DAA0D,YAAY,IAAI,CAC9F,CAAC;IACJ,CAAC;IAED,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,EAAE,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,EAAE;QACrC,SAAS,EAAE,IAAI;QACf,4DAA4D;QAC5D,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;KAChE,CAAC,CAAC;IAEH,KAAK,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,GAAG,eAAe,CAAC,EAAE,CAAC;QACtD,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type BuildWalletExtensionProfileOptions } from './real-wallet-cache.js';
2
+ import { type RealWalletExtensionLaunchOptions, type RealWalletExtensionSession } from './real-wallet-extension.js';
3
+ export type RealWalletExtensionFixtureOptions = Omit<RealWalletExtensionLaunchOptions, 'extensionPath' | 'profileDir'> & {
4
+ /** Unpacked Chromium extension directory. Defaults to WEB3_TESTER_REAL_WALLET_EXTENSION_PATH. */
5
+ extensionPath?: string;
6
+ /**
7
+ * Explicit persistent profile directory. Disables the profile cache; the
8
+ * test reuses (and mutates) this profile directly, so parallel tests must
9
+ * not share it.
10
+ */
11
+ profileDir?: string;
12
+ /** Cache identity for non-MetaMask setup. Required when profileDir is absent. */
13
+ profileCacheKey?: string;
14
+ /** Directory cached profiles live in. Defaults to ~/.cache/web3-tester/profiles. */
15
+ cacheDir?: string;
16
+ /** Rebuild the cached profile even if it already exists. */
17
+ forceProfile?: boolean;
18
+ /** One-time extension setup baked into the cached profile. */
19
+ profileSetup?: BuildWalletExtensionProfileOptions['setup'];
20
+ /** Wait policy after profileSetup. Defaults to buildWalletExtensionProfile's behavior. */
21
+ waitForState?: BuildWalletExtensionProfileOptions['waitForState'];
22
+ };
23
+ export type RealWalletExtensionFixtures = {
24
+ realWalletExtensionOptions: RealWalletExtensionFixtureOptions;
25
+ realWalletExtension: RealWalletExtensionSession;
26
+ };
27
+ /**
28
+ * Playwright fixtures for arbitrary Chromium wallet extensions. Each test
29
+ * gets either the explicit profileDir or a disposable clone of a cached
30
+ * profile prepared by profileSetup. `context` and `page` are rebound to the
31
+ * persistent extension context.
32
+ */
33
+ export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & RealWalletExtensionFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
34
+ export { expect } from './matchers.js';
35
+ //# sourceMappingURL=real-wallet-extension-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"real-wallet-extension-fixtures.d.ts","sourceRoot":"","sources":["../src/real-wallet-extension-fixtures.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,kCAAkC,EACxC,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAEL,KAAK,gCAAgC,EACrC,KAAK,0BAA0B,EAChC,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAClD,gCAAgC,EAChC,eAAe,GAAG,YAAY,CAC/B,GAAG;IACF,iGAAiG;IACjG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,kCAAkC,CAAC,OAAO,CAAC,CAAC;IAC3D,0FAA0F;IAC1F,YAAY,CAAC,EAAE,kCAAkC,CAAC,cAAc,CAAC,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,0BAA0B,EAAE,iCAAiC,CAAC;IAC9D,mBAAmB,EAAE,0BAA0B,CAAC;CACjD,CAAC;AAkBF;;;;;GAKG;AACH,eAAO,MAAM,IAAI,2QA+Ff,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,96 @@
1
+ import path from 'node:path';
2
+ import { test as base } from '@playwright/test';
3
+ import { buildWalletExtensionProfile, cloneWalletProfile, } from './real-wallet-cache.js';
4
+ import { benchmarkForTest, benchmarkObjectMethods } from './benchmark.js';
5
+ import { launchRealWalletExtension, } from './real-wallet-extension.js';
6
+ const extensionPathFromEnv = () => process.env.WEB3_TESTER_REAL_WALLET_EXTENSION_PATH || undefined;
7
+ const profileDirFromEnv = () => process.env.WEB3_TESTER_REAL_WALLET_PROFILE_DIR || undefined;
8
+ const assertChromiumProject = (browserName) => {
9
+ if (browserName !== 'chromium') {
10
+ throw new Error('@marigoldlabs/web3-tester/real-wallet-extension-fixtures launch a Chromium extension context. ' +
11
+ `The current Playwright project uses "${browserName}". Exclude real-extension tests from non-Chromium ` +
12
+ 'projects, or run them in a dedicated Chromium project.');
13
+ }
14
+ };
15
+ /**
16
+ * Playwright fixtures for arbitrary Chromium wallet extensions. Each test
17
+ * gets either the explicit profileDir or a disposable clone of a cached
18
+ * profile prepared by profileSetup. `context` and `page` are rebound to the
19
+ * persistent extension context.
20
+ */
21
+ export const test = base.extend({
22
+ realWalletExtensionOptions: [
23
+ async ({}, use) => {
24
+ await use({});
25
+ },
26
+ { option: true },
27
+ ],
28
+ realWalletExtension: async ({ realWalletExtensionOptions, browserName }, use, testInfo) => {
29
+ assertChromiumProject(browserName);
30
+ const benchmark = benchmarkForTest(testInfo, { suite: 'real-wallet-extension' });
31
+ let session;
32
+ try {
33
+ const options = realWalletExtensionOptions;
34
+ const extensionPath = options.extensionPath ?? extensionPathFromEnv();
35
+ if (!extensionPath) {
36
+ throw new Error('Real-wallet extension fixtures need extensionPath or ' +
37
+ 'WEB3_TESTER_REAL_WALLET_EXTENSION_PATH pointing at an unpacked Chromium wallet extension.');
38
+ }
39
+ let profileDir = options.profileDir ?? profileDirFromEnv();
40
+ if (!profileDir) {
41
+ if (!options.profileCacheKey) {
42
+ throw new Error('Real-wallet extension fixtures need profileCacheKey to build a cached profile, ' +
43
+ 'or an explicit profileDir pointing at a prepared wallet profile.');
44
+ }
45
+ const profileCacheKey = options.profileCacheKey;
46
+ const cachedProfile = await benchmark.measure('realWalletExtension.buildProfile', () => buildWalletExtensionProfile({
47
+ baseURL: options.baseURL,
48
+ cacheDir: options.cacheDir,
49
+ cacheKey: profileCacheKey,
50
+ extensionId: options.extensionId,
51
+ extensionName: options.extensionName,
52
+ extensionPath,
53
+ force: options.forceProfile,
54
+ headless: options.headless,
55
+ initialPage: options.initialPage,
56
+ launchArgs: options.launchArgs,
57
+ locale: options.locale,
58
+ setup: options.profileSetup,
59
+ slowMo: options.slowMo,
60
+ waitForState: options.waitForState,
61
+ }));
62
+ profileDir = await benchmark.measure('realWalletExtension.cloneProfile', () => cloneWalletProfile(cachedProfile, path.join(testInfo.outputDir, 'wallet-extension-profile')));
63
+ }
64
+ session = await benchmark.measure('realWalletExtension.launch', () => launchRealWalletExtension({
65
+ baseURL: options.baseURL,
66
+ extensionId: options.extensionId,
67
+ extensionName: options.extensionName,
68
+ extensionPath,
69
+ headless: options.headless,
70
+ initialPage: options.initialPage,
71
+ launchArgs: options.launchArgs,
72
+ locale: options.locale,
73
+ profileDir,
74
+ slowMo: options.slowMo,
75
+ }));
76
+ await use(benchmarkObjectMethods(session, benchmark, { prefix: 'realWalletExtension', exclude: ['close'] }));
77
+ }
78
+ finally {
79
+ if (session) {
80
+ const activeSession = session;
81
+ await benchmark.measure('realWalletExtension.close', () => activeSession.close().catch(() => undefined));
82
+ }
83
+ await benchmark.flush();
84
+ }
85
+ },
86
+ context: async ({ realWalletExtension }, use) => {
87
+ await use(realWalletExtension.context);
88
+ },
89
+ page: async ({ context }, use) => {
90
+ const page = await context.newPage();
91
+ await use(page);
92
+ await page.close().catch(() => undefined);
93
+ },
94
+ });
95
+ export { expect } from './matchers.js';
96
+ //# sourceMappingURL=real-wallet-extension-fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"real-wallet-extension-fixtures.js","sourceRoot":"","sources":["../src/real-wallet-extension-fixtures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { type BrowserContext, type Page } from '@playwright/test';
2
+ export type RealWalletProfile = {
3
+ profileDirectory?: string;
4
+ userDataDir: string;
5
+ };
6
+ export type BrowserExtensionManifest = {
7
+ manifest_version?: number;
8
+ name?: string;
9
+ version?: string;
10
+ action?: {
11
+ default_popup?: string;
12
+ };
13
+ browser_action?: {
14
+ default_popup?: string;
15
+ };
16
+ options_ui?: {
17
+ page?: string;
18
+ };
19
+ side_panel?: {
20
+ default_path?: string;
21
+ };
22
+ [key: string]: unknown;
23
+ };
24
+ export type RealWalletExtensionLaunchOptions = {
25
+ baseURL?: string;
26
+ /**
27
+ * Exact Chrome extension display name used when runtime/service-worker
28
+ * discovery is inconclusive. For localized manifests (`__MSG_*__`), pass
29
+ * this explicitly or pass `extensionId`.
30
+ */
31
+ extensionName?: string;
32
+ /**
33
+ * Known extension ID. Useful for persistent profiles that already have a
34
+ * wallet installed or for extensions without a service worker.
35
+ */
36
+ extensionId?: string;
37
+ /** Path to an unpacked Chrome extension directory. */
38
+ extensionPath: string;
39
+ /**
40
+ * Run the browser headless. There is deliberately no default — choose
41
+ * explicitly here or via WEB3_TESTER_REAL_WALLET_HEADLESS=true|false.
42
+ */
43
+ headless?: boolean;
44
+ /**
45
+ * Extension page to open after launch. Defaults to the manifest action
46
+ * popup/options/side-panel page when present; pass false to leave all
47
+ * extension pages untouched.
48
+ */
49
+ initialPage?: string | false;
50
+ /** Additional Chromium launch args appended after extension args. */
51
+ launchArgs?: readonly string[];
52
+ locale?: string;
53
+ profileDir: string;
54
+ slowMo?: number;
55
+ };
56
+ export type RealWalletExtensionSession = {
57
+ close(): Promise<void>;
58
+ context: BrowserContext;
59
+ extensionId: string;
60
+ extensionPath: string;
61
+ manifest: BrowserExtensionManifest;
62
+ /** Page opened from `initialPage`, when configured/resolved. */
63
+ page?: Page;
64
+ profile: RealWalletProfile;
65
+ extensionUrl(page?: string): string;
66
+ openPage(page?: string): Promise<Page>;
67
+ };
68
+ export declare function resolveRealWalletProfile(profileDir: string): RealWalletProfile;
69
+ /**
70
+ * Resolves the headed/headless choice for real-wallet extension launches.
71
+ * The explicit option wins over the environment.
72
+ */
73
+ export declare function resolveRealWalletHeadless(explicit?: boolean): boolean;
74
+ export declare function readExtensionManifest(extensionPath: string): BrowserExtensionManifest;
75
+ export declare function extensionManifestName(extensionPath: string): string | undefined;
76
+ export declare function extensionManifestDefaultPage(manifest: BrowserExtensionManifest): string | undefined;
77
+ export declare function extensionPageUrl(extensionId: string, page?: string): string;
78
+ export declare function resolveExtensionPageUrl(extensionId: string, page?: string): string;
79
+ export declare function extensionIdFromUrl(url: string): string | undefined;
80
+ export declare function discoverRealWalletExtensionId(context: BrowserContext, options?: {
81
+ extensionName?: string;
82
+ timeoutMs?: number;
83
+ }): Promise<string>;
84
+ export declare function openRealWalletExtensionPage(context: BrowserContext, extensionId: string, page?: string): Promise<Page>;
85
+ export declare function launchRealWalletExtension(options: RealWalletExtensionLaunchOptions): Promise<RealWalletExtensionSession>;
86
+ //# sourceMappingURL=real-wallet-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"real-wallet-extension.d.ts","sourceRoot":"","sources":["../src/real-wallet-extension.ts"],"names":[],"mappings":"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"}