@avalix/chroma 0.0.11 → 0.0.13
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 +27 -228
- package/dist/index.d.mts +28 -21
- package/dist/index.mjs +123 -101
- package/package.json +5 -3
- package/scripts/download-extensions.ts +3 -3
- package/src/context-playwright/index.test.ts +148 -0
- package/src/context-playwright/index.ts +17 -10
- package/src/context-playwright/types.ts +8 -26
- package/src/context-playwright/wallet-factory.test.ts +106 -0
- package/src/context-playwright/wallet-factory.ts +72 -96
- package/src/index.test.ts +57 -0
- package/src/utils/download-extension.ts +1 -1
- package/src/wallets/polkadot-js.test.ts +97 -0
- package/src/wallets/polkadot-js.ts +15 -1
- package/src/wallets/talisman.test.ts +97 -0
- package/src/wallets/talisman.ts +121 -53
package/src/wallets/talisman.ts
CHANGED
|
@@ -12,7 +12,11 @@ export const TALISMAN_CONFIG = {
|
|
|
12
12
|
extensionName: `talisman-extension-${VERSION}`,
|
|
13
13
|
} as const
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/*
|
|
16
|
+
* Helper function to find extension popup
|
|
17
|
+
* Coverage excluded: requires real browser context with Chrome extension APIs.
|
|
18
|
+
*/
|
|
19
|
+
/* c8 ignore start */
|
|
16
20
|
async function findExtensionPopup(context: BrowserContext, extensionId: string): Promise<Page> {
|
|
17
21
|
// Wait for extension popup to appear with retry logic
|
|
18
22
|
const maxAttempts = 10
|
|
@@ -36,6 +40,7 @@ async function findExtensionPopup(context: BrowserContext, extensionId: string):
|
|
|
36
40
|
|
|
37
41
|
throw new Error(`Extension popup not found for ID: ${extensionId}`)
|
|
38
42
|
}
|
|
43
|
+
/* c8 ignore stop */
|
|
39
44
|
|
|
40
45
|
// Get Talisman extension path
|
|
41
46
|
export async function getTalismanExtensionPath(): Promise<string> {
|
|
@@ -54,65 +59,121 @@ export async function getTalismanExtensionPath(): Promise<string> {
|
|
|
54
59
|
return extensionDir
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
/*
|
|
63
|
+
* Wallet interaction functions below are excluded from coverage because:
|
|
64
|
+
* - They require a real Chromium browser with extension support
|
|
65
|
+
* - They interact with Chrome extension popup pages
|
|
66
|
+
*/
|
|
67
|
+
/* c8 ignore start */
|
|
68
|
+
|
|
69
|
+
// Helper function to find Talisman onboarding page
|
|
70
|
+
async function findOnboardingPage(
|
|
71
|
+
context: BrowserContext,
|
|
72
|
+
extensionId: string,
|
|
73
|
+
): Promise<Page> {
|
|
74
|
+
// Open new dashboard page
|
|
75
|
+
const popupUrl = `chrome-extension://${extensionId}/dashboard.html`
|
|
76
|
+
const newPage = await context.newPage()
|
|
77
|
+
await newPage.goto(popupUrl)
|
|
78
|
+
await newPage.waitForLoadState('domcontentloaded')
|
|
79
|
+
|
|
80
|
+
// Close any other extension tabs that may have been opened automatically
|
|
81
|
+
for (const p of context.pages()) {
|
|
82
|
+
if (p !== newPage && p.url().includes(`chrome-extension://${extensionId}/`)) {
|
|
83
|
+
await p.close()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return newPage
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Helper function to complete Talisman onboarding flow
|
|
91
|
+
async function completeOnboarding(
|
|
92
|
+
extensionPage: Page,
|
|
93
|
+
password: string,
|
|
94
|
+
): Promise<void> {
|
|
95
|
+
// Bring the onboarding page to front
|
|
96
|
+
await extensionPage.bringToFront()
|
|
97
|
+
|
|
98
|
+
// Wait for the page to load and become interactive
|
|
99
|
+
await extensionPage.waitForLoadState('domcontentloaded')
|
|
100
|
+
|
|
101
|
+
if (await extensionPage.getByRole('button', { name: 'Settings' }).isVisible()) {
|
|
102
|
+
await extensionPage.getByRole('button', { name: 'Settings' }).click({ force: true })
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Click the get started button
|
|
107
|
+
await extensionPage.getByTestId('onboarding-get-started-button').click()
|
|
108
|
+
|
|
109
|
+
// Fill the password
|
|
110
|
+
await extensionPage.getByRole('textbox', { name: 'Enter password' }).fill(password)
|
|
111
|
+
await extensionPage.getByRole('textbox', { name: 'Confirm password' }).fill(password)
|
|
112
|
+
await extensionPage.getByTestId('onboarding-password-confirm-button').click()
|
|
113
|
+
|
|
114
|
+
// Click the no thanks button
|
|
115
|
+
await extensionPage.getByRole('button', { name: 'No thanks' }).click()
|
|
116
|
+
await extensionPage.getByTestId('onboarding-enter-talisman-button').click()
|
|
117
|
+
|
|
118
|
+
// Navigate directly to settings/general page
|
|
119
|
+
const extensionId = extensionPage.url().match(/chrome-extension:\/\/([^/]+)/)?.[1]
|
|
120
|
+
await extensionPage.goto(`chrome-extension://${extensionId}/dashboard.html#/settings/general`)
|
|
121
|
+
await extensionPage.waitForLoadState('domcontentloaded')
|
|
122
|
+
await extensionPage.getByRole('link', { name: 'Security & Privacy' }).click()
|
|
123
|
+
|
|
124
|
+
// Toggle the risk scan setting
|
|
125
|
+
await extensionPage.getByTestId('component-toggle-button').first().click()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Talisman specific Polkadot mnemonic import implementation
|
|
129
|
+
export async function importPolkadotMnemonic(
|
|
59
130
|
page: Page & { __extensionContext: BrowserContext, __extensionId: string },
|
|
60
131
|
{ seed, name = 'Test Account', password = 'h3llop0lkadot!' }: WalletAccount,
|
|
61
132
|
): Promise<void> {
|
|
62
133
|
const context = page.__extensionContext
|
|
63
134
|
const extensionId = page.__extensionId
|
|
64
135
|
|
|
65
|
-
|
|
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()
|
|
136
|
+
const extensionPage = await findOnboardingPage(context, extensionId)
|
|
72
137
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (url.includes('onboarding.html') || url.includes(`chrome-extension://${extensionId}/`)) {
|
|
76
|
-
extensionPage = p
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
}
|
|
138
|
+
try {
|
|
139
|
+
await completeOnboarding(extensionPage, password!)
|
|
80
140
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
141
|
+
// Import Polkadot account via Recovery Phrase
|
|
142
|
+
await extensionPage.getByRole('link', { name: 'Manage Accounts' }).click()
|
|
143
|
+
await extensionPage.getByRole('button', { name: 'Get Started' }).click()
|
|
144
|
+
await extensionPage.getByRole('button', { name: 'Add Account' }).click()
|
|
145
|
+
await extensionPage.getByRole('button', { name: 'Import Import an existing' }).click()
|
|
146
|
+
await extensionPage.getByRole('button', { name: 'Import via Recovery Phrase' }).click()
|
|
147
|
+
await extensionPage.getByRole('button', { name: 'Polkadot Relay Chain, Asset' }).click()
|
|
148
|
+
await extensionPage.getByRole('textbox', { name: 'Choose a name' }).fill(name!)
|
|
149
|
+
await extensionPage.getByRole('textbox', { name: 'Enter your 12 or 24 word' }).fill(seed!)
|
|
150
|
+
await extensionPage.getByTestId('account-add-mnemonic-import-button').click()
|
|
84
151
|
|
|
85
|
-
|
|
86
|
-
if (attempt < maxAttempts - 1) {
|
|
87
|
-
await new Promise(resolve => setTimeout(resolve, retryDelay))
|
|
88
|
-
}
|
|
152
|
+
await extensionPage.close()
|
|
89
153
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
throw
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error('❌ Error during Talisman Polkadot account import:', error)
|
|
156
|
+
throw error
|
|
93
157
|
}
|
|
158
|
+
}
|
|
94
159
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Click the get started button
|
|
103
|
-
await extensionPage.getByTestId('onboarding-get-started-button').click()
|
|
160
|
+
// Talisman specific Ethereum private key import implementation
|
|
161
|
+
export async function importEthPrivateKey(
|
|
162
|
+
page: Page & { __extensionContext: BrowserContext, __extensionId: string },
|
|
163
|
+
{ seed, name = 'Test Account', password = 'h3llop0lkadot!' }: WalletAccount,
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
const context = page.__extensionContext
|
|
166
|
+
const extensionId = page.__extensionId
|
|
104
167
|
|
|
105
|
-
|
|
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()
|
|
168
|
+
const extensionPage = await findOnboardingPage(context, extensionId)
|
|
109
169
|
|
|
110
|
-
|
|
111
|
-
await extensionPage
|
|
112
|
-
await extensionPage.getByTestId('onboarding-enter-talisman-button').click()
|
|
170
|
+
try {
|
|
171
|
+
await completeOnboarding(extensionPage, password!)
|
|
113
172
|
|
|
114
|
-
// Import Ethereum account
|
|
115
|
-
await extensionPage.getByRole('
|
|
173
|
+
// Import Ethereum account via Private Key
|
|
174
|
+
await extensionPage.getByRole('link', { name: 'Manage Accounts' }).click()
|
|
175
|
+
await extensionPage.getByRole('button', { name: 'Get Started' }).click()
|
|
176
|
+
await extensionPage.getByRole('button', { name: 'Add Account' }).click()
|
|
116
177
|
await extensionPage.getByRole('button', { name: 'Import Import an existing' }).click()
|
|
117
178
|
await extensionPage.getByRole('button', { name: 'Import via Private Key' }).click()
|
|
118
179
|
await extensionPage.getByRole('button', { name: 'Select account platform' }).click()
|
|
@@ -124,7 +185,7 @@ export async function importEthPrivateKey(
|
|
|
124
185
|
await extensionPage.close()
|
|
125
186
|
}
|
|
126
187
|
catch (error) {
|
|
127
|
-
console.error('❌ Error during Talisman account import:', error)
|
|
188
|
+
console.error('❌ Error during Talisman Ethereum account import:', error)
|
|
128
189
|
throw error
|
|
129
190
|
}
|
|
130
191
|
}
|
|
@@ -134,14 +195,18 @@ export async function authorizeTalisman(
|
|
|
134
195
|
page: Page & { __extensionContext: BrowserContext, __extensionId: string },
|
|
135
196
|
options: { accountName?: string } = {},
|
|
136
197
|
): Promise<void> {
|
|
137
|
-
const { accountName = 'Test Account' } = options
|
|
138
198
|
const context = page.__extensionContext
|
|
139
199
|
const extensionId = page.__extensionId
|
|
200
|
+
const { accountName = 'Test Account' } = options
|
|
140
201
|
|
|
141
202
|
const extensionPopup = await findExtensionPopup(context, extensionId)
|
|
203
|
+
await extensionPopup.waitForLoadState('domcontentloaded')
|
|
142
204
|
|
|
143
205
|
// Authorize Talisman account
|
|
144
|
-
|
|
206
|
+
const accountButton = extensionPopup.getByRole('button', { name: accountName })
|
|
207
|
+
await accountButton.waitFor({ state: 'visible' })
|
|
208
|
+
await accountButton.scrollIntoViewIfNeeded()
|
|
209
|
+
await accountButton.click({ force: true })
|
|
145
210
|
await extensionPopup.getByTestId('connection-connect-button').click()
|
|
146
211
|
|
|
147
212
|
try {
|
|
@@ -161,12 +226,9 @@ export async function approveTalismanTx(
|
|
|
161
226
|
|
|
162
227
|
const extensionPopup = await findExtensionPopup(context, extensionId)
|
|
163
228
|
|
|
164
|
-
|
|
229
|
+
if (await extensionPopup.getByRole('button', { name: 'Yes' }).isVisible()) {
|
|
165
230
|
await extensionPopup.getByRole('button', { name: 'Yes' }).click()
|
|
166
231
|
}
|
|
167
|
-
catch {
|
|
168
|
-
console.log('No another popup found, skipping')
|
|
169
|
-
}
|
|
170
232
|
|
|
171
233
|
await extensionPopup.getByRole('button', { name: 'Approve' }).click()
|
|
172
234
|
}
|
|
@@ -179,5 +241,11 @@ export async function rejectTalismanTx(
|
|
|
179
241
|
const extensionId = page.__extensionId
|
|
180
242
|
|
|
181
243
|
const extensionPopup = await findExtensionPopup(context, extensionId)
|
|
182
|
-
|
|
244
|
+
|
|
245
|
+
const rejectButton = extensionPopup.getByTestId('connection-reject-button')
|
|
246
|
+
.or(extensionPopup.getByRole('button', { name: 'Cancel' }))
|
|
247
|
+
|
|
248
|
+
await rejectButton.click()
|
|
183
249
|
}
|
|
250
|
+
|
|
251
|
+
/* c8 ignore stop */
|