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.
readonly — set via page.evaluate() to bypass Playwright's refusal to fill readonly inputsreadonly — direct DOM injection with input + change events dispatcheddisabled — clicked via evaluate() after removing the disabled attribute#entropy_id insteadwaitForTimeout(3000) pause follows every derivation trigger#bip32_passphrase_id#entropy_id and trigger derivation without Refresh#address_idaccount=2, index=5, passphrase is validVerify 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).
| # | Action | Method |
|---|---|---|
| 1 | Set account to 2 | setFieldValue(page, 'account_id', '2') |
| 2 | Set address index to 5 | setFieldValue(page, 'address_index_id', '5') |
| 3 | Set BIP39 passphrase to "my secret passphrase" | setBip39Passphrase(page, '...') |
| 4 | Click Refresh to trigger derivation | clickRefresh(page) |
| 5 | Read generated address | getDisplayedAddress(page) |
- ✗Empty address — derivation did not complete within the 3s timeout
- ✗Regex mismatch — wrong blockchain selected, or unexpected address format returned
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.
| # | Action | Method |
|---|---|---|
| 1 | Set account=2, address index=5 | setFieldValue(…) × 2 |
| 2 | Set passphrase to "" (empty) | setBip39Passphrase(page, '') |
| 3 | Click Refresh → read addressWithoutPassphrase | clickRefresh + getDisplayedAddress |
| 4 | Set passphrase to "my secret passphrase" | setBip39Passphrase(page, '...') |
| 5 | Click Refresh → read addressWithPassphrase | clickRefresh + getDisplayedAddress |
- ✗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
account produces a different address
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.
| # | Action | Method |
|---|---|---|
| 1 | Set address index=5, passphrase to "my secret passphrase" | setFieldValue + setBip39Passphrase |
| 2 | Set account=0, click Refresh → addressAccount0 | setFieldValue + clickRefresh + getDisplayedAddress |
| 3 | Set account=2, click Refresh → addressAccount2 | setFieldValue + clickRefresh + getDisplayedAddress |
- ✗Both addresses equal — the account field change is not propagated to the BIP44 derivation engine
address index produces a different addressVerify 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.
| # | Action | Method |
|---|---|---|
| 1 | Set account=2, passphrase to "my secret passphrase" | setFieldValue + setBip39Passphrase |
| 2 | Set address index=0, click Refresh → addressIndex0 | setFieldValue + clickRefresh + getDisplayedAddress |
| 3 | Set address index=5, click Refresh → addressIndex5 | setFieldValue + clickRefresh + getDisplayedAddress |
- ✗Both addresses equal — the address index change is not propagated to the BIP44 derivation engine
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.
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
| # | Action | Method |
|---|---|---|
| 1 | Set account=2, index=5, passphrase | setFieldValue × 2 + setBip39Passphrase |
| 2 | Inject fixed entropy → trigger derivation → address1 | deriveFromFixedEntropy + getDisplayedAddress |
| 3 | Re-inject same account, index, passphrase | setFieldValue × 2 + setBip39Passphrase |
| 4 | Inject same fixed entropy again → address2 | deriveFromFixedEntropy + getDisplayedAddress |
- ✗address1 ≠ address2 — derivation is not deterministic; serious BIP32 implementation bug
- ✗Empty address — derivation waterfall not triggered by the
changeevent on#entropy_id