@avalix/chroma 0.0.5 → 0.0.7
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 +168 -73
- package/dist/index.d.ts +51 -27
- package/dist/index.js +259 -109
- package/package.json +10 -4
- package/scripts/cli.js +30 -0
- package/scripts/download-extensions.ts +32 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
End-to-end testing library for Polkadot wallet interactions using Playwright.
|
|
4
4
|
|
|
5
|
-
>
|
|
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
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,6 +12,18 @@ npm install @avalix/chroma @playwright/test
|
|
|
12
12
|
|
|
13
13
|
**Note**: `@playwright/test` is a peer dependency and must be installed separately to avoid conflicts.
|
|
14
14
|
|
|
15
|
+
### Download Extensions
|
|
16
|
+
|
|
17
|
+
Before running your tests, you need to download the wallet extensions:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @avalix/chroma download-extensions
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This will download the wallet extensions (Polkadot JS and Talisman) to `./.chroma` directory in your project root.
|
|
24
|
+
|
|
25
|
+
**Important**: You must run this command before running Playwright tests. If the extension is not found, tests will fail with a helpful error message.
|
|
26
|
+
|
|
15
27
|
## Quick Start
|
|
16
28
|
|
|
17
29
|
### Basic Usage
|
|
@@ -19,9 +31,11 @@ npm install @avalix/chroma @playwright/test
|
|
|
19
31
|
```typescript
|
|
20
32
|
import { expect, test } from '@avalix/chroma'
|
|
21
33
|
|
|
22
|
-
test('should connect wallet and sign transaction', async ({ page,
|
|
34
|
+
test('should connect wallet and sign transaction', async ({ page, wallets }) => {
|
|
35
|
+
const polkadotJs = wallets['polkadot-js']
|
|
36
|
+
|
|
23
37
|
// Import a test account
|
|
24
|
-
await
|
|
38
|
+
await polkadotJs.importMnemonic({
|
|
25
39
|
seed: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk',
|
|
26
40
|
name: 'Test Account',
|
|
27
41
|
password: 'securePassword123'
|
|
@@ -32,11 +46,11 @@ test('should connect wallet and sign transaction', async ({ page, importAccount,
|
|
|
32
46
|
|
|
33
47
|
// Connect wallet
|
|
34
48
|
await page.click('button:has-text("Connect Wallet")')
|
|
35
|
-
await authorize()
|
|
49
|
+
await polkadotJs.authorize()
|
|
36
50
|
|
|
37
51
|
// Perform transaction
|
|
38
52
|
await page.click('button:has-text("Send Transaction")')
|
|
39
|
-
await approveTx({ password: 'securePassword123' })
|
|
53
|
+
await polkadotJs.approveTx({ password: 'securePassword123' })
|
|
40
54
|
|
|
41
55
|
// Verify transaction success
|
|
42
56
|
await expect(page.locator('.transaction-success')).toBeVisible()
|
|
@@ -48,33 +62,71 @@ test('should connect wallet and sign transaction', async ({ page, importAccount,
|
|
|
48
62
|
```typescript
|
|
49
63
|
import { createWalletTest, expect } from '@avalix/chroma'
|
|
50
64
|
|
|
51
|
-
// Create test with custom configuration
|
|
52
65
|
const customTest = createWalletTest({
|
|
53
|
-
|
|
54
|
-
walletConfig: {
|
|
55
|
-
customPath: './my-custom-extension'
|
|
56
|
-
},
|
|
66
|
+
wallets: [{ type: 'polkadot-js' }],
|
|
57
67
|
headless: false,
|
|
58
68
|
slowMo: 100
|
|
59
69
|
})
|
|
60
70
|
|
|
61
|
-
customTest('test with custom config', async ({ page,
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
customTest('test with custom config', async ({ page, wallets }) => {
|
|
72
|
+
const polkadotJs = wallets['polkadot-js']
|
|
73
|
+
|
|
74
|
+
await polkadotJs.importMnemonic({
|
|
64
75
|
seed: 'your seed phrase here...',
|
|
65
76
|
name: 'My Test Account'
|
|
66
77
|
})
|
|
67
|
-
|
|
78
|
+
await page.goto('http://localhost:3000')
|
|
79
|
+
await polkadotJs.authorize()
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Multiple Wallets
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { createWalletTest, expect } from '@avalix/chroma'
|
|
87
|
+
|
|
88
|
+
// Test with multiple wallet extensions
|
|
89
|
+
const multiWalletTest = createWalletTest({
|
|
90
|
+
wallets: [
|
|
91
|
+
{ type: 'polkadot-js' },
|
|
92
|
+
{ type: 'talisman' }
|
|
93
|
+
],
|
|
94
|
+
headless: false,
|
|
95
|
+
slowMo: 150
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
multiWalletTest('test with multiple wallets', async ({ page, wallets }) => {
|
|
99
|
+
const polkadotJs = wallets['polkadot-js']
|
|
100
|
+
const talisman = wallets.talisman
|
|
101
|
+
|
|
102
|
+
// Import to Polkadot JS
|
|
103
|
+
await polkadotJs.importMnemonic({
|
|
104
|
+
seed: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk',
|
|
105
|
+
name: 'Alice'
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// Import to Talisman using Ethereum private key
|
|
109
|
+
await talisman.importEthPrivateKey({
|
|
110
|
+
privateKey: '0x...',
|
|
111
|
+
name: 'Bob'
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
await page.goto('http://localhost:3000')
|
|
115
|
+
|
|
116
|
+
// Use specific wallet
|
|
117
|
+
await polkadotJs.authorize()
|
|
118
|
+
await polkadotJs.approveTx()
|
|
68
119
|
})
|
|
69
120
|
```
|
|
70
121
|
|
|
71
122
|
## Features
|
|
72
123
|
|
|
73
|
-
- 🔐 **
|
|
124
|
+
- 🔐 **Easy Extension Setup**: Simple command to download wallet extensions
|
|
74
125
|
- 🧪 **Test Fixtures**: Ready-to-use Playwright fixtures for wallet operations
|
|
75
126
|
- 📝 **Account Management**: Import accounts with seed phrases and custom names
|
|
76
127
|
- ✅ **Transaction Approval**: Approve transactions with password authentication
|
|
77
128
|
- 🔗 **dApp Authorization**: Connect wallet to decentralized applications
|
|
129
|
+
- 🔀 **Multi-Wallet Support**: Test with multiple wallet extensions simultaneously
|
|
78
130
|
- ⚙️ **Configurable**: Custom extension paths, headless mode, and slow motion settings
|
|
79
131
|
|
|
80
132
|
## API Reference
|
|
@@ -87,110 +139,152 @@ Pre-configured test function with Polkadot JS extension.
|
|
|
87
139
|
```typescript
|
|
88
140
|
import { test } from '@avalix/chroma'
|
|
89
141
|
|
|
90
|
-
test('my wallet test', async ({ page,
|
|
91
|
-
|
|
142
|
+
test('my wallet test', async ({ page, wallets }) => {
|
|
143
|
+
const polkadotJs = wallets['polkadot-js']
|
|
144
|
+
|
|
145
|
+
await polkadotJs.importMnemonic({ seed: '...' })
|
|
146
|
+
await polkadotJs.authorize()
|
|
92
147
|
})
|
|
93
148
|
```
|
|
94
149
|
|
|
95
150
|
#### `createWalletTest(options?: ChromaTestOptions)`
|
|
96
|
-
Create a custom test function with specific configuration.
|
|
151
|
+
Create a custom test function with specific configuration. Supports single and multi-wallet modes.
|
|
97
152
|
|
|
98
153
|
```typescript
|
|
99
154
|
import { createWalletTest } from '@avalix/chroma'
|
|
100
155
|
|
|
156
|
+
// Single wallet (default)
|
|
157
|
+
const test = createWalletTest()
|
|
158
|
+
|
|
159
|
+
// Single wallet with custom config
|
|
101
160
|
const customTest = createWalletTest({
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
161
|
+
wallets: [{ type: 'polkadot-js' }],
|
|
162
|
+
headless: false,
|
|
163
|
+
slowMo: 150
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// Multiple wallets
|
|
167
|
+
const multiTest = createWalletTest({
|
|
168
|
+
wallets: [
|
|
169
|
+
{ type: 'polkadot-js' },
|
|
170
|
+
{ type: 'talisman' }
|
|
171
|
+
]
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Usage
|
|
175
|
+
test('example', async ({ page, wallets }) => {
|
|
176
|
+
const polkadotJs = wallets['polkadot-js']
|
|
177
|
+
|
|
178
|
+
await polkadotJs.importMnemonic({ seed: '...' })
|
|
179
|
+
await polkadotJs.authorize()
|
|
180
|
+
await polkadotJs.approveTx()
|
|
109
181
|
})
|
|
110
182
|
```
|
|
111
183
|
|
|
112
184
|
### Test Fixtures
|
|
113
185
|
|
|
114
|
-
#### `
|
|
115
|
-
|
|
186
|
+
#### `page`
|
|
187
|
+
Playwright page instance with wallet extension(s) loaded.
|
|
188
|
+
|
|
189
|
+
#### `wallets`
|
|
190
|
+
Typed object containing wallet instances for each configured wallet. Provides full TypeScript autocomplete.
|
|
116
191
|
|
|
117
192
|
```typescript
|
|
193
|
+
// Base wallet instance (common methods)
|
|
194
|
+
interface BaseWalletInstance {
|
|
195
|
+
extensionId: string
|
|
196
|
+
importMnemonic: (options: WalletAccount) => Promise<void>
|
|
197
|
+
authorize: (options?: { accountName?: string }) => Promise<void>
|
|
198
|
+
approveTx: (options?: { password?: string }) => Promise<void>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Polkadot-JS wallet instance
|
|
202
|
+
interface PolkadotJsWalletInstance extends BaseWalletInstance {
|
|
203
|
+
type: 'polkadot-js'
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Talisman wallet instance (with additional methods)
|
|
207
|
+
interface TalismanWalletInstance extends BaseWalletInstance {
|
|
208
|
+
type: 'talisman'
|
|
209
|
+
importEthPrivateKey: (options: { privateKey: string, name?: string, password?: string }) => Promise<void>
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Note: Talisman currently does not support importMnemonic - use importEthPrivateKey instead
|
|
213
|
+
|
|
214
|
+
// Wallets collection - each wallet has its specific type
|
|
215
|
+
interface Wallets {
|
|
216
|
+
'polkadot-js': PolkadotJsWalletInstance
|
|
217
|
+
'talisman': TalismanWalletInstance
|
|
218
|
+
}
|
|
219
|
+
|
|
118
220
|
interface WalletAccount {
|
|
119
221
|
seed: string
|
|
120
222
|
name?: string // Default: 'Test Account'
|
|
121
223
|
password?: string // Default: 'h3llop0lkadot!'
|
|
122
224
|
}
|
|
123
|
-
|
|
124
|
-
await importAccount({
|
|
125
|
-
seed: 'your twelve word seed phrase here...',
|
|
126
|
-
name: 'My Test Account',
|
|
127
|
-
password: 'securePassword123'
|
|
128
|
-
})
|
|
129
225
|
```
|
|
130
226
|
|
|
131
|
-
|
|
132
|
-
Authorize the dApp to connect with the wallet. Call this after triggering wallet connection from your dApp.
|
|
227
|
+
**Usage:**
|
|
133
228
|
|
|
134
229
|
```typescript
|
|
135
|
-
|
|
136
|
-
|
|
230
|
+
test('example', async ({ page, wallets }) => {
|
|
231
|
+
const polkadotJs = wallets['polkadot-js'] // Type: PolkadotJsWalletInstance
|
|
137
232
|
|
|
138
|
-
|
|
139
|
-
|
|
233
|
+
// Import mnemonic (available on all wallets)
|
|
234
|
+
await polkadotJs.importMnemonic({
|
|
235
|
+
seed: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk',
|
|
236
|
+
name: 'Test Account',
|
|
237
|
+
password: 'securePassword123'
|
|
238
|
+
})
|
|
140
239
|
|
|
141
|
-
|
|
142
|
-
await
|
|
240
|
+
await page.goto('http://localhost:3000')
|
|
241
|
+
await polkadotJs.authorize()
|
|
242
|
+
await polkadotJs.approveTx({ password: 'securePassword123' })
|
|
243
|
+
})
|
|
143
244
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
245
|
+
// Talisman-specific features
|
|
246
|
+
test('talisman example', async ({ page, wallets }) => {
|
|
247
|
+
const talisman = wallets.talisman // Type: TalismanWalletInstance
|
|
147
248
|
|
|
148
|
-
|
|
249
|
+
// Talisman-specific method: import Ethereum private key
|
|
250
|
+
await talisman.importEthPrivateKey({
|
|
251
|
+
privateKey: '0x...',
|
|
252
|
+
name: 'My Account',
|
|
253
|
+
password: 'mypassword'
|
|
254
|
+
})
|
|
149
255
|
|
|
150
|
-
|
|
151
|
-
|
|
256
|
+
// Common methods also available
|
|
257
|
+
await talisman.authorize({ accountName: 'My Account' })
|
|
258
|
+
await talisman.approveTx()
|
|
259
|
+
})
|
|
260
|
+
```
|
|
152
261
|
|
|
153
|
-
|
|
154
|
-
import { downloadAndExtractPolkadotExtension } from '@avalix/chroma'
|
|
262
|
+
## Configuration
|
|
155
263
|
|
|
156
|
-
|
|
157
|
-
|
|
264
|
+
### Extension Download
|
|
265
|
+
Run the download command to get the required wallet extensions:
|
|
158
266
|
|
|
159
|
-
|
|
160
|
-
|
|
267
|
+
```bash
|
|
268
|
+
npx @avalix/chroma download-extensions
|
|
161
269
|
```
|
|
162
270
|
|
|
163
|
-
|
|
271
|
+
Extensions will be downloaded to `./.chroma` directory in your project root. Add this directory to your `.gitignore`:
|
|
164
272
|
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
ChromaTestOptions,
|
|
168
|
-
WalletAccount,
|
|
169
|
-
WalletConfig,
|
|
170
|
-
WalletFixtures,
|
|
171
|
-
WalletType
|
|
172
|
-
} from '@avalix/chroma'
|
|
273
|
+
```gitignore
|
|
274
|
+
.chroma/
|
|
173
275
|
```
|
|
174
276
|
|
|
175
|
-
## Configuration
|
|
176
|
-
|
|
177
|
-
### Default Directory
|
|
178
|
-
The Polkadot JS extension will be automatically downloaded to `./.chroma` directory in your project root. You can customize this by:
|
|
179
|
-
|
|
180
|
-
1. Using `downloadAndExtractPolkadotExtension('./custom-path')`
|
|
181
|
-
2. Using `createWalletTest()` with `walletConfig.customPath`
|
|
182
|
-
|
|
183
277
|
### Browser Settings
|
|
184
278
|
- **Headless Mode**: Disabled by default for better debugging
|
|
185
279
|
- **Slow Motion**: 150ms delay between actions (configurable)
|
|
186
|
-
- **Extension Loading**: Automatically loads
|
|
280
|
+
- **Extension Loading**: Automatically loads configured wallet extensions
|
|
187
281
|
|
|
188
282
|
## Supported Wallets
|
|
189
283
|
|
|
190
284
|
| Wallet | Status | Version |
|
|
191
285
|
|--------|--------|---------|
|
|
192
286
|
| Polkadot JS Extension | ✅ Supported | v0.61.7 |
|
|
193
|
-
| Talisman |
|
|
287
|
+
| Talisman | ✅ Supported | v3.0.5 |
|
|
194
288
|
| SubWallet | ⏳ Planned | - |
|
|
195
289
|
|
|
196
290
|
## Requirements
|
|
@@ -201,8 +295,9 @@ The Polkadot JS extension will be automatically downloaded to `./.chroma` direct
|
|
|
201
295
|
## Contributing
|
|
202
296
|
|
|
203
297
|
This project is in active development. Currently focusing on:
|
|
204
|
-
- Polkadot JS Extension support
|
|
298
|
+
- Polkadot JS Extension and Talisman support
|
|
205
299
|
- Core testing fixtures
|
|
300
|
+
- Additional wallet integrations
|
|
206
301
|
- Documentation improvements
|
|
207
302
|
|
|
208
303
|
## License
|
package/dist/index.d.ts
CHANGED
|
@@ -1,40 +1,64 @@
|
|
|
1
|
-
import { BrowserContext,
|
|
1
|
+
import { BrowserContext, Page, expect } from "@playwright/test";
|
|
2
|
+
import * as playwright_test0 from "playwright/test";
|
|
2
3
|
|
|
3
|
-
//#region src/context-playwright/
|
|
4
|
-
type WalletType =
|
|
5
|
-
interface WalletConfig {
|
|
6
|
-
downloadUrl?: string;
|
|
7
|
-
customPath?: string;
|
|
8
|
-
}
|
|
4
|
+
//#region src/context-playwright/types.d.ts
|
|
5
|
+
type WalletType = 'polkadot-js' | 'talisman';
|
|
9
6
|
interface WalletAccount {
|
|
10
7
|
seed: string;
|
|
11
8
|
name?: string;
|
|
12
9
|
password?: string;
|
|
13
10
|
}
|
|
14
|
-
interface
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
headless?: boolean;
|
|
18
|
-
slowMo?: number;
|
|
19
|
-
}
|
|
20
|
-
interface ExtendedPage extends Page$1 {
|
|
21
|
-
__extensionContext: BrowserContext$1;
|
|
22
|
-
__extensionId: string;
|
|
11
|
+
interface WalletConfig {
|
|
12
|
+
type: WalletType;
|
|
13
|
+
downloadUrl?: string;
|
|
23
14
|
}
|
|
24
|
-
interface
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
interface BaseWalletInstance {
|
|
16
|
+
extensionId: string;
|
|
17
|
+
importMnemonic: (options: WalletAccount) => Promise<void>;
|
|
18
|
+
authorize: (options?: {
|
|
19
|
+
accountName?: string;
|
|
20
|
+
}) => Promise<void>;
|
|
30
21
|
approveTx: (options?: {
|
|
31
22
|
password?: string;
|
|
32
23
|
}) => Promise<void>;
|
|
33
24
|
}
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
interface PolkadotJsWalletInstance extends BaseWalletInstance {
|
|
26
|
+
type: 'polkadot-js';
|
|
27
|
+
}
|
|
28
|
+
interface TalismanWalletInstance extends BaseWalletInstance {
|
|
29
|
+
type: 'talisman';
|
|
30
|
+
importEthPrivateKey: (options: {
|
|
31
|
+
privateKey: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
password?: string;
|
|
34
|
+
}) => Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
interface WalletTypeMap {
|
|
37
|
+
'polkadot-js': PolkadotJsWalletInstance;
|
|
38
|
+
'talisman': TalismanWalletInstance;
|
|
39
|
+
}
|
|
40
|
+
type Wallets = WalletTypeMap;
|
|
41
|
+
type ConfiguredWallets<T extends readonly WalletConfig[]> = { [K in T[number]['type']]: WalletTypeMap[K] };
|
|
42
|
+
type ExtendedPage = Page & {
|
|
43
|
+
__extensionContext: BrowserContext;
|
|
44
|
+
__walletExtensionIds: Map<string, string>;
|
|
45
|
+
};
|
|
46
|
+
interface ChromaTestOptions<T extends readonly WalletConfig[] = WalletConfig[]> {
|
|
47
|
+
wallets?: T;
|
|
48
|
+
headless?: boolean;
|
|
49
|
+
slowMo?: number;
|
|
50
|
+
}
|
|
51
|
+
interface WalletFixtures<W = Wallets> {
|
|
52
|
+
page: ExtendedPage;
|
|
53
|
+
wallets: W;
|
|
54
|
+
}
|
|
55
|
+
interface WalletWorkerFixtures {
|
|
56
|
+
walletContext: BrowserContext;
|
|
57
|
+
walletExtensionIds: Map<string, string>;
|
|
58
|
+
}
|
|
36
59
|
//#endregion
|
|
37
|
-
//#region src/context-playwright/
|
|
38
|
-
declare function
|
|
60
|
+
//#region src/context-playwright/index.d.ts
|
|
61
|
+
declare function createWalletTest<const T extends readonly WalletConfig[]>(options?: ChromaTestOptions<T>): playwright_test0.TestType<playwright_test0.PlaywrightTestArgs & playwright_test0.PlaywrightTestOptions & WalletFixtures<T extends readonly WalletConfig[] ? ConfiguredWallets<T> : WalletTypeMap>, playwright_test0.PlaywrightWorkerArgs & playwright_test0.PlaywrightWorkerOptions & WalletWorkerFixtures>;
|
|
62
|
+
declare const test: ReturnType<typeof createWalletTest>;
|
|
39
63
|
//#endregion
|
|
40
|
-
export {
|
|
64
|
+
export { createWalletTest, expect, test };
|
package/dist/index.js
CHANGED
|
@@ -1,139 +1,289 @@
|
|
|
1
1
|
import { chromium, expect, test as test$1 } from "@playwright/test";
|
|
2
|
-
import fs
|
|
2
|
+
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
|
-
import { pipeline } from "node:stream/promises";
|
|
6
|
-
import { Extract } from "unzipper";
|
|
7
5
|
|
|
8
|
-
//#region src/
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
//#region src/wallets/polkadot-js.ts
|
|
7
|
+
const POLKADOT_JS_CONFIG = {
|
|
8
|
+
downloadUrl: "https://github.com/polkadot-js/extension/releases/download/v0.61.7/master-chrome-build.zip",
|
|
9
|
+
extensionName: "polkadot-extension-0.61.7"
|
|
10
|
+
};
|
|
11
|
+
async function findExtensionPopup$1(context, extensionId) {
|
|
12
|
+
const pages = context.pages();
|
|
13
|
+
for (const p of pages) if (p.url().includes(`chrome-extension://${extensionId}/`)) return p;
|
|
14
|
+
throw new Error(`Extension popup not found for ID: ${extensionId}`);
|
|
15
|
+
}
|
|
16
|
+
async function getPolkadotJSExtensionPath() {
|
|
17
|
+
const extensionsDir = path.resolve(process.cwd(), ".chroma");
|
|
18
|
+
const extensionDir = path.join(extensionsDir, POLKADOT_JS_CONFIG.extensionName);
|
|
19
|
+
if (!fs.existsSync(extensionDir) || fs.readdirSync(extensionDir).length === 0) throw new Error(`Polkadot-JS extension not found at: ${extensionDir}\n\nPlease download the extension first by running:\n npx @avalix/chroma download-extensions\n\nOr if you're using this as a dependency:\n npm run chroma:download\n`);
|
|
20
|
+
console.log(`✅ Found Polkadot-JS extension at: ${extensionDir}`);
|
|
21
|
+
return extensionDir;
|
|
22
|
+
}
|
|
23
|
+
async function importPolkadotJSAccount(page, { seed, name = "Test Account", password = "h3llop0lkadot!" }) {
|
|
24
|
+
const context = page.__extensionContext;
|
|
25
|
+
const extensionPopupUrl = `chrome-extension://${page.__extensionId}/index.html`;
|
|
26
|
+
const extensionPage = await context.newPage();
|
|
27
|
+
try {
|
|
28
|
+
await extensionPage.goto(extensionPopupUrl);
|
|
29
|
+
const understoodButton = extensionPage.getByRole("button", { name: "Understood, let me continue" });
|
|
30
|
+
if (await understoodButton.count() > 0) {
|
|
31
|
+
await understoodButton.click();
|
|
32
|
+
await extensionPage.waitForTimeout(100);
|
|
33
|
+
}
|
|
34
|
+
await extensionPage.goto(`${extensionPopupUrl}#/account/import-seed`);
|
|
35
|
+
await extensionPage.locator("textarea").fill(seed);
|
|
36
|
+
await extensionPage.locator("button:has-text(\"Next\")").click();
|
|
37
|
+
await extensionPage.locator("input[type=\"text\"]").fill(name);
|
|
38
|
+
await extensionPage.locator("input[type=\"password\"]").fill(password);
|
|
39
|
+
await extensionPage.locator("div").filter({ hasText: /^Repeat password for verification$/ }).getByRole("textbox").fill(password);
|
|
40
|
+
await extensionPage.getByRole("button", { name: "Add the account with the supplied seed" }).click();
|
|
41
|
+
console.log(`✅ Created Polkadot-JS wallet account: ${name}`);
|
|
42
|
+
} finally {
|
|
43
|
+
await extensionPage.close();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function authorizePolkadotJS(page) {
|
|
47
|
+
const context = page.__extensionContext;
|
|
48
|
+
const extensionId = page.__extensionId;
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
50
|
+
const extensionPopup = await findExtensionPopup$1(context, extensionId);
|
|
51
|
+
await extensionPopup.getByText("Select all").click();
|
|
52
|
+
await extensionPopup.getByRole("button", { name: /Connect \d+ account\(s\)/ }).click();
|
|
53
|
+
console.log("✅ Polkadot-JS wallet connected successfully");
|
|
54
|
+
}
|
|
55
|
+
async function approvePolkadotJSTx(page, options = {}) {
|
|
56
|
+
const { password = "h3llop0lkadot!" } = options;
|
|
57
|
+
const context = page.__extensionContext;
|
|
58
|
+
const extensionId = page.__extensionId;
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
60
|
+
const extensionPopup = await findExtensionPopup$1(context, extensionId);
|
|
61
|
+
await extensionPopup.getByRole("textbox").fill(password);
|
|
62
|
+
await extensionPopup.getByRole("button", { name: "Sign the transaction" }).click();
|
|
63
|
+
console.log("✅ Polkadot-JS transaction signed successfully");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/wallets/talisman.ts
|
|
68
|
+
const TALISMAN_CONFIG = {
|
|
69
|
+
downloadUrl: "https://github.com/avalix-labs/polkadot-wallets/raw/refs/heads/main/talisman/talisman-3.0.5.zip",
|
|
70
|
+
extensionName: "talisman-extension-3.0.5"
|
|
71
|
+
};
|
|
72
|
+
async function findExtensionPopup(context, extensionId) {
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
74
|
+
const pages = context.pages();
|
|
75
|
+
for (const p of pages) if (p.url().includes(`chrome-extension://${extensionId}/`)) {
|
|
76
|
+
p.setViewportSize({
|
|
77
|
+
width: 400,
|
|
78
|
+
height: 600
|
|
79
|
+
});
|
|
80
|
+
p.waitForLoadState("domcontentloaded");
|
|
81
|
+
return p;
|
|
18
82
|
}
|
|
83
|
+
throw new Error(`Extension popup not found for ID: ${extensionId}`);
|
|
84
|
+
}
|
|
85
|
+
async function getTalismanExtensionPath() {
|
|
86
|
+
const extensionsDir = path.resolve(process.cwd(), ".chroma");
|
|
87
|
+
const extensionDir = path.join(extensionsDir, TALISMAN_CONFIG.extensionName);
|
|
88
|
+
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\nOr if you're using this as a dependency:\n npm run chroma:download\n`);
|
|
89
|
+
console.log(`✅ Found Talisman extension at: ${extensionDir}`);
|
|
90
|
+
return extensionDir;
|
|
91
|
+
}
|
|
92
|
+
async function importEthPrivateKey(page, { seed, name = "Test Account", password = "h3llop0lkadot!" }) {
|
|
93
|
+
const context = page.__extensionContext;
|
|
94
|
+
const extensionId = page.__extensionId;
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
96
|
+
let extensionPage = null;
|
|
97
|
+
const pages = context.pages();
|
|
98
|
+
for (const page$1 of pages) {
|
|
99
|
+
const url = page$1.url();
|
|
100
|
+
console.log(`📄 Found page: ${url}`);
|
|
101
|
+
if (url.includes("onboarding.html") || url.includes(`chrome-extension://${extensionId}/`)) {
|
|
102
|
+
extensionPage = page$1;
|
|
103
|
+
console.log(`✅ Found Talisman onboarding page: ${url}`);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!extensionPage) throw new Error(`Talisman onboarding page not found`);
|
|
19
108
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
-
await
|
|
27
|
-
await
|
|
28
|
-
|
|
29
|
-
|
|
109
|
+
await extensionPage.bringToFront();
|
|
110
|
+
console.log("🔄 Reloading onboarding page for fresh state...");
|
|
111
|
+
await extensionPage.reload();
|
|
112
|
+
await extensionPage.waitForLoadState("domcontentloaded");
|
|
113
|
+
await extensionPage.getByTestId("onboarding-get-started-button").click();
|
|
114
|
+
await extensionPage.getByRole("textbox", { name: "Enter password" }).fill(password);
|
|
115
|
+
await extensionPage.getByRole("textbox", { name: "Confirm password" }).fill(password);
|
|
116
|
+
await extensionPage.getByTestId("onboarding-password-confirm-button").click();
|
|
117
|
+
await extensionPage.getByRole("button", { name: "No thanks" }).click();
|
|
118
|
+
await extensionPage.getByTestId("onboarding-enter-talisman-button").click();
|
|
119
|
+
await extensionPage.getByRole("button", { name: "Add account Create or import" }).click();
|
|
120
|
+
await extensionPage.getByRole("button", { name: "Import Import an existing" }).click();
|
|
121
|
+
await extensionPage.getByRole("button", { name: "Import via Private Key" }).click();
|
|
122
|
+
await extensionPage.getByRole("button", { name: "Select account platform" }).click();
|
|
123
|
+
await extensionPage.getByRole("option", { name: "Ethereum" }).locator("div").click();
|
|
124
|
+
await extensionPage.getByRole("textbox", { name: "Choose a name" }).fill(name);
|
|
125
|
+
await extensionPage.getByRole("textbox", { name: "Enter your private key" }).fill(seed);
|
|
126
|
+
await extensionPage.getByRole("button", { name: "Save" }).click();
|
|
127
|
+
await extensionPage.close();
|
|
128
|
+
console.log("✅ Talisman account import completed");
|
|
30
129
|
} catch (error) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
130
|
+
console.error("❌ Error during Talisman account import:", error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function authorizeTalisman(page, options = {}) {
|
|
135
|
+
const { accountName = "Test Account" } = options;
|
|
136
|
+
const context = page.__extensionContext;
|
|
137
|
+
const extensionId = page.__extensionId;
|
|
138
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
139
|
+
const extensionPopup = await findExtensionPopup(context, extensionId);
|
|
140
|
+
await extensionPopup.getByRole("button", { name: accountName }).click();
|
|
141
|
+
await extensionPopup.getByTestId("connection-connect-button").click();
|
|
142
|
+
try {
|
|
143
|
+
await (await findExtensionPopup(context, extensionId)).getByRole("button", { name: "Approve" }).click();
|
|
144
|
+
} catch {
|
|
145
|
+
console.log("No another popup found, skipping");
|
|
34
146
|
}
|
|
35
147
|
}
|
|
148
|
+
async function approveTalismanTx(page) {
|
|
149
|
+
const context = page.__extensionContext;
|
|
150
|
+
const extensionId = page.__extensionId;
|
|
151
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
152
|
+
await (await findExtensionPopup(context, extensionId)).getByRole("button", { name: "Approve" }).click();
|
|
153
|
+
}
|
|
36
154
|
|
|
37
155
|
//#endregion
|
|
38
|
-
//#region src/context-playwright/
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
156
|
+
//#region src/context-playwright/types.ts
|
|
157
|
+
const WALLET_TYPES = ["polkadot-js", "talisman"];
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/context-playwright/wallet-factory.ts
|
|
161
|
+
function createExtendedPage(page, context, extensionId) {
|
|
162
|
+
const extPage = page;
|
|
163
|
+
extPage.__extensionContext = context;
|
|
164
|
+
extPage.__extensionId = extensionId;
|
|
165
|
+
return extPage;
|
|
166
|
+
}
|
|
167
|
+
function createWalletInstance(walletType, extensionId, context) {
|
|
168
|
+
let importedAccountName;
|
|
169
|
+
const baseInstance = {
|
|
170
|
+
extensionId,
|
|
171
|
+
importMnemonic: async (options) => {
|
|
172
|
+
const page = context.pages()[0] || await context.newPage();
|
|
173
|
+
const extPage = createExtendedPage(page, context, extensionId);
|
|
174
|
+
importedAccountName = options.name || "Test Account";
|
|
175
|
+
switch (walletType) {
|
|
176
|
+
case "polkadot-js":
|
|
177
|
+
await importPolkadotJSAccount(extPage, options);
|
|
178
|
+
break;
|
|
179
|
+
case "talisman": throw new Error("Talisman importMnemonic is not yet implemented.");
|
|
180
|
+
default: throw new Error(`Unsupported wallet type: ${walletType}`);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
authorize: async (options = {}) => {
|
|
184
|
+
const page = context.pages()[0] || await context.newPage();
|
|
185
|
+
const extPage = createExtendedPage(page, context, extensionId);
|
|
186
|
+
const accountName = options.accountName || importedAccountName;
|
|
187
|
+
switch (walletType) {
|
|
188
|
+
case "polkadot-js":
|
|
189
|
+
await authorizePolkadotJS(extPage);
|
|
190
|
+
break;
|
|
191
|
+
case "talisman":
|
|
192
|
+
await authorizeTalisman(extPage, { accountName });
|
|
193
|
+
break;
|
|
194
|
+
default: throw new Error(`Unsupported wallet type: ${walletType}`);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
approveTx: async (options = {}) => {
|
|
198
|
+
const page = context.pages()[0] || await context.newPage();
|
|
199
|
+
const extPage = createExtendedPage(page, context, extensionId);
|
|
200
|
+
switch (walletType) {
|
|
201
|
+
case "polkadot-js":
|
|
202
|
+
await approvePolkadotJSTx(extPage, options);
|
|
203
|
+
break;
|
|
204
|
+
case "talisman":
|
|
205
|
+
await approveTalismanTx(extPage);
|
|
206
|
+
break;
|
|
207
|
+
default: throw new Error(`Unsupported wallet type: ${walletType}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
46
211
|
switch (walletType) {
|
|
47
|
-
case "polkadot-js": return
|
|
48
|
-
|
|
212
|
+
case "polkadot-js": return {
|
|
213
|
+
...baseInstance,
|
|
214
|
+
type: "polkadot-js"
|
|
215
|
+
};
|
|
216
|
+
case "talisman": return {
|
|
217
|
+
...baseInstance,
|
|
218
|
+
type: "talisman",
|
|
219
|
+
importEthPrivateKey: async (options) => {
|
|
220
|
+
const page = context.pages()[0] || await context.newPage();
|
|
221
|
+
const extPage = createExtendedPage(page, context, extensionId);
|
|
222
|
+
importedAccountName = options.name || "Test Account";
|
|
223
|
+
await importEthPrivateKey(extPage, {
|
|
224
|
+
seed: options.privateKey,
|
|
225
|
+
name: options.name,
|
|
226
|
+
password: options.password
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
};
|
|
49
230
|
default: throw new Error(`Unsupported wallet type: ${walletType}`);
|
|
50
231
|
}
|
|
51
232
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
233
|
+
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/context-playwright/index.ts
|
|
236
|
+
async function getExtensionPathForWallet(config) {
|
|
237
|
+
const { type } = config;
|
|
238
|
+
switch (type) {
|
|
239
|
+
case "polkadot-js": return await getPolkadotJSExtensionPath();
|
|
240
|
+
case "talisman": return await getTalismanExtensionPath();
|
|
241
|
+
default: throw new Error(`Unsupported wallet type: ${type}`);
|
|
242
|
+
}
|
|
56
243
|
}
|
|
57
244
|
function createWalletTest(options = {}) {
|
|
58
|
-
const {
|
|
59
|
-
const
|
|
245
|
+
const { headless = false, slowMo = 150 } = options;
|
|
246
|
+
const walletConfigs = options.wallets && options.wallets.length > 0 ? options.wallets : [{ type: "polkadot-js" }];
|
|
247
|
+
const isMultiWallet = walletConfigs.length > 1;
|
|
60
248
|
return test$1.extend({
|
|
61
|
-
|
|
62
|
-
await
|
|
63
|
-
},
|
|
64
|
-
walletConfig: async ({}, use) => {
|
|
65
|
-
await use(finalWalletConfig);
|
|
66
|
-
},
|
|
67
|
-
page: async ({}, use) => {
|
|
68
|
-
const extensionPath = await getExtensionPath(walletType, finalWalletConfig);
|
|
249
|
+
walletContext: [async ({}, use) => {
|
|
250
|
+
const extensionPathsString = (await Promise.all(walletConfigs.map((config) => getExtensionPathForWallet(config)))).join(",");
|
|
69
251
|
const context = await chromium.launchPersistentContext("", {
|
|
70
252
|
headless,
|
|
71
|
-
|
|
253
|
+
channel: "chromium",
|
|
254
|
+
args: [`--load-extension=${extensionPathsString}`, `--disable-extensions-except=${extensionPathsString}`],
|
|
72
255
|
slowMo
|
|
73
256
|
});
|
|
74
|
-
|
|
75
|
-
const extendedPage = page;
|
|
76
|
-
extendedPage.__extensionContext = context;
|
|
77
|
-
let [background] = context.serviceWorkers();
|
|
78
|
-
if (!background) background = await context.waitForEvent("serviceworker");
|
|
79
|
-
extendedPage.__extensionId = background.url().split("/")[2];
|
|
80
|
-
await use(page);
|
|
257
|
+
await use(context);
|
|
81
258
|
await context.close();
|
|
259
|
+
}, { scope: "worker" }],
|
|
260
|
+
walletExtensionIds: [async ({ walletContext }, use) => {
|
|
261
|
+
const extensionIds = /* @__PURE__ */ new Map();
|
|
262
|
+
if (walletContext.serviceWorkers().length === 0) await walletContext.waitForEvent("serviceworker");
|
|
263
|
+
if (isMultiWallet) await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
264
|
+
const allServiceWorkers = walletContext.serviceWorkers();
|
|
265
|
+
for (let i = 0; i < walletConfigs.length && i < allServiceWorkers.length; i++) {
|
|
266
|
+
const extensionId = allServiceWorkers[i].url().split("/")[2];
|
|
267
|
+
const walletType = walletConfigs[i].type;
|
|
268
|
+
extensionIds.set(walletType, extensionId);
|
|
269
|
+
console.log(`✅ Loaded ${walletType} extension with ID: ${extensionId}`);
|
|
270
|
+
}
|
|
271
|
+
await use(extensionIds);
|
|
272
|
+
}, { scope: "worker" }],
|
|
273
|
+
page: async ({ walletContext, walletExtensionIds }, use) => {
|
|
274
|
+
const extendedPage = walletContext.pages()[0] || await walletContext.newPage();
|
|
275
|
+
extendedPage.__extensionContext = walletContext;
|
|
276
|
+
extendedPage.__walletExtensionIds = walletExtensionIds;
|
|
277
|
+
await use(extendedPage);
|
|
82
278
|
},
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const extensionPage = await context.newPage();
|
|
88
|
-
try {
|
|
89
|
-
await extensionPage.goto(extensionPopupUrl);
|
|
90
|
-
const understoodButton = extensionPage.getByRole("button", { name: "Understood, let me continue" });
|
|
91
|
-
if (await understoodButton.count() > 0) {
|
|
92
|
-
await understoodButton.click();
|
|
93
|
-
await extensionPage.waitForTimeout(100);
|
|
94
|
-
}
|
|
95
|
-
await extensionPage.goto(`${extensionPopupUrl}#/account/import-seed`);
|
|
96
|
-
await extensionPage.locator("textarea").fill(seed);
|
|
97
|
-
await extensionPage.locator("button:has-text(\"Next\")").click();
|
|
98
|
-
await extensionPage.locator("input[type=\"text\"]").fill(name);
|
|
99
|
-
await extensionPage.locator("input[type=\"password\"]").fill(password);
|
|
100
|
-
await extensionPage.locator("div").filter({ hasText: /^Repeat password for verification$/ }).getByRole("textbox").fill(password);
|
|
101
|
-
await extensionPage.getByRole("button", { name: "Add the account with the supplied seed" }).click();
|
|
102
|
-
console.log(`✅ Created wallet account: ${name}`);
|
|
103
|
-
} finally {
|
|
104
|
-
await extensionPage.close();
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
await use(importAccount);
|
|
108
|
-
},
|
|
109
|
-
authorize: async ({ page }, use) => {
|
|
110
|
-
const authorize = async () => {
|
|
111
|
-
const context = page.__extensionContext;
|
|
112
|
-
const extensionId = page.__extensionId;
|
|
113
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
114
|
-
const extensionPopup = await findExtensionPopup(context, extensionId);
|
|
115
|
-
await extensionPopup.getByText("Select all").click();
|
|
116
|
-
await extensionPopup.getByRole("button", { name: /Connect \d+ account\(s\)/ }).click();
|
|
117
|
-
console.log("✅ Wallet connected successfully");
|
|
118
|
-
};
|
|
119
|
-
await use(authorize);
|
|
120
|
-
},
|
|
121
|
-
approveTx: async ({ page }, use) => {
|
|
122
|
-
const approveTx = async (options$1 = {}) => {
|
|
123
|
-
const { password = "h3llop0lkadot!" } = options$1;
|
|
124
|
-
const context = page.__extensionContext;
|
|
125
|
-
const extensionId = page.__extensionId;
|
|
126
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
127
|
-
const extensionPopup = await findExtensionPopup(context, extensionId);
|
|
128
|
-
await extensionPopup.getByRole("textbox").fill(password);
|
|
129
|
-
await extensionPopup.getByRole("button", { name: "Sign the transaction" }).click();
|
|
130
|
-
console.log("✅ Transaction signed successfully");
|
|
131
|
-
};
|
|
132
|
-
await use(approveTx);
|
|
279
|
+
wallets: async ({ walletContext, walletExtensionIds }, use) => {
|
|
280
|
+
const walletMap = {};
|
|
281
|
+
for (const [walletType, extensionId] of walletExtensionIds) if (WALLET_TYPES.includes(walletType)) walletMap[walletType] = createWalletInstance(walletType, extensionId, walletContext);
|
|
282
|
+
await use(walletMap);
|
|
133
283
|
}
|
|
134
284
|
});
|
|
135
285
|
}
|
|
136
286
|
const test = createWalletTest();
|
|
137
287
|
|
|
138
288
|
//#endregion
|
|
139
|
-
export { createWalletTest,
|
|
289
|
+
export { createWalletTest, expect, test };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avalix/chroma",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.7",
|
|
5
5
|
"description": "End-to-end testing library for Polkadot wallet interactions",
|
|
6
6
|
"author": "Avalix Labs",
|
|
7
7
|
"license": "MIT",
|
|
@@ -30,14 +30,19 @@
|
|
|
30
30
|
"main": "./dist/index.js",
|
|
31
31
|
"module": "./dist/index.js",
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
|
+
"bin": {
|
|
34
|
+
"chroma": "./scripts/cli.js"
|
|
35
|
+
},
|
|
33
36
|
"files": [
|
|
34
|
-
"dist"
|
|
37
|
+
"dist",
|
|
38
|
+
"scripts"
|
|
35
39
|
],
|
|
36
40
|
"scripts": {
|
|
37
41
|
"build": "tsdown",
|
|
38
42
|
"dev": "tsdown --watch",
|
|
39
43
|
"lint": "eslint --fix .",
|
|
40
|
-
"prepublishOnly": "npm run build"
|
|
44
|
+
"prepublishOnly": "npm run build",
|
|
45
|
+
"download-extensions": "rm -rf .chroma && tsx scripts/download-extensions.ts"
|
|
41
46
|
},
|
|
42
47
|
"peerDependencies": {
|
|
43
48
|
"@playwright/test": "^1.55.0"
|
|
@@ -51,7 +56,8 @@
|
|
|
51
56
|
"@types/node": "^24.3.3",
|
|
52
57
|
"@types/unzipper": "^0.10.10",
|
|
53
58
|
"eslint": "^9.35.0",
|
|
54
|
-
"tsdown": "latest"
|
|
59
|
+
"tsdown": "latest",
|
|
60
|
+
"tsx": "^4.20.6"
|
|
55
61
|
},
|
|
56
62
|
"publishConfig": {
|
|
57
63
|
"access": "public"
|
package/scripts/cli.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'node:child_process'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import process from 'node:process'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
|
|
10
|
+
const command = process.argv[2]
|
|
11
|
+
|
|
12
|
+
if (command === 'download-extensions') {
|
|
13
|
+
const scriptPath = path.join(__dirname, 'download-extensions.ts')
|
|
14
|
+
|
|
15
|
+
// Use tsx to run TypeScript file
|
|
16
|
+
const child = spawn('npx', ['tsx', scriptPath], {
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
shell: true,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
child.on('exit', (code) => {
|
|
22
|
+
process.exit(code || 0)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
console.log('Unknown command:', command)
|
|
27
|
+
console.log('\nAvailable commands:')
|
|
28
|
+
console.log(' download-extensions - Download wallet extensions for testing')
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
import { downloadAndExtractExtension } from '../src/utils/download-extension.js'
|
|
4
|
+
import { POLKADOT_JS_CONFIG } from '../src/wallets/polkadot-js.js'
|
|
5
|
+
import { TALISMAN_CONFIG } from '../src/wallets/talisman.js'
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log('🚀 Downloading Chroma wallet extensions...\n')
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
// Download Polkadot-JS extension
|
|
12
|
+
await downloadAndExtractExtension({
|
|
13
|
+
downloadUrl: POLKADOT_JS_CONFIG.downloadUrl,
|
|
14
|
+
extensionName: POLKADOT_JS_CONFIG.extensionName,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Download Talisman extension
|
|
18
|
+
await downloadAndExtractExtension({
|
|
19
|
+
downloadUrl: TALISMAN_CONFIG.downloadUrl,
|
|
20
|
+
extensionName: TALISMAN_CONFIG.extensionName,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
console.log('\n✅ All extensions downloaded successfully!')
|
|
24
|
+
console.log('You can now run your Playwright tests.')
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error('\n❌ Failed to download extensions:', error instanceof Error ? error.message : String(error))
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
main()
|