Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6aa2155
feat(accounts): surface authAddress in on-chain account information
fmsouza May 18, 2026
02fa617
test(accounts): consolidate mapper tests into mappers.spec.ts
fmsouza May 18, 2026
be5a86f
feat(accounts): add useRekeyedAddressesQuery
fmsouza May 18, 2026
d08caba
feat(accounts): add useLedgerAccountPreview composite hook
fmsouza May 18, 2026
36390b2
test(accounts): cover missing-asset-metadata fallback in useLedgerAcc…
fmsouza May 18, 2026
0845b32
feat(accounts): add prefetchLedgerAccountPreview util
fmsouza May 18, 2026
d595e0b
test(accounts): disable query retries in prefetchLedgerAccountPreview…
fmsouza May 18, 2026
efdcdfd
feat(ledger): add useLedgerAccountInfoContent presentation hook
fmsouza May 18, 2026
302d485
refactor(ledger): carry Decimal through useLedgerAccountInfoContent (…
fmsouza May 18, 2026
1fbee7d
feat(ledger): add LedgerAccountInfoContent bottom-sheet component
fmsouza May 18, 2026
9103107
refactor(ledger): import LedgerAccountPreviewAsset from package, drop…
fmsouza May 18, 2026
9d16bbc
style(ledger): fix import order and memoize renderItem in LedgerAccou…
fmsouza May 18, 2026
7381742
feat(ledger): native-parity row layout, info affordance, preview pref…
fmsouza May 18, 2026
82d402e
fix(ledger): use PWTouchableIcon for tappable info and close icons
fmsouza May 18, 2026
602df8d
test(ledger): integration test for the account info sheet
fmsouza May 18, 2026
4b5eed9
test(ledger): move ledger-account svg stub to vitest.integration-setup
fmsouza May 18, 2026
5e56913
style(ledger): apply formatter and copyright-header fixes
fmsouza May 18, 2026
69f91aa
fix(ledger): carry asset decimals for display precision; strengthen i…
fmsouza May 18, 2026
07e44fd
feat(accounts): add LedgerSelectableAccount model + useLedgerRekeyedScan
fmsouza May 18, 2026
7b8e138
feat(ledger): optional title override for the account info sheet
fmsouza May 18, 2026
9a8cafc
docs(ledger): document titleOverride param
fmsouza May 18, 2026
67d860d
feat(ledger): thread LedgerSelectableAccount union select→verify→import
fmsouza May 18, 2026
99f4534
fix(ledger): never persist a rekeyed watch account without its auth L…
fmsouza May 18, 2026
6a40db8
feat(ledger): surface rekeyed accounts in Ledger import with auth aut…
fmsouza May 18, 2026
0315cfe
fix(ledger): robust isAllSelected under progressive rekeyed append; c…
fmsouza May 18, 2026
3fbbbbc
feat(ledger): i18n for rekeyed account import
fmsouza May 18, 2026
847d76d
style(ledger): formatter and copyright-header fixups (sub-project B)
fmsouza May 18, 2026
27d95b0
test(ledger): integration test for rekeyed-account import
fmsouza May 18, 2026
0e6593a
style(ledger): copyright-header fixups for sub-project B test harness
fmsouza May 18, 2026
9eef2a7
feat(accounts): expose per-asset usdPrice on LedgerAccountPreviewAsset
fmsouza May 19, 2026
11f649c
fix(ledger): render account info sheet with canonical AccountDisplay …
fmsouza May 19, 2026
93d3ee9
fix(ledger): show account icon via logicalTypeOverride for preview ac…
fmsouza May 19, 2026
80b305f
fix(ledger): always render checkbox on account rows (checked+disabled…
fmsouza May 19, 2026
6752fcc
style(ledger): copyright-header fixups for UI fixes
fmsouza May 19, 2026
1afab31
fix(ledger): remove copy-address from the accounts-found row
fmsouza May 19, 2026
f33bfc2
fix(ledger): address code-review findings on the account info sheet
fmsouza May 19, 2026
2d4ea4b
fix(ledger): bind connecting-sheet Cancel to a stable close handler
fmsouza May 20, 2026
2fa5cf1
fix(ledger): address second-pass review findings
fmsouza May 20, 2026
a30c983
chore: fmt
fmsouza May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions apps/mobile/src/__integration__/ledger-account-info-sheet.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Copyright 2022-2025 Pera Wallet, LDA
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
*/

import {
afterAll,
afterEach,
beforeAll,
beforeEach,
describe,
expect,
it,
} from 'vitest'
import { fireEvent, screen, waitFor } from '@testing-library/react'

import { server } from '@test-utils/msw-server'
import { renderWithNavigation } from '@test-utils/renderWithNavigation'
import { resetTestKeystore } from '@test-utils/algorand-keystore-test'
import {
resetTestDatabase,
seedAlgoAsset,
setupTestDatabase,
teardownTestDatabase,
} from '@test-utils/database-setup'
import { useAccountsStore } from '@perawallet/wallet-core-accounts'
import {
mockAlgodAccountInformation,
mockAlgodStatus,
mockIndexerSearchForAccounts,
} from '@perawallet/wallet-core-blockchain/test-handlers'
import { LedgerSelectAccountsScreen } from '@modules/ledger/screens/LedgerSelectAccountsScreen'

import { HD_TEST_ADDRESS } from './__fixtures__/onboarding'

const SLOW_TEST_TIMEOUT_MS = 30000

const LEDGER_ADDRESS = HD_TEST_ADDRESS

describe('Flow: Ledger account info sheet', () => {
beforeAll(async () => {
server.listen({ onUnhandledRequest: 'warn' })
await setupTestDatabase()
})
afterEach(() => server.resetHandlers())
afterAll(async () => {
server.close()
await teardownTestDatabase()
})

beforeEach(async () => {
await resetTestDatabase()
await seedAlgoAsset('mainnet')
resetTestKeystore()
useAccountsStore.getState().setAccounts([])

server.use(
mockAlgodAccountInformation({
address: LEDGER_ADDRESS,
response: { amount: 408_200_000, 'min-balance': 100_000 },
}),
mockAlgodStatus({ response: { 'last-round': 100 } }),
mockIndexerSearchForAccounts(),
)
})

it(
'Given a discovered Ledger account, when the user taps the ⓘ affordance, then the account info sheet opens and shows the Ledger title and account info list',
async () => {
renderWithNavigation(
LedgerSelectAccountsScreen,
'LedgerSelectAccounts',
{
initialParams: {
deviceId: 'test-device-id',
deviceName: 'Ledger Nano X',
transportType: 'ble',
accounts: [
{
address: LEDGER_ADDRESS,
publicKey: new Uint8Array([1]),
accountIndex: 0,
},
],
},
},
)

// Assert the ⓘ affordance renders for the discovered account
const infoButton = await screen.findByTestId(
`ledger_select_row_${LEDGER_ADDRESS}-info`,
)

// Sheet should NOT be open initially
expect(screen.queryByTestId('ledger_account_info_list')).toBeNull()

// Tap the ⓘ button to open the info sheet
fireEvent.click(infoButton)

// Sheet opened and the list rendered
await waitFor(
() =>
expect(
screen.getByTestId('ledger_account_info_list'),
).toBeTruthy(),
{ timeout: 10000 },
)

// The sheet title is the i18n key (integration harness doesn't
// initialise i18n — t() returns the raw key, see comment below).
await waitFor(
() =>
expect(
screen.getByText('ledger.account_info.default_title'),
).toBeTruthy(),
{ timeout: 10000 },
)

// Section headers should be rendered.
// Note: i18n is not initialised in the integration test harness,
// so t() returns the raw key — assert on what is actually rendered.
await waitFor(
() => {
expect(
screen.getByText('ledger.account_info.account_details'),
).toBeTruthy()
expect(
screen.getByText('ledger.account_info.assets'),
).toBeTruthy()
},
{ timeout: 10000 },
)
},
SLOW_TEST_TIMEOUT_MS,
)
})
134 changes: 134 additions & 0 deletions apps/mobile/src/__integration__/ledger-imported-account-row.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
Copyright 2022-2025 Pera Wallet, LDA
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
*/

import {
afterAll,
afterEach,
beforeAll,
beforeEach,
describe,
expect,
it,
} from 'vitest'
import { screen, waitFor } from '@testing-library/react'

import { server } from '@test-utils/msw-server'
import { renderWithNavigation } from '@test-utils/renderWithNavigation'
import { resetTestKeystore } from '@test-utils/algorand-keystore-test'
import {
resetTestDatabase,
seedAlgoAsset,
setupTestDatabase,
teardownTestDatabase,
} from '@test-utils/database-setup'
import {
AccountTypes,
useAccountsStore,
} from '@perawallet/wallet-core-accounts'
import type { HardwareWalletAccount } from '@perawallet/wallet-core-accounts'
import {
mockAlgodAccountInformation,
mockAlgodStatus,
mockIndexerSearchForAccounts,
} from '@perawallet/wallet-core-blockchain/test-handlers'
import { LedgerSelectAccountsScreen } from '@modules/ledger/screens/LedgerSelectAccountsScreen'

import { HD_TEST_ADDRESS } from './__fixtures__/onboarding'

const SLOW_TEST_TIMEOUT_MS = 30000

const LEDGER_ADDRESS = HD_TEST_ADDRESS

describe('Flow: Ledger imported account row checkbox', () => {
beforeAll(async () => {
server.listen({ onUnhandledRequest: 'warn' })
await setupTestDatabase()
})
afterEach(() => server.resetHandlers())
afterAll(async () => {
server.close()
await teardownTestDatabase()
})

beforeEach(async () => {
await resetTestDatabase()
await seedAlgoAsset('mainnet')
resetTestKeystore()
useAccountsStore.getState().setAccounts([])

useAccountsStore.getState().setAccounts([
{
type: AccountTypes.hardware,
address: LEDGER_ADDRESS,
hardwareDetails: {
manufacturer: 'ledger',
deviceId: 'd',
deviceName: 'Ledger Nano X',
accountIndex: 0,
transportType: 'ble',
},
} satisfies HardwareWalletAccount,
])

server.use(
mockAlgodAccountInformation({
address: LEDGER_ADDRESS,
response: { amount: 408_200_000, 'min-balance': 100_000 },
}),
mockAlgodStatus({ response: { 'last-round': 100 } }),
mockIndexerSearchForAccounts(),
)
})

it(
'Given an already-imported Ledger account discovered on the select-accounts screen, the row renders its checkbox (checked and disabled)',
async () => {
renderWithNavigation(
LedgerSelectAccountsScreen,
'LedgerSelectAccounts',
{
initialParams: {
deviceId: 'test-device-id',
deviceName: 'Ledger Nano X',
transportType: 'ble',
accounts: [
{
address: LEDGER_ADDRESS,
publicKey: new Uint8Array([1]),
accountIndex: 0,
},
],
},
},
)

const checkbox = await waitFor(
() =>
screen.getByTestId(
`ledger_select_row_${LEDGER_ADDRESS}-checkbox`,
),
{ timeout: 10000 },
)

// The "already imported" chip renders under the same `isImported`
// flag that forces the checkbox to checked + disabled in
// LedgerAccountSelectionRow, so its presence is the faithful
// assertion of this scenario. (Integration tests run without
// i18n, so the chip renders the raw key.)
expect(checkbox).not.toBeNull()
expect(
screen.queryByText('ledger.select_accounts.already_imported'),
).not.toBeNull()
},
SLOW_TEST_TIMEOUT_MS,
)
})
Loading
Loading