11import React from "react" ;
2+ import { View } from "react-native" ;
3+ import type { CurrencyResult } from "@ledgerhq/cryptoassets/hooks" ;
24import { createNativeStackNavigator } from "@react-navigation/native-stack" ;
35import { render , screen , waitFor , within , withFlagOverrides } from "@tests/test-renderer" ;
46import { genAccount } from "@ledgerhq/ledger-wallet-framework/mocks/account" ;
5- import { getCryptoCurrencyById } from "@ledgerhq/live-common/currencies/index" ;
7+ import {
8+ findCryptoCurrencyById ,
9+ getCryptoCurrencyById ,
10+ } from "@ledgerhq/live-common/currencies/index" ;
11+ import { createFixtureTokenAccount } from "@ledgerhq/live-common/mock/fixtures/cryptoCurrencies" ;
612import type { Account } from "@ledgerhq/types-live" ;
7- import { NavigatorName , ScreenName } from "~/const" ;
13+ import type { BaseNavigatorStackParamList } from "~/components/RootNavigator/types/BaseNavigator" ;
14+ import { BASE_NAVIGATOR_ID , NavigatorName , ScreenName } from "~/const" ;
815import type { State } from "~/reducers/types" ;
916import AssetDetailNavigator from "../Navigator" ;
1017import { ASSET_DETAIL_TEST_IDS } from "../testIds" ;
@@ -17,6 +24,7 @@ import {
1724
1825const mockIsCurrencyAvailable = jest . fn ( ) . mockReturnValue ( false ) ;
1926const mockIsAcceptedCurrency = jest . fn ( ) . mockReturnValue ( false ) ;
27+ const mockUseCurrencyById = jest . fn < CurrencyResult , [ string ] > ( ) ;
2028
2129jest . mock ( "@ledgerhq/live-common/platform/providers/RampCatalogProvider/useRampCatalog" , ( ) => ( {
2230 useRampCatalog : ( ) => ( { isCurrencyAvailable : mockIsCurrencyAvailable } ) ,
@@ -26,6 +34,15 @@ jest.mock("@ledgerhq/live-common/modularDrawer/hooks/useAcceptedCurrency", () =>
2634 useAcceptedCurrency : ( ) => mockIsAcceptedCurrency ,
2735} ) ) ;
2836
37+ jest . mock ( "@ledgerhq/cryptoassets/hooks" , ( ) => ( {
38+ useCurrencyById : ( id : string ) => mockUseCurrencyById ( id ) ,
39+ } ) ) ;
40+
41+ jest . mock ( "@features/platform-currencies" , ( ) => ( {
42+ ...jest . requireActual ( "@features/platform-currencies" ) ,
43+ useCurrencyById : ( id : string ) => mockUseCurrencyById ( id ) ,
44+ } ) ) ;
45+
2946jest . mock ( "@ledgerhq/asset-detail" , ( ) => ( {
3047 ...jest . requireActual ( "@ledgerhq/asset-detail" ) ,
3148 useTradeAvailability : jest . fn ( ) ,
@@ -54,7 +71,12 @@ jest.mock("@ledgerhq/live-common/bridge/useAccountBridge", () => ({
5471 ) ,
5572} ) ) ;
5673
57- const Stack = createNativeStackNavigator ( ) ;
74+ const Stack = createNativeStackNavigator < BaseNavigatorStackParamList , typeof BASE_NAVIGATOR_ID > ( ) ;
75+ const MARKET_LIST_TEST_ID = "market-list-screen" ;
76+
77+ function MarketListScreen ( ) {
78+ return < View testID = { MARKET_LIST_TEST_ID } /> ;
79+ }
5880
5981type NavigatorParams = {
6082 currencyId : string ;
@@ -68,7 +90,7 @@ function AssetDetailTestNavigator({
6890 params ?: NavigatorParams ;
6991} = { } ) {
7092 return (
71- < Stack . Navigator >
93+ < Stack . Navigator id = { BASE_NAVIGATOR_ID } >
7294 < Stack . Screen
7395 name = { NavigatorName . AssetDetail }
7496 component = { AssetDetailNavigator }
@@ -78,6 +100,7 @@ function AssetDetailTestNavigator({
78100 } }
79101 options = { { headerShown : false } }
80102 />
103+ < Stack . Screen name = { ScreenName . MarketList } component = { MarketListScreen } />
81104 </ Stack . Navigator >
82105 ) ;
83106}
@@ -125,6 +148,12 @@ function withBlacklistedTokens(tokenIds: string[]) {
125148
126149describe ( "AssetDetail screen layout" , ( ) => {
127150 beforeEach ( ( ) => {
151+ mockUseCurrencyById . mockReset ( ) ;
152+ mockUseCurrencyById . mockImplementation ( ( id : string ) => ( {
153+ currency : findCryptoCurrencyById ( id ) ,
154+ loading : false ,
155+ error : undefined ,
156+ } ) ) ;
128157 mockIsCurrencyAvailable . mockReturnValue ( false ) ;
129158 mockIsAcceptedCurrency . mockReturnValue ( false ) ;
130159 setAvailability ( ) ;
@@ -487,5 +516,117 @@ describe("AssetDetail screen layout", () => {
487516 } ) ;
488517 expect ( screen . getByTestId ( ASSET_DETAIL_TEST_IDS . balanceGraph ) ) . toBeVisible ( ) ;
489518 } ) ;
519+
520+ it . each ( [ "deeplink_asset" , "deeplink_market" ] ) (
521+ "redirects a missing token %s deeplink to Market" ,
522+ async source => {
523+ const tokenId = "ethereum/erc20/does_not_exist" ;
524+ mockUseCurrencyById . mockReturnValue ( {
525+ currency : undefined ,
526+ loading : false ,
527+ error : undefined ,
528+ } ) ;
529+
530+ render (
531+ < AssetDetailTestNavigator
532+ params = { {
533+ currencyId : tokenId ,
534+ source,
535+ marketState : { id : tokenId , ledgerIds : [ tokenId ] } ,
536+ } }
537+ /> ,
538+ ) ;
539+
540+ await waitFor ( ( ) => expect ( screen . getByTestId ( MARKET_LIST_TEST_ID ) ) . toBeVisible ( ) ) ;
541+ expect ( screen . queryByTestId ( ASSET_DETAIL_TEST_IDS . screen ) ) . toBeNull ( ) ;
542+ } ,
543+ ) ;
544+
545+ it ( "keeps a valid token deeplink on Asset Detail" , ( ) => {
546+ const token = createFixtureTokenAccount ( ) . token ;
547+ mockUseCurrencyById . mockReturnValue ( {
548+ currency : token ,
549+ loading : false ,
550+ error : undefined ,
551+ } ) ;
552+
553+ render (
554+ < AssetDetailTestNavigator
555+ params = { {
556+ currencyId : token . id ,
557+ source : "deeplink_asset" ,
558+ marketState : { id : token . id , ledgerIds : [ token . id ] } ,
559+ } }
560+ /> ,
561+ ) ;
562+
563+ expect ( screen . getByTestId ( ASSET_DETAIL_TEST_IDS . screen ) ) . toBeVisible ( ) ;
564+ expect ( screen . queryByTestId ( MARKET_LIST_TEST_ID ) ) . toBeNull ( ) ;
565+ } ) ;
566+
567+ it ( "keeps Asset Detail visible while the token lookup is loading" , ( ) => {
568+ const tokenId = "ethereum/erc20/loading" ;
569+ mockUseCurrencyById . mockReturnValue ( {
570+ currency : undefined ,
571+ loading : true ,
572+ error : undefined ,
573+ } ) ;
574+
575+ render (
576+ < AssetDetailTestNavigator
577+ params = { {
578+ currencyId : tokenId ,
579+ source : "deeplink_asset" ,
580+ marketState : { id : tokenId , ledgerIds : [ tokenId ] } ,
581+ } }
582+ /> ,
583+ ) ;
584+
585+ expect ( screen . getByTestId ( ASSET_DETAIL_TEST_IDS . screen ) ) . toBeVisible ( ) ;
586+ expect ( screen . queryByTestId ( MARKET_LIST_TEST_ID ) ) . toBeNull ( ) ;
587+ } ) ;
588+
589+ it ( "keeps the Asset Detail screen on token lookup errors" , ( ) => {
590+ const tokenId = "ethereum/erc20/unavailable" ;
591+ mockUseCurrencyById . mockReturnValue ( {
592+ currency : undefined ,
593+ loading : false ,
594+ error : new Error ( "CAL unavailable" ) ,
595+ } ) ;
596+
597+ render (
598+ < AssetDetailTestNavigator
599+ params = { {
600+ currencyId : tokenId ,
601+ source : "deeplink_market" ,
602+ marketState : { id : tokenId , ledgerIds : [ tokenId ] } ,
603+ } }
604+ /> ,
605+ ) ;
606+
607+ expect ( screen . getByTestId ( ASSET_DETAIL_TEST_IDS . screen ) ) . toBeVisible ( ) ;
608+ expect ( screen . queryByTestId ( MARKET_LIST_TEST_ID ) ) . toBeNull ( ) ;
609+ } ) ;
610+
611+ it ( "keeps market-only assets on Asset Detail outside deeplinks" , ( ) => {
612+ mockUseCurrencyById . mockReturnValue ( {
613+ currency : undefined ,
614+ loading : false ,
615+ error : undefined ,
616+ } ) ;
617+
618+ render (
619+ < AssetDetailTestNavigator
620+ params = { {
621+ currencyId : "market-only-asset" ,
622+ source : "market_banner" ,
623+ marketState : { id : "market-only-asset" } ,
624+ } }
625+ /> ,
626+ ) ;
627+
628+ expect ( screen . getByTestId ( ASSET_DETAIL_TEST_IDS . screen ) ) . toBeVisible ( ) ;
629+ expect ( screen . queryByTestId ( MARKET_LIST_TEST_ID ) ) . toBeNull ( ) ;
630+ } ) ;
490631 } ) ;
491632} ) ;
0 commit comments