The Polkadot-JS extension is the most popular wallet for Polkadot and Substrate-based chains. Chroma provides automated control over the extension for testing dApps that integrate with Polkadot-JS.
Configuration
Chroma uses Polkadot-JS extension version 0.62.6:
const POLKADOT_JS_CONFIG = {
downloadUrl: 'https://github.com/polkadot-js/extension/releases/download/v0.62.6/master-chrome-build.zip',
extensionName: 'polkadot-extension-0.62.6'
}
The extension is automatically downloaded from the official Polkadot-JS releases when you run npx @avalix/chroma download-extensions.
Setup
Add Polkadot-JS wallet to your test configuration:
import { createWalletTest } from '@avalix/chroma'
const test = createWalletTest({
wallets: [{ type: 'polkadot-js' }]
})
Access the wallet in your tests:
test('connect polkadot-js wallet', async ({ page, wallets }) => {
const wallet = wallets['polkadot-js']
// Use wallet methods
})
Methods
importMnemonic
Imports an account using a seed phrase (mnemonic).
await wallets['polkadot-js'].importMnemonic({
seed: string,
name?: string,
password?: string
}): Promise<void>
The 12 or 24-word mnemonic seed phrase for the account.
name
string
default:"Test Account"
Display name for the account in the wallet.
password
string
default:"h3llop0lkadot!"
Password to encrypt the account. Used when signing transactions.
Example
const DOT_TEST_MNEMONIC = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk'
test.beforeAll(async ({ wallets }) => {
await wallets['polkadot-js'].importMnemonic({
seed: DOT_TEST_MNEMONIC,
name: '// Alice',
})
})
authorize
Authorizes the dApp to connect to the wallet. This approves the connection request popup that appears when a dApp calls web3Enable().
await wallets['polkadot-js'].authorize(): Promise<void>
Example
test('sign transaction on polkadot starter', async ({ page, wallets }) => {
await page.goto('https://polkadot-starter-vue-dedot.vercel.app/')
await page.getByRole('button', { name: /Connect Wallet/i }).click()
// Approve connection in Polkadot-JS popup
await wallets['polkadot-js'].authorize()
await page.getByText('// Alice').click()
})
approveTx
Approves a transaction by entering the password and clicking the sign button.
await wallets['polkadot-js'].approveTx(options?: {
password?: string
}): Promise<void>
options.password
string
default:"h3llop0lkadot!"
Password to unlock the account for signing. Must match the password used during import.
Example
test('sign transaction on polkadot starter', async ({ page, wallets }) => {
// ... connect wallet and navigate to dApp ...
// Trigger transaction in dApp
await page.getByRole('button', { name: 'Sign Transaction' }).nth(3).click()
// Approve in Polkadot-JS popup
await wallets['polkadot-js'].approveTx()
await page.getByText('Processing transaction...').waitFor({ state: 'visible' })
})
rejectTx
Rejects a pending transaction by clicking the cancel button.
await wallets['polkadot-js'].rejectTx(): Promise<void>
Example
test('reject transaction', async ({ page, wallets }) => {
// ... connect wallet and navigate to dApp ...
// Trigger transaction in dApp
await page.getByRole('button', { name: 'Sign Transaction' }).first().click()
// Reject in Polkadot-JS popup
await wallets['polkadot-js'].rejectTx()
await page.getByText('Error: Cancelled').waitFor({ state: 'visible' })
})
Complete example
Here’s a full test that imports an account, connects the wallet, and signs a transaction:
import { createWalletTest } from '@avalix/chroma'
const POLKADOT_DAPP_URL = 'https://polkadot-starter-vue-dedot.vercel.app/'
const ACCOUNT_NAME = '// Alice'
const DOT_TEST_MNEMONIC = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk'
const test = createWalletTest({
headless: false,
})
test.describe('with polkadot-js wallet', () => {
test.beforeAll(async ({ wallets }) => {
await wallets['polkadot-js'].importMnemonic({
seed: DOT_TEST_MNEMONIC,
name: ACCOUNT_NAME,
})
})
test('sign transaction on polkadot starter', async ({ page, wallets }) => {
await page.goto(POLKADOT_DAPP_URL)
await page.waitForLoadState('networkidle')
await page.getByRole('button', { name: /Connect Wallet/i }).click()
const modalVisible = await page.locator('h2:has-text("CONNECT WALLET")').isVisible()
if (modalVisible) {
await page.getByRole('button', { name: /CONNECT/i }).nth(2).click()
}
await wallets['polkadot-js'].authorize()
await page.getByText(ACCOUNT_NAME).click()
// Reject transaction
await page.getByRole('button', { name: 'Sign Transaction' }).first().click()
await wallets['polkadot-js'].rejectTx()
await page.getByText('Error: Cancelled').waitFor({ state: 'visible' })
// Sign transaction
await page.getByRole('button', { name: 'Sign Transaction' }).nth(3).click()
await wallets['polkadot-js'].approveTx()
await page.getByText('Processing transaction...').waitFor({ state: 'visible' })
})
})
Extension path helper
Chroma provides a helper to get the extension path for advanced use cases:
import { getPolkadotJSExtensionPath } from '@avalix/chroma/wallets/polkadot-js'
const extensionPath = await getPolkadotJSExtensionPath()
// Returns: /path/to/project/.chroma/polkadot-extension-0.62.6
If the extension hasn’t been downloaded, getPolkadotJSExtensionPath() will throw an error instructing you to run npx @avalix/chroma download-extensions.
Implementation details
Chroma automatically finds and interacts with the Polkadot-JS extension popup using retry logic:
- Maximum 10 attempts with 500ms delay between retries
- Searches for pages matching
chrome-extension://{extensionId}/
- Waits for
domcontentloaded state before interaction
Password management
The default password h3llop0lkadot! is used for test accounts. You can override this when importing accounts:
await wallets['polkadot-js'].importMnemonic({
seed: 'your mnemonic here',
name: 'My Account',
password: 'custom-password-123'
})
// Use the same password when approving transactions
await wallets['polkadot-js'].approveTx({ password: 'custom-password-123' })
TypeScript types
The Polkadot-JS wallet instance type is auto-inferred:
import type { PolkadotJsWalletInstance } from '@avalix/chroma'
type PolkadotJsWallet = {
extensionId: string
type: 'polkadot-js'
importMnemonic: (options: WalletAccount) => Promise<void>
authorize: () => Promise<void>
approveTx: (options?: { password?: string }) => Promise<void>
rejectTx: () => Promise<void>
}