Playwright · Electron E2E

E2E Test Protocols
HD Wallet Use Case

File  tests/playwright/e2e/hd_wallet_usecase.test.js
Suite  Use Case - HD Wallet : account, address index, BIP39 passphrase
Tests  5
Blockchain  Bitcoin
General Context

These tests drive the Cryptocalc application as a real user would, through its Electron GUI. Each test launches a fresh instance of the application, performs UI interactions, and verifies the resulting state of the wallet fields.

Key Technical Constraints
#account_id, #address_index_id
Fields are readonly — set via page.evaluate() to bypass Playwright's refusal to fill readonly inputs
#bip32_passphrase_id
Also readonly — direct DOM injection with input + change events dispatched
#refresh_btn_id
May be disabled — clicked via evaluate() after removing the disabled attribute
Refresh regenerates entropy
Each click produces new random entropy — determinism tests inject directly into #entropy_id instead
BIP32 derivation is async
A waitForTimeout(3000) pause follows every derivation trigger
Shared Helpers
switchToHDWallet(page)Navigate to Wallet tab, select "HD Wallet" mode
setFieldValue(page, id, val)Inject a value into any field (readonly-safe), dispatches input + change
setBip39Passphrase(page, val)Wraps setFieldValue for #bip32_passphrase_id
clickRefresh(page)Trigger Refresh via evaluate() — generates new random entropy
deriveFromFixedEntropy(page, e)Inject entropy into #entropy_id and trigger derivation without Refresh
getDisplayedAddress(page)Read the current text content of #address_id
Test Protocols
01
Format Validation
Bitcoin address generated with account=2, index=5, passphrase is valid
Objective

Verify that the HD Wallet derivation produces a well-formed Bitcoin address when given a non-default account, address index, and a BIP39 passphrase. The address must match one of the three valid Bitcoin address formats: Legacy (P2PKH), P2SH, or Bech32 (SegWit).

Preconditions
App launched & ready Wallet tab active Mode: HD Wallet Blockchain: Bitcoin
Steps
#ActionMethod
1Set account to 2setFieldValue(page, 'account_id', '2')
2Set address index to 5setFieldValue(page, 'address_index_id', '5')
3Set BIP39 passphrase to "my secret passphrase"setBip39Passphrase(page, '...')
4Click Refresh to trigger derivationclickRefresh(page)
5Read generated addressgetDisplayedAddress(page)
Expected Result
/^(1[a-zA-HJ-NP-Z0-9]{25,34} ← Legacy P2PKH |3[a-zA-HJ-NP-Z0-9]{25,34} ← P2SH |bc1[a-z0-9]{6,87})$/ ← Bech32 SegWit
Failure Scenarios
  • Empty address — derivation did not complete within the 3s timeout
  • Regex mismatch — wrong blockchain selected, or unexpected address format returned
02
Passphrase Effect
BIP39 passphrase modifies the derived address (same account/index)
Objective

Verify that the BIP39 passphrase acts as a second factor in HD derivation. With all other parameters identical, a non-empty passphrase must produce a different address than an empty passphrase, demonstrating that the passphrase is actually used in the BIP39 seed computation.

Preconditions
App launched & ready Wallet tab active Mode: HD Wallet Blockchain: Bitcoin
Steps
#ActionMethod
1Set account=2, address index=5setFieldValue(…) × 2
2Set passphrase to "" (empty)setBip39Passphrase(page, '')
3Click Refresh → read addressWithoutPassphraseclickRefresh + getDisplayedAddress
4Set passphrase to "my secret passphrase"setBip39Passphrase(page, '...')
5Click Refresh → read addressWithPassphraseclickRefresh + getDisplayedAddress
Expected Result
addressWithPassphrase ≠ addressWithoutPassphrase
Failure Scenarios
  • Both addresses equal — passphrase is not being incorporated in the BIP39 seed computation
  • Note: absolute address values differ between runs (random entropy per Refresh); only the inequality within a single run is asserted
03
Account Level
Different account produces a different address
Objective

Verify that the BIP44 account level of the derivation path affects the output. Changing the account value while keeping all other parameters constant must produce a different address, confirming that the derivation path m / purpose' / coin_type' / account' / change / index is correctly parameterized.

Preconditions
App launched & ready Wallet tab active Mode: HD Wallet Blockchain: Bitcoin
Steps
#ActionMethod
1Set address index=5, passphrase to "my secret passphrase"setFieldValue + setBip39Passphrase
2Set account=0, click Refresh → addressAccount0setFieldValue + clickRefresh + getDisplayedAddress
3Set account=2, click Refresh → addressAccount2setFieldValue + clickRefresh + getDisplayedAddress
Expected Result
addressAccount0 ≠ addressAccount2
Failure Scenarios
  • Both addresses equal — the account field change is not propagated to the BIP44 derivation engine
04
Address Index Level
Different address index produces a different address
Objective

Verify that the BIP44 address index level of the derivation path affects the output. Changing the address index while keeping all other parameters constant must produce a different address, confirming that address enumeration within an account works correctly.

Preconditions
App launched & ready Wallet tab active Mode: HD Wallet Blockchain: Bitcoin
Steps
#ActionMethod
1Set account=2, passphrase to "my secret passphrase"setFieldValue + setBip39Passphrase
2Set address index=0, click Refresh → addressIndex0setFieldValue + clickRefresh + getDisplayedAddress
3Set address index=5, click Refresh → addressIndex5setFieldValue + clickRefresh + getDisplayedAddress
Expected Result
addressIndex0 ≠ addressIndex5
Failure Scenarios
  • Both addresses equal — the address index change is not propagated to the BIP44 derivation engine
05
Determinism
Same entropy + same params always produce the same address
Objective

Verify that HD Wallet derivation is fully deterministic: given the same entropy, the same BIP39 passphrase, the same account and the same address index, the derived address is always identical across multiple derivations.

Design Note
Why Refresh cannot be used here: clickRefresh() regenerates random entropy on every call — two consecutive Refresh calls will always produce different addresses by design. This is not a bug.

Instead, this test injects a fixed 256-bit entropy value directly into #entropy_id and dispatches input + change events to trigger the BIP32 derivation waterfall, bypassing the random entropy generation step entirely. a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 64 hex chars · 256 bits · valid BIP39 entropy
Preconditions
App launched & ready Wallet tab active Mode: HD Wallet Blockchain: Bitcoin
Steps
#ActionMethod
1Set account=2, index=5, passphrasesetFieldValue × 2 + setBip39Passphrase
2Inject fixed entropy → trigger derivation → address1deriveFromFixedEntropy + getDisplayedAddress
3Re-inject same account, index, passphrasesetFieldValue × 2 + setBip39Passphrase
4Inject same fixed entropy again → address2deriveFromFixedEntropy + getDisplayedAddress
Expected Result
address1 = address2 (non-empty)
Failure Scenarios
  • address1 ≠ address2 — derivation is not deterministic; serious BIP32 implementation bug
  • Empty address — derivation waterfall not triggered by the change event on #entropy_id
Summary
#TitleAssertionBlockchain
01 Bitcoin address format validation regex Bitcoin
02 Passphrase modifies the derived address inequality Bitcoin
03 Different account produces a different address inequality Bitcoin
04 Different address index produces a different address inequality Bitcoin
05 Same entropy + same params → same address (determinism) equality Bitcoin