import { fireEvent, render, waitFor } from '@testing-library/react-native'
import BigNumber from 'bignumber.js'
import React from 'react'
import { Provider } from 'react-redux'
import AppAnalytics from 'src/analytics/AppAnalytics'
import { SendEvents } from 'src/analytics/Events'
import { SendOrigin } from 'src/analytics/types'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { RecipientType } from 'src/recipients/recipient'
import SendEnterAmount from 'src/send/SendEnterAmount'
import { usePrepareSendTransactions } from 'src/send/usePrepareSendTransactions'
import { getMultichainFeatures } from 'src/statsig'
import { NetworkId } from 'src/transactions/types'
import { PreparedTransactionsPossible } from 'src/viem/prepareTransactions'
import MockedNavigator from 'test/MockedNavigator'
import { createMockStore, mockStoreBalancesToTokenBalances } from 'test/utils'
import {
mockAccount,
mockCeloAddress,
mockCeloTokenBalance,
mockCeloTokenId,
mockCeurTokenId,
mockCusdTokenId,
mockPoofTokenId,
mockTokenBalances,
mockUSDCTokenId,
} from 'test/values'
jest.mock('src/statsig')
jest.mock('src/send/usePrepareSendTransactions')
const mockPrepareTransactionsResultPossible: PreparedTransactionsPossible = {
type: 'possible',
transactions: [
{
from: '0xfrom',
to: '0xto',
data: '0xdata',
gas: BigInt(5e15), // 0.005 CELO
maxFeePerGas: BigInt(1),
maxPriorityFeePerGas: undefined,
_baseFeePerGas: BigInt(1),
},
{
from: '0xfrom',
to: '0xto',
data: '0xdata',
gas: BigInt(1e15), // 0.001 CELO
maxFeePerGas: BigInt(1),
maxPriorityFeePerGas: undefined,
_baseFeePerGas: BigInt(1),
},
],
feeCurrency: mockCeloTokenBalance,
}
const tokenBalances = {
[mockCeloTokenId]: { ...mockTokenBalances[mockCeloTokenId], balance: '10' },
[mockCusdTokenId]: { ...mockTokenBalances[mockCusdTokenId], balance: '10' },
[mockUSDCTokenId]: mockTokenBalances[mockUSDCTokenId], // filtered out for networkId
[mockPoofTokenId]: { ...mockTokenBalances[mockPoofTokenId], balance: '0' }, // filtered out for no balance
[mockCeurTokenId]: { ...mockTokenBalances[mockCeurTokenId], balance: '100' },
}
const feeCurrencies = [
tokenBalances[mockCeloTokenId],
tokenBalances[mockCeurTokenId],
tokenBalances[mockCusdTokenId],
]
const store = createMockStore({
tokens: {
tokenBalances,
},
})
const refreshPreparedTransactionsSpy = jest.fn()
jest.mocked(usePrepareSendTransactions).mockReturnValue({
prepareTransactionsResult: undefined,
prepareTransactionLoading: false,
refreshPreparedTransactions: refreshPreparedTransactionsSpy,
clearPreparedTransactions: jest.fn(),
prepareTransactionError: undefined,
})
const params = {
origin: SendOrigin.AppSendFlow,
recipient: {
recipientType: RecipientType.Address,
address: '0x123',
},
isFromScan: false,
}
describe('SendEnterAmount', () => {
beforeEach(() => {
jest.clearAllMocks()
jest.mocked(getMultichainFeatures).mockReturnValue({
showSend: [NetworkId['celo-alfajores']],
})
})
it('should render only the allowed send tokens', () => {
const { getAllByTestId } = render(
)
const tokens = getAllByTestId('TokenBalanceItem')
expect(tokens).toHaveLength(3)
expect(tokens[0]).toHaveTextContent('CELO')
expect(tokens[1]).toHaveTextContent('cEUR')
expect(tokens[2]).toHaveTextContent('cUSD')
})
it('should prepare transactions with the expected inputs', async () => {
const defaultToken = mockStoreBalancesToTokenBalances([tokenBalances[mockCeloTokenId]])[0]
const { getByTestId } = render(
)
fireEvent.changeText(getByTestId('SendEnterAmount/TokenAmountInput'), '.25')
await waitFor(() => expect(refreshPreparedTransactionsSpy).toHaveBeenCalledTimes(1))
expect(refreshPreparedTransactionsSpy).toHaveBeenCalledWith({
amount: new BigNumber('0.25'),
token: defaultToken,
walletAddress: mockAccount.toLowerCase(),
recipientAddress: '0x123', // matches mock screen nav params
feeCurrencies: mockStoreBalancesToTokenBalances(feeCurrencies),
})
})
it('should handle navigating to the next step', async () => {
jest.mocked(usePrepareSendTransactions).mockReturnValue({
prepareTransactionsResult: mockPrepareTransactionsResultPossible,
prepareTransactionLoading: false,
refreshPreparedTransactions: jest.fn(),
clearPreparedTransactions: jest.fn(),
prepareTransactionError: undefined,
})
const { getByTestId, getByText } = render(
)
fireEvent.changeText(getByTestId('SendEnterAmount/TokenAmountInput'), '8')
await waitFor(() => expect(getByText('review')).not.toBeDisabled())
fireEvent.press(getByText('review'))
await waitFor(() => expect(AppAnalytics.track).toHaveBeenCalledTimes(1))
expect(AppAnalytics.track).toHaveBeenCalledWith(SendEvents.send_amount_continue, {
amountInUsd: '106.01',
isScan: false,
localCurrency: 'PHP',
localCurrencyAmount: '140.99',
localCurrencyExchangeRate: '1.33',
networkId: 'celo-alfajores',
origin: 'app_send_flow',
recipientType: 'Address',
tokenId: mockCeloTokenId,
underlyingAmount: '8',
underlyingTokenAddress: mockCeloAddress,
underlyingTokenSymbol: 'CELO',
amountEnteredIn: 'token',
})
expect(navigate).toHaveBeenCalledWith(Screens.SendConfirmation, {
origin: params.origin,
isFromScan: params.isFromScan,
transactionData: {
tokenId: mockCeloTokenId,
recipient: params.recipient,
inputAmount: new BigNumber(8),
amountIsInLocalCurrency: false,
tokenAddress: mockCeloAddress,
tokenAmount: new BigNumber(8),
},
})
})
it('should set the correct default token using the last used token', () => {
const { getByTestId } = render(
)
expect(getByTestId('SendEnterAmount/TokenSelect')).toHaveTextContent('cEUR')
expect(getByTestId('SendEnterAmount/TokenSelect')).not.toBeDisabled()
})
it('should set the correct default token given a token override and last used token', () => {
const { getByTestId } = render(
)
expect(getByTestId('SendEnterAmount/TokenSelect')).toHaveTextContent('cUSD')
expect(getByTestId('SendEnterAmount/TokenSelect')).not.toBeDisabled()
})
it('should disable token selection', () => {
const { getByTestId } = render(
)
expect(getByTestId('SendEnterAmount/TokenSelect')).toHaveTextContent('cUSD')
expect(getByTestId('SendEnterAmount/TokenSelect')).toBeDisabled()
})
})