@avalix/chroma 0.0.11 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  End-to-end testing library for Polkadot wallet interactions using Playwright.
4
4
 
5
- > **⚠️ Active Development**: This library is currently under active development. The API may change and breaking changes can occur between versions. Please pin your version and review changelogs carefully when updating.
6
-
7
5
  ## Installation
8
6
 
9
7
  ```bash
@@ -27,7 +25,7 @@ This will download the wallet extensions (Polkadot JS and Talisman) to `./.chrom
27
25
  ```json
28
26
  {
29
27
  "scripts": {
30
- "prepare": "chroma download-extensions"
28
+ "test:prepare": "chroma download-extensions"
31
29
  }
32
30
  }
33
31
  ```
@@ -36,258 +34,60 @@ This will download the wallet extensions (Polkadot JS and Talisman) to `./.chrom
36
34
 
37
35
  ## Quick Start
38
36
 
39
- ### Basic Usage
40
-
41
37
  ```typescript
42
- import { expect, test } from '@avalix/chroma'
38
+ import { createWalletTest, expect } from '@avalix/chroma'
43
39
 
44
- test('should connect wallet and sign transaction', async ({ page, wallets }) => {
40
+ const test = createWalletTest({
41
+ wallets: [{ type: 'polkadot-js' }]
42
+ })
43
+
44
+ test('connect wallet and sign transaction', async ({ page, wallets }) => {
45
45
  const polkadotJs = wallets['polkadot-js']
46
46
 
47
- // Import a test account
48
47
  await polkadotJs.importMnemonic({
49
48
  seed: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk',
50
49
  name: 'Test Account',
51
50
  password: 'securePassword123'
52
51
  })
53
52
 
54
- // Navigate to your dApp
55
53
  await page.goto('http://localhost:3000')
56
-
57
- // Connect wallet
58
54
  await page.click('button:has-text("Connect Wallet")')
59
55
  await polkadotJs.authorize()
60
56
 
61
- // Perform transaction
62
57
  await page.click('button:has-text("Send Transaction")')
63
58
  await polkadotJs.approveTx({ password: 'securePassword123' })
64
59
 
65
- // Verify transaction success
66
60
  await expect(page.locator('.transaction-success')).toBeVisible()
67
61
  })
68
62
  ```
69
63
 
70
- ### Custom Configuration
71
-
72
- ```typescript
73
- import { createWalletTest, expect } from '@avalix/chroma'
74
-
75
- const customTest = createWalletTest({
76
- wallets: [{ type: 'polkadot-js' }],
77
- headless: false,
78
- slowMo: 100
79
- })
80
-
81
- customTest('test with custom config', async ({ page, wallets }) => {
82
- const polkadotJs = wallets['polkadot-js']
83
-
84
- await polkadotJs.importMnemonic({
85
- seed: 'your seed phrase here...',
86
- name: 'My Test Account'
87
- })
88
- await page.goto('http://localhost:3000')
89
- await polkadotJs.authorize()
90
- })
91
- ```
92
-
93
64
  ### Multiple Wallets
94
65
 
95
66
  ```typescript
96
- import { createWalletTest, expect } from '@avalix/chroma'
67
+ import { createWalletTest } from '@avalix/chroma'
97
68
 
98
- // Test with multiple wallet extensions
99
- const multiWalletTest = createWalletTest({
100
- wallets: [
101
- { type: 'polkadot-js' },
102
- { type: 'talisman' }
103
- ],
104
- headless: false,
105
- slowMo: 150
69
+ const test = createWalletTest({
70
+ wallets: [{ type: 'polkadot-js' }, { type: 'talisman' }]
106
71
  })
107
72
 
108
- multiWalletTest('test with multiple wallets', async ({ page, wallets }) => {
73
+ test('multi-wallet test', async ({ page, wallets }) => {
109
74
  const polkadotJs = wallets['polkadot-js']
110
75
  const talisman = wallets.talisman
111
76
 
112
- // Import to Polkadot JS
113
- await polkadotJs.importMnemonic({
114
- seed: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk',
115
- name: 'Alice'
116
- })
117
-
118
- // Import to Talisman using Ethereum private key
119
- await talisman.importEthPrivateKey({
120
- privateKey: '0x...',
121
- name: 'Bob'
122
- })
77
+ await polkadotJs.importMnemonic({ seed: '...', name: 'Alice' })
78
+ await talisman.importEthPrivateKey({ privateKey: '0x...', name: 'Bob' })
123
79
 
124
80
  await page.goto('http://localhost:3000')
125
-
126
- // Use specific wallet
127
81
  await polkadotJs.authorize()
128
- await polkadotJs.approveTx()
129
82
  })
130
83
  ```
131
84
 
132
85
  ## Features
133
86
 
134
- - 🔐 **Easy Extension Setup**: Simple command to download wallet extensions
135
- - 🧪 **Test Fixtures**: Ready-to-use Playwright fixtures for wallet operations
136
- - 📝 **Account Management**: Import accounts with seed phrases and custom names
137
- - **Transaction Approval**: Approve transactions with password authentication
138
- - 🔗 **dApp Authorization**: Connect wallet to decentralized applications
139
- - 🔀 **Multi-Wallet Support**: Test with multiple wallet extensions simultaneously
140
- - ⚙️ **Configurable**: Custom extension paths, headless mode, and slow motion settings
141
-
142
- ## API Reference
143
-
144
- ### Core Functions
145
-
146
- #### `test` (Default Test Function)
147
- Pre-configured test function with Polkadot JS extension.
148
-
149
- ```typescript
150
- import { test } from '@avalix/chroma'
151
-
152
- test('my wallet test', async ({ page, wallets }) => {
153
- const polkadotJs = wallets['polkadot-js']
154
-
155
- await polkadotJs.importMnemonic({ seed: '...' })
156
- await polkadotJs.authorize()
157
- })
158
- ```
159
-
160
- #### `createWalletTest(options?: ChromaTestOptions)`
161
- Create a custom test function with specific configuration. Supports single and multi-wallet modes.
162
-
163
- ```typescript
164
- import { createWalletTest } from '@avalix/chroma'
165
-
166
- // Single wallet (default)
167
- const test = createWalletTest()
168
-
169
- // Single wallet with custom config
170
- const customTest = createWalletTest({
171
- wallets: [{ type: 'polkadot-js' }],
172
- headless: false,
173
- slowMo: 150
174
- })
175
-
176
- // Multiple wallets
177
- const multiTest = createWalletTest({
178
- wallets: [
179
- { type: 'polkadot-js' },
180
- { type: 'talisman' }
181
- ]
182
- })
183
-
184
- // Usage
185
- test('example', async ({ page, wallets }) => {
186
- const polkadotJs = wallets['polkadot-js']
187
-
188
- await polkadotJs.importMnemonic({ seed: '...' })
189
- await polkadotJs.authorize()
190
- await polkadotJs.approveTx()
191
- })
192
- ```
193
-
194
- ### Test Fixtures
195
-
196
- #### `page`
197
- Playwright page instance with wallet extension(s) loaded.
198
-
199
- #### `wallets`
200
- Typed object containing wallet instances for each configured wallet. Provides full TypeScript autocomplete.
201
-
202
- ```typescript
203
- // Base wallet instance (common methods)
204
- interface BaseWalletInstance {
205
- extensionId: string
206
- importMnemonic: (options: WalletAccount) => Promise<void>
207
- authorize: (options?: { accountName?: string }) => Promise<void>
208
- approveTx: (options?: { password?: string }) => Promise<void>
209
- }
210
-
211
- // Polkadot-JS wallet instance
212
- interface PolkadotJsWalletInstance extends BaseWalletInstance {
213
- type: 'polkadot-js'
214
- }
215
-
216
- // Talisman wallet instance (with additional methods)
217
- interface TalismanWalletInstance extends BaseWalletInstance {
218
- type: 'talisman'
219
- importEthPrivateKey: (options: { privateKey: string, name?: string, password?: string }) => Promise<void>
220
- }
221
-
222
- // Note: Talisman currently does not support importMnemonic - use importEthPrivateKey instead
223
-
224
- // Wallets collection - each wallet has its specific type
225
- interface Wallets {
226
- 'polkadot-js': PolkadotJsWalletInstance
227
- 'talisman': TalismanWalletInstance
228
- }
229
-
230
- interface WalletAccount {
231
- seed: string
232
- name?: string // Default: 'Test Account'
233
- password?: string // Default: 'h3llop0lkadot!'
234
- }
235
- ```
236
-
237
- **Usage:**
238
-
239
- ```typescript
240
- test('example', async ({ page, wallets }) => {
241
- const polkadotJs = wallets['polkadot-js'] // Type: PolkadotJsWalletInstance
242
-
243
- // Import mnemonic (available on all wallets)
244
- await polkadotJs.importMnemonic({
245
- seed: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk',
246
- name: 'Test Account',
247
- password: 'securePassword123'
248
- })
249
-
250
- await page.goto('http://localhost:3000')
251
- await polkadotJs.authorize()
252
- await polkadotJs.approveTx({ password: 'securePassword123' })
253
- })
254
-
255
- // Talisman-specific features
256
- test('talisman example', async ({ page, wallets }) => {
257
- const talisman = wallets.talisman // Type: TalismanWalletInstance
258
-
259
- // Talisman-specific method: import Ethereum private key
260
- await talisman.importEthPrivateKey({
261
- privateKey: '0x...',
262
- name: 'My Account',
263
- password: 'mypassword'
264
- })
265
-
266
- // Common methods also available
267
- await talisman.authorize({ accountName: 'My Account' })
268
- await talisman.approveTx()
269
- })
270
- ```
271
-
272
- ## Configuration
273
-
274
- ### Extension Download
275
- Run the download command to get the required wallet extensions:
276
-
277
- ```bash
278
- npx chroma download-extensions
279
- ```
280
-
281
- Extensions will be downloaded to `./.chroma` directory in your project root. Add this directory to your `.gitignore`:
282
-
283
- ```gitignore
284
- .chroma/
285
- ```
286
-
287
- ### Browser Settings
288
- - **Headless Mode**: Disabled by default for better debugging
289
- - **Slow Motion**: 150ms delay between actions (configurable)
290
- - **Extension Loading**: Automatically loads configured wallet extensions
87
+ - **Easy Extension Setup** - Download wallet extensions with a single command
88
+ - **Multi-Wallet Support** - Test with multiple wallet extensions simultaneously
89
+ - **TypeScript Support** - Full type safety and autocomplete
90
+ - **VS Code Integration** - Works with [Playwright Test for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
291
91
 
292
92
  ## Supported Chains
293
93
 
@@ -304,20 +104,13 @@ Extensions will be downloaded to `./.chroma` directory in your project root. Add
304
104
  | Polkadot JS Extension | ✅ Supported | v0.62.6 |
305
105
  | Talisman | ✅ Supported | v3.1.13 |
306
106
  | SubWallet | ⏳ Planned | - |
107
+ | MetaMask | ⏳ Planned | - |
307
108
 
308
109
  ## Requirements
309
110
 
310
111
  - Node.js 24+
311
112
  - @playwright/test ^1.55.0
312
113
 
313
- ## Contributing
314
-
315
- This project is in active development. Currently focusing on:
316
- - Polkadot JS Extension and Talisman support
317
- - Core testing fixtures
318
- - Additional wallet integrations
319
- - Documentation improvements
320
-
321
114
  ## License
322
115
 
323
116
  MIT
package/dist/index.d.mts CHANGED
@@ -9923,38 +9923,45 @@ declare namespace test_d_exports {
9923
9923
  export { BlobReporterOptions, Config, Expect, ExpectMatcherState, ExpectMatcherUtils, Fixtures, FullConfig, FullProject, HtmlReporterOptions, JUnitReporterOptions, JsonReporterOptions, ListReporterOptions, Location, MatcherHintOptions, MatcherReturnType, Metadata, PageAssertionsToHaveScreenshotOptions, PlaywrightTestArgs, PlaywrightTestConfig, PlaywrightTestOptions, PlaywrightTestProject, PlaywrightWorkerArgs, PlaywrightWorkerOptions, Project, ReporterDescription, ScreenshotMode, TestAnnotation, TestDetails, TestDetailsAnnotation, TestFixture, TestInfo, TestInfoError, TestStatus, TestStepInfo, TestType, TraceMode, VideoMode, WorkerFixture, WorkerInfo, _baseTest, test$2 as default, defineConfig, expect$1 as expect, mergeExpects, mergeTests, test$2 as test };
9924
9924
  }
9925
9925
  //#endregion
9926
- //#region src/context-playwright/types.d.ts
9927
- type WalletType = 'polkadot-js' | 'talisman';
9928
- interface WalletAccount {
9929
- seed: string;
9930
- name?: string;
9931
- password?: string;
9932
- }
9933
- interface WalletConfig {
9934
- type: WalletType;
9935
- downloadUrl?: string;
9936
- }
9937
- interface BaseWalletInstance {
9926
+ //#region src/context-playwright/wallet-factory.d.ts
9927
+ declare function createPolkadotJsWallet(extensionId: string, context: BrowserContext): {
9938
9928
  extensionId: string;
9929
+ type: "polkadot-js";
9939
9930
  importMnemonic: (options: WalletAccount) => Promise<void>;
9940
- authorize: (options?: {
9941
- accountName?: string;
9942
- }) => Promise<void>;
9931
+ authorize: () => Promise<void>;
9943
9932
  approveTx: (options?: {
9944
9933
  password?: string;
9945
9934
  }) => Promise<void>;
9946
9935
  rejectTx: () => Promise<void>;
9947
- }
9948
- interface PolkadotJsWalletInstance extends BaseWalletInstance {
9949
- type: 'polkadot-js';
9950
- }
9951
- interface TalismanWalletInstance extends BaseWalletInstance {
9952
- type: 'talisman';
9936
+ };
9937
+ declare function createTalismanWallet(extensionId: string, context: BrowserContext): {
9938
+ extensionId: string;
9939
+ type: "talisman";
9940
+ importPolkadotMnemonic: (options: WalletAccount) => Promise<void>;
9953
9941
  importEthPrivateKey: (options: {
9954
9942
  privateKey: string;
9955
9943
  name?: string;
9956
9944
  password?: string;
9957
9945
  }) => Promise<void>;
9946
+ authorize: (options?: {
9947
+ accountName?: string;
9948
+ }) => Promise<void>;
9949
+ approveTx: () => Promise<void>;
9950
+ rejectTx: () => Promise<void>;
9951
+ };
9952
+ type PolkadotJsWalletInstance = ReturnType<typeof createPolkadotJsWallet>;
9953
+ type TalismanWalletInstance = ReturnType<typeof createTalismanWallet>;
9954
+ //#endregion
9955
+ //#region src/context-playwright/types.d.ts
9956
+ type WalletType = 'polkadot-js' | 'talisman';
9957
+ interface WalletAccount {
9958
+ seed: string;
9959
+ name?: string;
9960
+ password?: string;
9961
+ }
9962
+ interface WalletConfig {
9963
+ type: WalletType;
9964
+ downloadUrl?: string;
9958
9965
  }
9959
9966
  interface WalletTypeMap {
9960
9967
  'polkadot-js': PolkadotJsWalletInstance;
package/dist/index.mjs CHANGED
@@ -103,35 +103,63 @@ async function getTalismanExtensionPath() {
103
103
  if (!fs.existsSync(extensionDir) || fs.readdirSync(extensionDir).length === 0) throw new Error(`Talisman extension not found at: ${extensionDir}\n\nPlease download the extension first by running:\n npx @avalix/chroma download-extensions\n`);
104
104
  return extensionDir;
105
105
  }
106
- async function importEthPrivateKey(page, { seed, name = "Test Account", password = "h3llop0lkadot!" }) {
106
+ async function findOnboardingPage(context, extensionId) {
107
+ const popupUrl = `chrome-extension://${extensionId}/dashboard.html`;
108
+ const newPage = await context.newPage();
109
+ await newPage.goto(popupUrl);
110
+ await newPage.waitForLoadState("domcontentloaded");
111
+ for (const p of context.pages()) if (p !== newPage && p.url().includes(`chrome-extension://${extensionId}/`)) await p.close();
112
+ return newPage;
113
+ }
114
+ async function completeOnboarding(extensionPage, password) {
115
+ await extensionPage.bringToFront();
116
+ await extensionPage.waitForLoadState("domcontentloaded");
117
+ if (await extensionPage.getByRole("button", { name: "Settings" }).isVisible()) {
118
+ await extensionPage.getByRole("button", { name: "Settings" }).click({ force: true });
119
+ return;
120
+ }
121
+ await extensionPage.getByTestId("onboarding-get-started-button").click();
122
+ await extensionPage.getByRole("textbox", { name: "Enter password" }).fill(password);
123
+ await extensionPage.getByRole("textbox", { name: "Confirm password" }).fill(password);
124
+ await extensionPage.getByTestId("onboarding-password-confirm-button").click();
125
+ await extensionPage.getByRole("button", { name: "No thanks" }).click();
126
+ await extensionPage.getByTestId("onboarding-enter-talisman-button").click();
127
+ const extensionId = extensionPage.url().match(/chrome-extension:\/\/([^/]+)/)?.[1];
128
+ await extensionPage.goto(`chrome-extension://${extensionId}/dashboard.html#/settings/general`);
129
+ await extensionPage.waitForLoadState("domcontentloaded");
130
+ await extensionPage.getByRole("link", { name: "Security & Privacy" }).click();
131
+ await extensionPage.getByTestId("component-toggle-button").first().click();
132
+ }
133
+ async function importPolkadotMnemonic(page, { seed, name = "Test Account", password = "h3llop0lkadot!" }) {
107
134
  const context = page.__extensionContext;
108
135
  const extensionId = page.__extensionId;
109
- const maxAttempts = 20;
110
- const retryDelay = 500;
111
- let extensionPage = null;
112
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
113
- const pages = context.pages();
114
- for (const p of pages) {
115
- const url = p.url();
116
- if (url.includes("onboarding.html") || url.includes(`chrome-extension://${extensionId}/`)) {
117
- extensionPage = p;
118
- break;
119
- }
120
- }
121
- if (extensionPage) break;
122
- if (attempt < maxAttempts - 1) await new Promise((resolve) => setTimeout(resolve, retryDelay));
136
+ const extensionPage = await findOnboardingPage(context, extensionId);
137
+ try {
138
+ await completeOnboarding(extensionPage, password);
139
+ await extensionPage.getByRole("link", { name: "Manage Accounts" }).click();
140
+ await extensionPage.getByRole("button", { name: "Get Started" }).click();
141
+ await extensionPage.getByRole("button", { name: "Add Account" }).click();
142
+ await extensionPage.getByRole("button", { name: "Import Import an existing" }).click();
143
+ await extensionPage.getByRole("button", { name: "Import via Recovery Phrase" }).click();
144
+ await extensionPage.getByRole("button", { name: "Polkadot Relay Chain, Asset" }).click();
145
+ await extensionPage.getByRole("textbox", { name: "Choose a name" }).fill(name);
146
+ await extensionPage.getByRole("textbox", { name: "Enter your 12 or 24 word" }).fill(seed);
147
+ await extensionPage.getByTestId("account-add-mnemonic-import-button").click();
148
+ await extensionPage.close();
149
+ } catch (error) {
150
+ console.error("❌ Error during Talisman Polkadot account import:", error);
151
+ throw error;
123
152
  }
124
- if (!extensionPage) throw new Error(`Talisman onboarding page not found after ${maxAttempts} attempts`);
153
+ }
154
+ async function importEthPrivateKey(page, { seed, name = "Test Account", password = "h3llop0lkadot!" }) {
155
+ const context = page.__extensionContext;
156
+ const extensionId = page.__extensionId;
157
+ const extensionPage = await findOnboardingPage(context, extensionId);
125
158
  try {
126
- await extensionPage.bringToFront();
127
- await extensionPage.waitForLoadState("domcontentloaded");
128
- await extensionPage.getByTestId("onboarding-get-started-button").click();
129
- await extensionPage.getByRole("textbox", { name: "Enter password" }).fill(password);
130
- await extensionPage.getByRole("textbox", { name: "Confirm password" }).fill(password);
131
- await extensionPage.getByTestId("onboarding-password-confirm-button").click();
132
- await extensionPage.getByRole("button", { name: "No thanks" }).click();
133
- await extensionPage.getByTestId("onboarding-enter-talisman-button").click();
134
- await extensionPage.getByRole("button", { name: "Add account Create or import" }).click();
159
+ await completeOnboarding(extensionPage, password);
160
+ await extensionPage.getByRole("link", { name: "Manage Accounts" }).click();
161
+ await extensionPage.getByRole("button", { name: "Get Started" }).click();
162
+ await extensionPage.getByRole("button", { name: "Add Account" }).click();
135
163
  await extensionPage.getByRole("button", { name: "Import Import an existing" }).click();
136
164
  await extensionPage.getByRole("button", { name: "Import via Private Key" }).click();
137
165
  await extensionPage.getByRole("button", { name: "Select account platform" }).click();
@@ -141,16 +169,20 @@ async function importEthPrivateKey(page, { seed, name = "Test Account", password
141
169
  await extensionPage.getByRole("button", { name: "Save" }).click();
142
170
  await extensionPage.close();
143
171
  } catch (error) {
144
- console.error("❌ Error during Talisman account import:", error);
172
+ console.error("❌ Error during Talisman Ethereum account import:", error);
145
173
  throw error;
146
174
  }
147
175
  }
148
176
  async function authorizeTalisman(page, options = {}) {
149
- const { accountName = "Test Account" } = options;
150
177
  const context = page.__extensionContext;
151
178
  const extensionId = page.__extensionId;
179
+ const { accountName = "Test Account" } = options;
152
180
  const extensionPopup = await findExtensionPopup(context, extensionId);
153
- await extensionPopup.getByRole("button", { name: accountName }).click();
181
+ await extensionPopup.waitForLoadState("domcontentloaded");
182
+ const accountButton = extensionPopup.getByRole("button", { name: accountName });
183
+ await accountButton.waitFor({ state: "visible" });
184
+ await accountButton.scrollIntoViewIfNeeded();
185
+ await accountButton.click({ force: true });
154
186
  await extensionPopup.getByTestId("connection-connect-button").click();
155
187
  try {
156
188
  await (await findExtensionPopup(context, extensionId)).getByRole("button", { name: "Approve" }).click();
@@ -160,23 +192,16 @@ async function approveTalismanTx(page) {
160
192
  const context = page.__extensionContext;
161
193
  const extensionId = page.__extensionId;
162
194
  const extensionPopup = await findExtensionPopup(context, extensionId);
163
- try {
164
- await extensionPopup.getByRole("button", { name: "Yes" }).click();
165
- } catch {
166
- console.log("No another popup found, skipping");
167
- }
195
+ if (await extensionPopup.getByRole("button", { name: "Yes" }).isVisible()) await extensionPopup.getByRole("button", { name: "Yes" }).click();
168
196
  await extensionPopup.getByRole("button", { name: "Approve" }).click();
169
197
  }
170
198
  async function rejectTalismanTx(page) {
171
199
  const context = page.__extensionContext;
172
200
  const extensionId = page.__extensionId;
173
- await (await findExtensionPopup(context, extensionId)).getByTestId("connection-reject-button").click();
201
+ const extensionPopup = await findExtensionPopup(context, extensionId);
202
+ await extensionPopup.getByTestId("connection-reject-button").or(extensionPopup.getByRole("button", { name: "Cancel" })).click();
174
203
  }
175
204
 
176
- //#endregion
177
- //#region src/context-playwright/types.ts
178
- const WALLET_TYPES = ["polkadot-js", "talisman"];
179
-
180
205
  //#endregion
181
206
  //#region src/context-playwright/wallet-factory.ts
182
207
  function createExtendedPage(page, context, extensionId) {
@@ -185,80 +210,58 @@ function createExtendedPage(page, context, extensionId) {
185
210
  extPage.__extensionId = extensionId;
186
211
  return extPage;
187
212
  }
188
- function createWalletInstance(walletType, extensionId, context) {
189
- let importedAccountName;
190
- const baseInstance = {
213
+ function createPolkadotJsWallet(extensionId, context) {
214
+ return {
191
215
  extensionId,
216
+ type: "polkadot-js",
192
217
  importMnemonic: async (options) => {
218
+ await importPolkadotJSAccount(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId), options);
219
+ },
220
+ authorize: async () => {
221
+ await authorizePolkadotJS(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId));
222
+ },
223
+ approveTx: async (options = {}) => {
224
+ await approvePolkadotJSTx(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId), options);
225
+ },
226
+ rejectTx: async () => {
227
+ await rejectPolkadotJSTx(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId));
228
+ }
229
+ };
230
+ }
231
+ function createTalismanWallet(extensionId, context) {
232
+ let importedAccountName;
233
+ return {
234
+ extensionId,
235
+ type: "talisman",
236
+ importPolkadotMnemonic: async (options) => {
193
237
  const extPage = createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId);
194
238
  importedAccountName = options.name || "Test Account";
195
- switch (walletType) {
196
- case "polkadot-js":
197
- await importPolkadotJSAccount(extPage, options);
198
- break;
199
- case "talisman": throw new Error("Talisman importMnemonic is not yet implemented.");
200
- default: throw new Error(`Unsupported wallet type: ${walletType}`);
201
- }
239
+ await importPolkadotMnemonic(extPage, options);
202
240
  },
203
- authorize: async (options = {}) => {
241
+ importEthPrivateKey: async (options) => {
204
242
  const extPage = createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId);
205
- const accountName = options.accountName || importedAccountName;
206
- switch (walletType) {
207
- case "polkadot-js":
208
- await authorizePolkadotJS(extPage);
209
- break;
210
- case "talisman":
211
- await authorizeTalisman(extPage, { accountName });
212
- break;
213
- default: throw new Error(`Unsupported wallet type: ${walletType}`);
214
- }
243
+ importedAccountName = options.name || "Test Account";
244
+ await importEthPrivateKey(extPage, {
245
+ seed: options.privateKey,
246
+ name: options.name,
247
+ password: options.password
248
+ });
215
249
  },
216
- approveTx: async (options = {}) => {
217
- const extPage = createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId);
218
- switch (walletType) {
219
- case "polkadot-js":
220
- await approvePolkadotJSTx(extPage, options);
221
- break;
222
- case "talisman":
223
- await approveTalismanTx(extPage);
224
- break;
225
- default: throw new Error(`Unsupported wallet type: ${walletType}`);
226
- }
250
+ authorize: async (options = {}) => {
251
+ await authorizeTalisman(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId), { accountName: options.accountName || importedAccountName });
252
+ },
253
+ approveTx: async () => {
254
+ await approveTalismanTx(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId));
227
255
  },
228
256
  rejectTx: async () => {
229
- const extPage = createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId);
230
- switch (walletType) {
231
- case "polkadot-js":
232
- await rejectPolkadotJSTx(extPage);
233
- break;
234
- case "talisman":
235
- await rejectTalismanTx(extPage);
236
- break;
237
- default: throw new Error(`Unsupported wallet type: ${walletType}`);
238
- }
257
+ await rejectTalismanTx(createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId));
239
258
  }
240
259
  };
241
- switch (walletType) {
242
- case "polkadot-js": return {
243
- ...baseInstance,
244
- type: "polkadot-js"
245
- };
246
- case "talisman": return {
247
- ...baseInstance,
248
- type: "talisman",
249
- importEthPrivateKey: async (options) => {
250
- const extPage = createExtendedPage(context.pages()[0] || await context.newPage(), context, extensionId);
251
- importedAccountName = options.name || "Test Account";
252
- await importEthPrivateKey(extPage, {
253
- seed: options.privateKey,
254
- name: options.name,
255
- password: options.password
256
- });
257
- }
258
- };
259
- default: throw new Error(`Unsupported wallet type: ${walletType}`);
260
- }
261
260
  }
261
+ const walletFactories = {
262
+ "polkadot-js": createPolkadotJsWallet,
263
+ "talisman": createTalismanWallet
264
+ };
262
265
 
263
266
  //#endregion
264
267
  //#region src/context-playwright/index.ts
@@ -306,7 +309,10 @@ function createWalletTest(options = {}) {
306
309
  },
307
310
  wallets: async ({ walletContext, walletExtensionIds }, use) => {
308
311
  const walletMap = {};
309
- for (const [walletType, extensionId] of walletExtensionIds) if (WALLET_TYPES.includes(walletType)) walletMap[walletType] = createWalletInstance(walletType, extensionId, walletContext);
312
+ for (const [walletType, extensionId] of walletExtensionIds) {
313
+ const factory = walletFactories[walletType];
314
+ if (factory) walletMap[walletType] = factory(extensionId, walletContext);
315
+ }
310
316
  await use(walletMap);
311
317
  }
312
318
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@avalix/chroma",
3
3
  "type": "module",
4
- "version": "0.0.11",
4
+ "version": "0.0.12",
5
5
  "description": "End-to-end testing library for Polkadot wallet interactions",
6
6
  "author": "Avalix Labs",
7
7
  "license": "MIT",
@@ -44,7 +44,8 @@
44
44
  "lint": "eslint --fix .",
45
45
  "prepublishOnly": "npm run build",
46
46
  "download-extensions": "rm -rf .chroma && tsx scripts/download-extensions.ts",
47
- "test": "playwright test --ui",
47
+ "test": "playwright test",
48
+ "test:ui": "playwright test --ui",
48
49
  "test:unit": "vitest",
49
50
  "test:unit:run": "vitest run",
50
51
  "test:unit:coverage": "vitest run --coverage"
@@ -19,15 +19,15 @@ async function clearChromaDir(): Promise<void> {
19
19
  const chromaDir = path.resolve(process.cwd(), '.chroma')
20
20
 
21
21
  if (fs.existsSync(chromaDir)) {
22
- console.log('🗑️ Clearing existing .chroma directory...')
22
+ console.log('🗑️ Clearing existing .chroma directory...')
23
23
  await fs.promises.rm(chromaDir, { recursive: true, force: true })
24
24
  }
25
25
  }
26
26
 
27
27
  async function main() {
28
28
  const version = await getVersion()
29
- console.log(`🎨 Chroma v${version}\n`)
30
- console.log('🚀 Downloading wallet extensions...\n')
29
+ console.log(`\n🎨 Chroma v${version}`)
30
+ console.log('🚀 Downloading wallet extensions...')
31
31
 
32
32
  try {
33
33
  // Clear existing .chroma directory
@@ -4,16 +4,13 @@ import type {
4
4
  ExtendedPage,
5
5
  WalletConfig,
6
6
  WalletFixtures,
7
- WalletInstance,
8
7
  Wallets,
9
- WalletType,
10
8
  WalletWorkerFixtures,
11
9
  } from './types.js'
12
10
  import { test as base, chromium } from '@playwright/test'
13
11
  import { getPolkadotJSExtensionPath } from '../wallets/polkadot-js.js'
14
12
  import { getTalismanExtensionPath } from '../wallets/talisman.js'
15
- import { WALLET_TYPES } from './types.js'
16
- import { createWalletInstance } from './wallet-factory.js'
13
+ import { walletFactories } from './wallet-factory.js'
17
14
 
18
15
  // Helper function to get extension path for a wallet config
19
16
  async function getExtensionPathForWallet(config: WalletConfig): Promise<string> {
@@ -117,17 +114,17 @@ export function createWalletTest<const T extends readonly WalletConfig[]>(
117
114
 
118
115
  // Wallet instances for each configured wallet
119
116
  wallets: async ({ walletContext, walletExtensionIds }, use) => {
120
- const walletMap: Partial<ExpectedWallets> = {}
117
+ const walletMap = {} as ExpectedWallets
121
118
 
122
119
  // Create wallet instance for each configured wallet
123
120
  for (const [walletType, extensionId] of walletExtensionIds) {
124
- if (WALLET_TYPES.includes(walletType as WalletType)) {
125
- const instance = createWalletInstance(walletType, extensionId, walletContext);
126
- (walletMap as Record<string, WalletInstance>)[walletType] = instance
121
+ const factory = walletFactories[walletType as keyof typeof walletFactories]
122
+ if (factory) {
123
+ walletMap[walletType as keyof ExpectedWallets] = factory(extensionId, walletContext) as ExpectedWallets[keyof ExpectedWallets]
127
124
  }
128
125
  }
129
126
 
130
- await use(walletMap as ExpectedWallets)
127
+ await use(walletMap)
131
128
  },
132
129
  })
133
130
  }
@@ -1,11 +1,16 @@
1
1
  import type { BrowserContext, Page } from '@playwright/test'
2
+ import type {
3
+ PolkadotJsWalletInstance,
4
+ TalismanWalletInstance,
5
+ WalletInstance,
6
+ } from './wallet-factory.js'
7
+
8
+ // Re-export wallet instance types
9
+ export type { PolkadotJsWalletInstance, TalismanWalletInstance, WalletInstance }
2
10
 
3
11
  // Wallet types - single source of truth
4
12
  export type WalletType = 'polkadot-js' | 'talisman'
5
13
 
6
- // Available wallet types as constant array
7
- export const WALLET_TYPES: readonly WalletType[] = ['polkadot-js', 'talisman'] as const
8
-
9
14
  // Wallet account configuration
10
15
  export interface WalletAccount {
11
16
  seed: string
@@ -19,29 +24,6 @@ export interface WalletConfig {
19
24
  downloadUrl?: string
20
25
  }
21
26
 
22
- // Base wallet instance - common methods for all wallets
23
- export interface BaseWalletInstance {
24
- extensionId: string
25
- importMnemonic: (options: WalletAccount) => Promise<void>
26
- authorize: (options?: { accountName?: string }) => Promise<void>
27
- approveTx: (options?: { password?: string }) => Promise<void>
28
- rejectTx: () => Promise<void>
29
- }
30
-
31
- // Polkadot-JS specific wallet instance
32
- export interface PolkadotJsWalletInstance extends BaseWalletInstance {
33
- type: 'polkadot-js'
34
- }
35
-
36
- // Talisman specific wallet instance (with additional methods)
37
- export interface TalismanWalletInstance extends BaseWalletInstance {
38
- type: 'talisman'
39
- importEthPrivateKey: (options: { privateKey: string, name?: string, password?: string }) => Promise<void>
40
- }
41
-
42
- // Union type of all wallet instances
43
- export type WalletInstance = PolkadotJsWalletInstance | TalismanWalletInstance
44
-
45
27
  // Map wallet type to its instance
46
28
  export interface WalletTypeMap {
47
29
  'polkadot-js': PolkadotJsWalletInstance
@@ -1,11 +1,5 @@
1
1
  import type { BrowserContext, Page } from '@playwright/test'
2
- import type {
3
- BaseWalletInstance,
4
- PolkadotJsWalletInstance,
5
- TalismanWalletInstance,
6
- WalletAccount,
7
- WalletInstance,
8
- } from './types.js'
2
+ import type { WalletAccount } from './types.js'
9
3
  import {
10
4
  approvePolkadotJSTx,
11
5
  authorizePolkadotJS,
@@ -16,6 +10,7 @@ import {
16
10
  approveTalismanTx,
17
11
  authorizeTalisman,
18
12
  importEthPrivateKey,
13
+ importPolkadotMnemonic,
19
14
  rejectTalismanTx,
20
15
  } from '../wallets/talisman.js'
21
16
 
@@ -27,114 +22,83 @@ function createExtendedPage(page: Page, context: BrowserContext, extensionId: st
27
22
  return extPage
28
23
  }
29
24
 
30
- // Create wallet instance helper with proper typing
31
- export function createWalletInstance(
32
- walletType: string,
33
- extensionId: string,
34
- context: BrowserContext,
35
- ): WalletInstance {
36
- // Store the imported account name for later use
37
- let importedAccountName: string | undefined
38
-
39
- // Common methods for all wallets
40
- const baseInstance: BaseWalletInstance = {
25
+ // Factory function for Polkadot-JS wallet
26
+ export function createPolkadotJsWallet(extensionId: string, context: BrowserContext) {
27
+ return {
41
28
  extensionId,
29
+ type: 'polkadot-js' as const,
42
30
  importMnemonic: async (options: WalletAccount) => {
43
31
  const page = context.pages()[0] || await context.newPage()
44
32
  const extPage = createExtendedPage(page, context, extensionId)
33
+ await importPolkadotJSAccount(extPage, options)
34
+ },
35
+ authorize: async () => {
36
+ const page = context.pages()[0] || await context.newPage()
37
+ const extPage = createExtendedPage(page, context, extensionId)
38
+ await authorizePolkadotJS(extPage)
39
+ },
40
+ approveTx: async (options: { password?: string } = {}) => {
41
+ const page = context.pages()[0] || await context.newPage()
42
+ const extPage = createExtendedPage(page, context, extensionId)
43
+ await approvePolkadotJSTx(extPage, options)
44
+ },
45
+ rejectTx: async () => {
46
+ const page = context.pages()[0] || await context.newPage()
47
+ const extPage = createExtendedPage(page, context, extensionId)
48
+ await rejectPolkadotJSTx(extPage)
49
+ },
50
+ }
51
+ }
45
52
 
46
- // Store the account name for future authorize calls
47
- importedAccountName = options.name || 'Test Account'
53
+ // Factory function for Talisman wallet
54
+ export function createTalismanWallet(extensionId: string, context: BrowserContext) {
55
+ let importedAccountName: string | undefined
48
56
 
49
- switch (walletType) {
50
- case 'polkadot-js':
51
- await importPolkadotJSAccount(extPage, options)
52
- break
53
- case 'talisman':
54
- throw new Error('Talisman importMnemonic is not yet implemented.')
55
- default:
56
- throw new Error(`Unsupported wallet type: ${walletType}`)
57
- }
57
+ return {
58
+ extensionId,
59
+ type: 'talisman' as const,
60
+ importPolkadotMnemonic: async (options: WalletAccount) => {
61
+ const page = context.pages()[0] || await context.newPage()
62
+ const extPage = createExtendedPage(page, context, extensionId)
63
+ importedAccountName = options.name || 'Test Account'
64
+ await importPolkadotMnemonic(extPage, options)
65
+ },
66
+ importEthPrivateKey: async (options: { privateKey: string, name?: string, password?: string }) => {
67
+ const page = context.pages()[0] || await context.newPage()
68
+ const extPage = createExtendedPage(page, context, extensionId)
69
+ importedAccountName = options.name || 'Test Account'
70
+ await importEthPrivateKey(extPage, {
71
+ seed: options.privateKey,
72
+ name: options.name,
73
+ password: options.password,
74
+ })
58
75
  },
59
76
  authorize: async (options: { accountName?: string } = {}) => {
60
77
  const page = context.pages()[0] || await context.newPage()
61
78
  const extPage = createExtendedPage(page, context, extensionId)
62
-
63
- // Use provided account name or fall back to the imported one
64
79
  const accountName = options.accountName || importedAccountName
65
-
66
- switch (walletType) {
67
- case 'polkadot-js':
68
- await authorizePolkadotJS(extPage)
69
- break
70
- case 'talisman':
71
- await authorizeTalisman(extPage, { accountName })
72
- break
73
- default:
74
- throw new Error(`Unsupported wallet type: ${walletType}`)
75
- }
80
+ await authorizeTalisman(extPage, { accountName })
76
81
  },
77
- approveTx: async (options: { password?: string } = {}) => {
82
+ approveTx: async () => {
78
83
  const page = context.pages()[0] || await context.newPage()
79
84
  const extPage = createExtendedPage(page, context, extensionId)
80
-
81
- switch (walletType) {
82
- case 'polkadot-js':
83
- await approvePolkadotJSTx(extPage, options)
84
- break
85
- case 'talisman':
86
- await approveTalismanTx(extPage)
87
- break
88
- default:
89
- throw new Error(`Unsupported wallet type: ${walletType}`)
90
- }
85
+ await approveTalismanTx(extPage)
91
86
  },
92
87
  rejectTx: async () => {
93
88
  const page = context.pages()[0] || await context.newPage()
94
89
  const extPage = createExtendedPage(page, context, extensionId)
95
-
96
- switch (walletType) {
97
- case 'polkadot-js':
98
- await rejectPolkadotJSTx(extPage)
99
- break
100
- case 'talisman':
101
- await rejectTalismanTx(extPage)
102
- break
103
- default:
104
- throw new Error(`Unsupported wallet type: ${walletType}`)
105
- }
90
+ await rejectTalismanTx(extPage)
106
91
  },
107
92
  }
93
+ }
108
94
 
109
- // Return wallet-specific instance with type discriminator
110
- switch (walletType) {
111
- case 'polkadot-js':
112
- return {
113
- ...baseInstance,
114
- type: 'polkadot-js',
115
- } as PolkadotJsWalletInstance
116
-
117
- case 'talisman':
118
- return {
119
- ...baseInstance,
120
- type: 'talisman',
121
- importEthPrivateKey: async (options: { privateKey: string, name?: string, password?: string }) => {
122
- const page = context.pages()[0] || await context.newPage()
123
- const extPage = createExtendedPage(page, context, extensionId)
124
-
125
- // Store the account name for future authorize calls
126
- importedAccountName = options.name || 'Test Account'
127
-
128
- // Use the seed property to pass the private key
129
- await importEthPrivateKey(extPage, {
130
- seed: options.privateKey,
131
- name: options.name,
132
- password: options.password,
133
- })
134
- },
135
- } as TalismanWalletInstance
136
-
137
- default:
138
- throw new Error(`Unsupported wallet type: ${walletType}`)
139
- }
95
+ // Wallet factories map - auto-inferred types
96
+ export const walletFactories = {
97
+ 'polkadot-js': createPolkadotJsWallet,
98
+ 'talisman': createTalismanWallet,
140
99
  }
100
+
101
+ // Auto-inferred types from factory functions
102
+ export type PolkadotJsWalletInstance = ReturnType<typeof createPolkadotJsWallet>
103
+ export type TalismanWalletInstance = ReturnType<typeof createTalismanWallet>
104
+ export type WalletInstance = PolkadotJsWalletInstance | TalismanWalletInstance
@@ -34,7 +34,7 @@ export async function downloadAndExtractExtension(options: DownloadExtensionOpti
34
34
  }
35
35
 
36
36
  try {
37
- console.log(`📥 Downloading ${extensionName}...`)
37
+ console.log(`\n📥 Downloading ${extensionName}...`)
38
38
 
39
39
  // Download the ZIP file
40
40
  const response = await fetch(downloadUrl)
@@ -54,65 +54,114 @@ export async function getTalismanExtensionPath(): Promise<string> {
54
54
  return extensionDir
55
55
  }
56
56
 
57
- // Talisman specific Ethereum private key import implementation
58
- export async function importEthPrivateKey(
57
+ // Helper function to find Talisman onboarding page
58
+ async function findOnboardingPage(
59
+ context: BrowserContext,
60
+ extensionId: string,
61
+ ): Promise<Page> {
62
+ // Open new dashboard page
63
+ const popupUrl = `chrome-extension://${extensionId}/dashboard.html`
64
+ const newPage = await context.newPage()
65
+ await newPage.goto(popupUrl)
66
+ await newPage.waitForLoadState('domcontentloaded')
67
+
68
+ // Close any other extension tabs that may have been opened automatically
69
+ for (const p of context.pages()) {
70
+ if (p !== newPage && p.url().includes(`chrome-extension://${extensionId}/`)) {
71
+ await p.close()
72
+ }
73
+ }
74
+
75
+ return newPage
76
+ }
77
+
78
+ // Helper function to complete Talisman onboarding flow
79
+ async function completeOnboarding(
80
+ extensionPage: Page,
81
+ password: string,
82
+ ): Promise<void> {
83
+ // Bring the onboarding page to front
84
+ await extensionPage.bringToFront()
85
+
86
+ // Wait for the page to load and become interactive
87
+ await extensionPage.waitForLoadState('domcontentloaded')
88
+
89
+ if (await extensionPage.getByRole('button', { name: 'Settings' }).isVisible()) {
90
+ await extensionPage.getByRole('button', { name: 'Settings' }).click({ force: true })
91
+ return
92
+ }
93
+
94
+ // Click the get started button
95
+ await extensionPage.getByTestId('onboarding-get-started-button').click()
96
+
97
+ // Fill the password
98
+ await extensionPage.getByRole('textbox', { name: 'Enter password' }).fill(password)
99
+ await extensionPage.getByRole('textbox', { name: 'Confirm password' }).fill(password)
100
+ await extensionPage.getByTestId('onboarding-password-confirm-button').click()
101
+
102
+ // Click the no thanks button
103
+ await extensionPage.getByRole('button', { name: 'No thanks' }).click()
104
+ await extensionPage.getByTestId('onboarding-enter-talisman-button').click()
105
+
106
+ // Navigate directly to settings/general page
107
+ const extensionId = extensionPage.url().match(/chrome-extension:\/\/([^/]+)/)?.[1]
108
+ await extensionPage.goto(`chrome-extension://${extensionId}/dashboard.html#/settings/general`)
109
+ await extensionPage.waitForLoadState('domcontentloaded')
110
+ await extensionPage.getByRole('link', { name: 'Security & Privacy' }).click()
111
+
112
+ // Toggle the risk scan setting
113
+ await extensionPage.getByTestId('component-toggle-button').first().click()
114
+ }
115
+
116
+ // Talisman specific Polkadot mnemonic import implementation
117
+ export async function importPolkadotMnemonic(
59
118
  page: Page & { __extensionContext: BrowserContext, __extensionId: string },
60
119
  { seed, name = 'Test Account', password = 'h3llop0lkadot!' }: WalletAccount,
61
120
  ): Promise<void> {
62
121
  const context = page.__extensionContext
63
122
  const extensionId = page.__extensionId
64
123
 
65
- // Wait for Talisman to open its onboarding tab with retry logic
66
- const maxAttempts = 20
67
- const retryDelay = 500
68
- let extensionPage: Page | null = null
69
-
70
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
71
- const pages = context.pages()
124
+ const extensionPage = await findOnboardingPage(context, extensionId)
72
125
 
73
- for (const p of pages) {
74
- const url = p.url()
75
- if (url.includes('onboarding.html') || url.includes(`chrome-extension://${extensionId}/`)) {
76
- extensionPage = p
77
- break
78
- }
79
- }
126
+ try {
127
+ await completeOnboarding(extensionPage, password!)
80
128
 
81
- if (extensionPage) {
82
- break
83
- }
129
+ // Import Polkadot account via Recovery Phrase
130
+ await extensionPage.getByRole('link', { name: 'Manage Accounts' }).click()
131
+ await extensionPage.getByRole('button', { name: 'Get Started' }).click()
132
+ await extensionPage.getByRole('button', { name: 'Add Account' }).click()
133
+ await extensionPage.getByRole('button', { name: 'Import Import an existing' }).click()
134
+ await extensionPage.getByRole('button', { name: 'Import via Recovery Phrase' }).click()
135
+ await extensionPage.getByRole('button', { name: 'Polkadot Relay Chain, Asset' }).click()
136
+ await extensionPage.getByRole('textbox', { name: 'Choose a name' }).fill(name!)
137
+ await extensionPage.getByRole('textbox', { name: 'Enter your 12 or 24 word' }).fill(seed!)
138
+ await extensionPage.getByTestId('account-add-mnemonic-import-button').click()
84
139
 
85
- // If not found, wait before retrying
86
- if (attempt < maxAttempts - 1) {
87
- await new Promise(resolve => setTimeout(resolve, retryDelay))
88
- }
140
+ await extensionPage.close()
89
141
  }
90
-
91
- if (!extensionPage) {
92
- throw new Error(`Talisman onboarding page not found after ${maxAttempts} attempts`)
142
+ catch (error) {
143
+ console.error('❌ Error during Talisman Polkadot account import:', error)
144
+ throw error
93
145
  }
146
+ }
94
147
 
95
- try {
96
- // Bring the onboarding page to front
97
- await extensionPage.bringToFront()
98
-
99
- // Wait for the page to load and become interactive
100
- await extensionPage.waitForLoadState('domcontentloaded')
101
-
102
- // Click the get started button
103
- await extensionPage.getByTestId('onboarding-get-started-button').click()
148
+ // Talisman specific Ethereum private key import implementation
149
+ export async function importEthPrivateKey(
150
+ page: Page & { __extensionContext: BrowserContext, __extensionId: string },
151
+ { seed, name = 'Test Account', password = 'h3llop0lkadot!' }: WalletAccount,
152
+ ): Promise<void> {
153
+ const context = page.__extensionContext
154
+ const extensionId = page.__extensionId
104
155
 
105
- // Fill the password
106
- await extensionPage.getByRole('textbox', { name: 'Enter password' }).fill(password!)
107
- await extensionPage.getByRole('textbox', { name: 'Confirm password' }).fill(password!)
108
- await extensionPage.getByTestId('onboarding-password-confirm-button').click()
156
+ const extensionPage = await findOnboardingPage(context, extensionId)
109
157
 
110
- // Click the no thanks button
111
- await extensionPage.getByRole('button', { name: 'No thanks' }).click()
112
- await extensionPage.getByTestId('onboarding-enter-talisman-button').click()
158
+ try {
159
+ await completeOnboarding(extensionPage, password!)
113
160
 
114
- // Import Ethereum account
115
- await extensionPage.getByRole('button', { name: 'Add account Create or import' }).click()
161
+ // Import Ethereum account via Private Key
162
+ await extensionPage.getByRole('link', { name: 'Manage Accounts' }).click()
163
+ await extensionPage.getByRole('button', { name: 'Get Started' }).click()
164
+ await extensionPage.getByRole('button', { name: 'Add Account' }).click()
116
165
  await extensionPage.getByRole('button', { name: 'Import Import an existing' }).click()
117
166
  await extensionPage.getByRole('button', { name: 'Import via Private Key' }).click()
118
167
  await extensionPage.getByRole('button', { name: 'Select account platform' }).click()
@@ -124,7 +173,7 @@ export async function importEthPrivateKey(
124
173
  await extensionPage.close()
125
174
  }
126
175
  catch (error) {
127
- console.error('❌ Error during Talisman account import:', error)
176
+ console.error('❌ Error during Talisman Ethereum account import:', error)
128
177
  throw error
129
178
  }
130
179
  }
@@ -134,14 +183,18 @@ export async function authorizeTalisman(
134
183
  page: Page & { __extensionContext: BrowserContext, __extensionId: string },
135
184
  options: { accountName?: string } = {},
136
185
  ): Promise<void> {
137
- const { accountName = 'Test Account' } = options
138
186
  const context = page.__extensionContext
139
187
  const extensionId = page.__extensionId
188
+ const { accountName = 'Test Account' } = options
140
189
 
141
190
  const extensionPopup = await findExtensionPopup(context, extensionId)
191
+ await extensionPopup.waitForLoadState('domcontentloaded')
142
192
 
143
193
  // Authorize Talisman account
144
- await extensionPopup.getByRole('button', { name: accountName }).click()
194
+ const accountButton = extensionPopup.getByRole('button', { name: accountName })
195
+ await accountButton.waitFor({ state: 'visible' })
196
+ await accountButton.scrollIntoViewIfNeeded()
197
+ await accountButton.click({ force: true })
145
198
  await extensionPopup.getByTestId('connection-connect-button').click()
146
199
 
147
200
  try {
@@ -161,12 +214,9 @@ export async function approveTalismanTx(
161
214
 
162
215
  const extensionPopup = await findExtensionPopup(context, extensionId)
163
216
 
164
- try {
217
+ if (await extensionPopup.getByRole('button', { name: 'Yes' }).isVisible()) {
165
218
  await extensionPopup.getByRole('button', { name: 'Yes' }).click()
166
219
  }
167
- catch {
168
- console.log('No another popup found, skipping')
169
- }
170
220
 
171
221
  await extensionPopup.getByRole('button', { name: 'Approve' }).click()
172
222
  }
@@ -179,5 +229,9 @@ export async function rejectTalismanTx(
179
229
  const extensionId = page.__extensionId
180
230
 
181
231
  const extensionPopup = await findExtensionPopup(context, extensionId)
182
- await extensionPopup.getByTestId('connection-reject-button').click()
232
+
233
+ const rejectButton = extensionPopup.getByTestId('connection-reject-button')
234
+ .or(extensionPopup.getByRole('button', { name: 'Cancel' }))
235
+
236
+ await rejectButton.click()
183
237
  }