diff --git a/src/transactions/feed/TransactionFeedV2.test.tsx b/src/transactions/feed/TransactionFeedV2.test.tsx index 0392f081a1..19349b6c18 100644 --- a/src/transactions/feed/TransactionFeedV2.test.tsx +++ b/src/transactions/feed/TransactionFeedV2.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, waitFor, within } from '@testing-library/react-native' +import { act, fireEvent, render, waitFor, within } from '@testing-library/react-native' import { FetchMock } from 'jest-fetch-mock/types' import React from 'react' import Toast from 'react-native-simple-toast' @@ -10,14 +10,18 @@ import { getDynamicConfigParams, getFeatureGate, getMultichainFeatures } from 's import TransactionFeedV2 from 'src/transactions/feed/TransactionFeedV2' import { NetworkId, + StandbyTransaction, TokenTransaction, TokenTransactionTypeV2, TransactionStatus, } from 'src/transactions/types' -import { mockCusdAddress, mockCusdTokenId } from 'test/values' +import { mockCeloTokenId, mockCusdAddress, mockCusdTokenId, mockQRCodeRecipient } from 'test/values' +import BigNumber from 'bignumber.js' +import { Action } from 'redux-saga' import { ApiReducersKeys } from 'src/redux/apiReducersList' import { vibrateSuccess } from 'src/styles/hapticFeedback' +import { addStandbyTransaction, transactionConfirmed } from 'src/transactions/actions' import { transactionFeedV2Api, type TransactionFeedV2Response } from 'src/transactions/api' import { setupApiStore } from 'src/transactions/apiTestHelpers' import { RecursivePartial } from 'test/utils' @@ -29,7 +33,7 @@ jest.mock('src/styles/hapticFeedback') const STAND_BY_TRANSACTION_SUBTITLE_KEY = 'confirmingTransaction' const mockFetch = fetch as FetchMock -function mockTransaction(data?: Partial): TokenTransaction { +function mockTransaction(data?: Partial): TokenTransaction { return { __typename: 'TokenTransferV3', networkId: NetworkId['celo-alfajores'], @@ -434,65 +438,86 @@ describe('TransactionFeedV2', () => { await waitFor(() => expect(tree.queryByTestId('TransactionList/loading')).toBeFalsy()) await waitFor(() => expect(Toast.showWithGravity).toBeCalledTimes(0)) }) -}) - -it('should vibrate when there are new completed transactions', async () => { - mockFetch.mockResponse(typedResponse({ transactions: [mockTransaction()] })) - const { store } = renderScreen({ transactions: { standbyTransactions: [mockTransaction()] } }) - await waitFor(() => expect(store.getState().transactions.standbyTransactions.length).toBe(0)) - expect(vibrateSuccess).toHaveBeenCalledTimes(1) -}) - -it('should vibrate when there are pending transactions that turned into completed', async () => { - const standByTransactionHash = '0x02' as string - mockFetch - .mockResponseOnce( - typedResponse({ transactions: [mockTransaction({ transactionHash: '0x01' })] }) - ) - .mockResponseOnce( + it('should vibrate when there is a pending transaction that turned into completed', async () => { + const standByTransactionHash = '0x02' as string + mockFetch.mockResponseOnce(typedResponse({ transactions: [] })).mockResponseOnce( typedResponse({ - transactions: [ - mockTransaction({ transactionHash: '0x01' }), - mockTransaction({ transactionHash: standByTransactionHash }), - ], + transactions: [mockTransaction({ transactionHash: standByTransactionHash })], }) ) - const { store, rerender, ...tree } = renderScreen({ - transactions: { - standbyTransactions: [ - mockTransaction({ - transactionHash: standByTransactionHash, - status: TransactionStatus.Pending, - }), - ], - }, - }) + const { store, ...tree } = renderScreen({ + transactions: { + standbyTransactions: [ + mockTransaction({ + context: { id: standByTransactionHash }, + transactionHash: standByTransactionHash, + status: TransactionStatus.Pending, + }), + ], + }, + }) - await waitFor(() => expect(tree.getByTestId('TransactionList').props.data.length).toBe(2)) - expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(1) - expect(tree.getByTestId('TransactionList').props.data[1].data.length).toBe(1) - const updatedStore = getInitialStore({ - transactions: { - standbyTransactions: [ - mockTransaction({ - transactionHash: standByTransactionHash, - status: TransactionStatus.Complete, - }), - ], - }, + expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(1) + + // imitate changing of pending stand by transaction to confirmed + await act(() => { + const changePendingToConfirmed = transactionConfirmed( + standByTransactionHash, + { status: TransactionStatus.Complete, transactionHash: standByTransactionHash, block: '' }, + mockTransaction().timestamp + ) as Action + store.dispatch(changePendingToConfirmed) + }) + + await waitFor(() => { + expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(1) + }) + expect(vibrateSuccess).toHaveBeenCalledTimes(1) }) - rerender( - - - - ) + it('should not vibrate when there is a new pending transaction', async () => { + const pendingStandByTransactionHash1 = '0x01' as string + const pendingStandByTransactionHash2 = '0x02' as string + const { store, rerender, ...tree } = renderScreen({ + transactions: { + standbyTransactions: [ + mockTransaction({ + context: { id: pendingStandByTransactionHash1 }, + transactionHash: pendingStandByTransactionHash1, + status: TransactionStatus.Pending, + }), + ], + }, + }) - await waitFor(() => expect(tree.getByTestId('TransactionList').props.data.length).toBe(1)) - await waitFor(() => { - expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(2) + expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(1) + + await act(() => { + const newPendingTransaction = addStandbyTransaction({ + __typename: 'TokenTransferV3', + context: { id: pendingStandByTransactionHash2 }, + type: TokenTransactionTypeV2.Sent, + networkId: NetworkId['celo-alfajores'], + amount: { + value: BigNumber(10).negated().toString(), + tokenAddress: mockCusdAddress, + tokenId: mockCusdTokenId, + }, + address: mockQRCodeRecipient.address, + metadata: {}, + feeCurrencyId: mockCeloTokenId, + transactionHash: pendingStandByTransactionHash2, + }) as Action + store.dispatch(newPendingTransaction) + }) + + await waitFor(() => { + expect(tree.getByTestId('TransactionList').props.data[0].data.length).toBe(2) + }) + expect(vibrateSuccess).not.toHaveBeenCalled() }) - await waitFor(() => expect(vibrateSuccess).toBeCalled()) + + // it('should vibrate when there are new completed transactions', () => {}) }) diff --git a/src/transactions/feed/TransactionFeedV2.tsx b/src/transactions/feed/TransactionFeedV2.tsx index 026de5178f..3d87aef6f2 100644 --- a/src/transactions/feed/TransactionFeedV2.tsx +++ b/src/transactions/feed/TransactionFeedV2.tsx @@ -192,7 +192,7 @@ function useStandByTransactions() { function useNewlyCompletedTransactions( standByTransactions: ReturnType ) { - const [prevStandBy, setPrevStandBy] = useState({ + const [previousStandBy, setPreviousStandBy] = useState({ pending: [] as string[], confirmed: [] as string[], hasNewlyCompletedTransactions: false, @@ -200,7 +200,7 @@ function useNewlyCompletedTransactions( useEffect( function updatePrevStandBy() { - setPrevStandBy((prev) => { + setPreviousStandBy((prev) => { const pendingHashes = standByTransactions.pending.map((tx) => tx.transactionHash) const confirmedHashes = standByTransactions.confirmed.map((tx) => tx.transactionHash) const hasNewlyCompletedTransactions = prev.pending.some((hash) => { @@ -217,7 +217,7 @@ function useNewlyCompletedTransactions( [standByTransactions] ) - return prevStandBy.hasNewlyCompletedTransactions + return previousStandBy.hasNewlyCompletedTransactions } function renderItem({ item: tx }: { item: TokenTransaction }) { @@ -350,14 +350,15 @@ export default function TransactionFeedV2() { [data?.transactions] ) - useEffect(() => { - if (originalArgs?.endCursor === undefined) return - - const isFirstPage = originalArgs.endCursor === FIRST_PAGE_TIMESTAMP - if (isFirstPage && newlyCompletedTransactions) { - vibrateSuccess() - } - }, [newlyCompletedTransactions, originalArgs]) + useEffect( + function vibrateForNewCompletedTransactions() { + const isFirstPage = originalArgs?.endCursor === FIRST_PAGE_TIMESTAMP + if (isFirstPage && newlyCompletedTransactions) { + vibrateSuccess() + } + }, + [newlyCompletedTransactions, originalArgs] + ) const confirmedTransactions = useMemo(() => { const flattenedPages = Object.values(paginatedData).flat()