2021-09-15 17:59:51 +02:00
|
|
|
import React from 'react';
|
|
|
|
import { useSelector } from 'react-redux';
|
2023-05-24 13:40:58 +02:00
|
|
|
import { fireEvent, screen } from '@testing-library/react';
|
|
|
|
import configureStore from 'redux-mock-store';
|
|
|
|
import mockState from '../../../../test/data/mock-state.json';
|
2021-09-15 17:59:51 +02:00
|
|
|
import transactionGroup from '../../../../test/data/mock-pending-transaction-data.json';
|
|
|
|
import {
|
|
|
|
getConversionRate,
|
|
|
|
getSelectedAccount,
|
|
|
|
getTokenExchangeRates,
|
|
|
|
getPreferences,
|
|
|
|
getShouldShowFiat,
|
2023-07-17 19:48:15 +02:00
|
|
|
getCurrentNetwork,
|
2021-09-15 17:59:51 +02:00
|
|
|
} from '../../../selectors';
|
|
|
|
import {
|
|
|
|
renderWithProvider,
|
|
|
|
setBackgroundConnection,
|
|
|
|
} from '../../../../test/jest';
|
|
|
|
|
|
|
|
import { useGasFeeEstimates } from '../../../hooks/useGasFeeEstimates';
|
2023-01-27 19:28:03 +01:00
|
|
|
import { GasEstimateTypes } from '../../../../shared/constants/gas';
|
2023-02-24 20:21:55 +01:00
|
|
|
import { getTokens } from '../../../ducks/metamask/metamask';
|
2023-07-25 15:06:11 +02:00
|
|
|
import {
|
|
|
|
MetaMetricsEventCategory,
|
|
|
|
MetaMetricsEventName,
|
|
|
|
} from '../../../../shared/constants/metametrics';
|
|
|
|
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
|
|
|
|
2021-09-16 01:04:14 +02:00
|
|
|
import TransactionListItem from '.';
|
2021-09-15 17:59:51 +02:00
|
|
|
|
|
|
|
const FEE_MARKET_ESTIMATE_RETURN_VALUE = {
|
2023-01-27 19:28:03 +01:00
|
|
|
gasEstimateType: GasEstimateTypes.feeMarket,
|
2021-09-15 17:59:51 +02:00
|
|
|
gasFeeEstimates: {
|
|
|
|
low: {
|
|
|
|
minWaitTimeEstimate: 180000,
|
|
|
|
maxWaitTimeEstimate: 300000,
|
|
|
|
suggestedMaxPriorityFeePerGas: '3',
|
|
|
|
suggestedMaxFeePerGas: '53',
|
|
|
|
},
|
|
|
|
medium: {
|
|
|
|
minWaitTimeEstimate: 15000,
|
|
|
|
maxWaitTimeEstimate: 60000,
|
|
|
|
suggestedMaxPriorityFeePerGas: '7',
|
|
|
|
suggestedMaxFeePerGas: '70',
|
|
|
|
},
|
|
|
|
high: {
|
|
|
|
minWaitTimeEstimate: 0,
|
|
|
|
maxWaitTimeEstimate: 15000,
|
|
|
|
suggestedMaxPriorityFeePerGas: '10',
|
|
|
|
suggestedMaxFeePerGas: '100',
|
|
|
|
},
|
|
|
|
estimatedBaseFee: '50',
|
|
|
|
},
|
|
|
|
estimatedGasFeeTimeBounds: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
jest.mock('react-redux', () => {
|
|
|
|
const actual = jest.requireActual('react-redux');
|
|
|
|
|
|
|
|
return {
|
|
|
|
...actual,
|
|
|
|
useSelector: jest.fn(),
|
2023-07-17 19:48:15 +02:00
|
|
|
useDispatch: jest.fn(),
|
2021-09-15 17:59:51 +02:00
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
jest.mock('../../../hooks/useGasFeeEstimates', () => ({
|
|
|
|
useGasFeeEstimates: jest.fn(),
|
|
|
|
}));
|
|
|
|
|
|
|
|
setBackgroundConnection({
|
|
|
|
getGasFeeTimeEstimate: jest.fn(),
|
|
|
|
getGasFeeEstimatesAndStartPolling: jest.fn(),
|
|
|
|
});
|
|
|
|
|
2021-09-15 21:00:56 +02:00
|
|
|
jest.mock('react', () => {
|
|
|
|
const originReact = jest.requireActual('react');
|
|
|
|
return {
|
|
|
|
...originReact,
|
|
|
|
useLayoutEffect: jest.fn(),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2023-05-24 13:40:58 +02:00
|
|
|
jest.mock('../../../store/actions.ts', () => ({
|
|
|
|
tryReverseResolveAddress: jest.fn().mockReturnValue({ type: 'TYPE' }),
|
|
|
|
}));
|
|
|
|
|
|
|
|
jest.mock('../../../store/institutional/institution-background', () => ({
|
|
|
|
mmiActionsFactory: () => ({
|
|
|
|
getCustodianTransactionDeepLink: jest
|
|
|
|
.fn()
|
|
|
|
.mockReturnValue({ type: 'TYPE' }),
|
|
|
|
}),
|
|
|
|
}));
|
|
|
|
|
|
|
|
const mockStore = configureStore();
|
|
|
|
|
2021-09-15 17:59:51 +02:00
|
|
|
const generateUseSelectorRouter = (opts) => (selector) => {
|
|
|
|
if (selector === getConversionRate) {
|
|
|
|
return 1;
|
|
|
|
} else if (selector === getSelectedAccount) {
|
|
|
|
return {
|
|
|
|
balance: opts.balance ?? '2AA1EFB94E0000',
|
|
|
|
};
|
|
|
|
} else if (selector === getTokenExchangeRates) {
|
|
|
|
return opts.tokenExchangeRates ?? {};
|
2023-07-17 19:48:15 +02:00
|
|
|
} else if (selector === getCurrentNetwork) {
|
|
|
|
return { nickname: 'Ethereum Mainnet' };
|
2021-09-15 17:59:51 +02:00
|
|
|
} else if (selector === getPreferences) {
|
|
|
|
return (
|
|
|
|
opts.preferences ?? {
|
|
|
|
useNativeCurrencyAsPrimaryCurrency: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} else if (selector === getShouldShowFiat) {
|
|
|
|
return opts.shouldShowFiat ?? false;
|
2023-02-24 20:21:55 +01:00
|
|
|
} else if (selector === getTokens) {
|
|
|
|
return opts.tokens ?? [];
|
2021-09-15 17:59:51 +02:00
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
describe('TransactionListItem', () => {
|
2023-07-25 15:06:11 +02:00
|
|
|
describe('ActivityListItem interactions', () => {
|
|
|
|
beforeAll(() => {
|
|
|
|
useGasFeeEstimates.mockImplementation(
|
|
|
|
() => FEE_MARKET_ESTIMATE_RETURN_VALUE,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(() => {
|
|
|
|
useGasFeeEstimates.mockRestore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should show the activity details popover and log metrics when the activity list item is clicked', () => {
|
|
|
|
useSelector.mockImplementation(
|
|
|
|
generateUseSelectorRouter({
|
|
|
|
balance: '0x3',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
const store = mockStore(mockState);
|
|
|
|
const mockTrackEvent = jest.fn();
|
|
|
|
const { queryByTestId } = renderWithProvider(
|
|
|
|
<MetaMetricsContext.Provider value={mockTrackEvent}>
|
|
|
|
<TransactionListItem transactionGroup={transactionGroup} />
|
|
|
|
</MetaMetricsContext.Provider>,
|
|
|
|
store,
|
|
|
|
);
|
|
|
|
const activityListItem = queryByTestId('activity-list-item');
|
|
|
|
fireEvent.click(activityListItem);
|
|
|
|
expect(mockTrackEvent).toHaveBeenCalledWith({
|
|
|
|
event: MetaMetricsEventName.ActivityDetailsOpened,
|
|
|
|
category: MetaMetricsEventCategory.Navigation,
|
|
|
|
properties: {
|
|
|
|
activity_type: 'send',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const popoverClose = queryByTestId('popover-close');
|
|
|
|
fireEvent.click(popoverClose);
|
|
|
|
expect(mockTrackEvent).toHaveBeenCalledWith({
|
|
|
|
event: MetaMetricsEventName.ActivityDetailsClosed,
|
|
|
|
category: MetaMetricsEventCategory.Navigation,
|
|
|
|
properties: {
|
|
|
|
activity_type: 'send',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-09-16 01:04:14 +02:00
|
|
|
describe('when account has insufficient balance to cover gas', () => {
|
|
|
|
beforeAll(() => {
|
2021-09-15 17:59:51 +02:00
|
|
|
useGasFeeEstimates.mockImplementation(
|
|
|
|
() => FEE_MARKET_ESTIMATE_RETURN_VALUE,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2021-09-16 01:04:14 +02:00
|
|
|
afterAll(() => {
|
2022-08-23 22:13:02 +02:00
|
|
|
useGasFeeEstimates.mockRestore();
|
2021-09-15 17:59:51 +02:00
|
|
|
});
|
|
|
|
|
2021-09-16 01:04:14 +02:00
|
|
|
it(`should indicate account has insufficient funds to cover gas price for cancellation of pending transaction`, () => {
|
2021-09-15 17:59:51 +02:00
|
|
|
useSelector.mockImplementation(
|
|
|
|
generateUseSelectorRouter({
|
|
|
|
balance: '0x3',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
const { queryByTestId } = renderWithProvider(
|
|
|
|
<TransactionListItem transactionGroup={transactionGroup} />,
|
|
|
|
);
|
|
|
|
expect(queryByTestId('not-enough-gas__tooltip')).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
2021-09-16 01:04:14 +02:00
|
|
|
it('should not disable "cancel" button when user has sufficient funds', () => {
|
2021-09-15 17:59:51 +02:00
|
|
|
useSelector.mockImplementation(
|
|
|
|
generateUseSelectorRouter({
|
|
|
|
balance: '2AA1EFB94E0000',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
const { queryByTestId } = renderWithProvider(
|
|
|
|
<TransactionListItem transactionGroup={transactionGroup} />,
|
|
|
|
);
|
|
|
|
expect(queryByTestId('not-enough-gas__tooltip')).not.toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
2021-09-16 01:04:14 +02:00
|
|
|
it(`should open the edit gas popover when cancel is clicked`, () => {
|
2021-09-15 17:59:51 +02:00
|
|
|
useSelector.mockImplementation(
|
|
|
|
generateUseSelectorRouter({
|
|
|
|
balance: '2AA1EFB94E0000',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
const { getByText, queryByText } = renderWithProvider(
|
|
|
|
<TransactionListItem transactionGroup={transactionGroup} />,
|
|
|
|
);
|
|
|
|
expect(queryByText('Cancel transaction')).not.toBeInTheDocument();
|
|
|
|
|
|
|
|
const cancelButton = getByText('Cancel');
|
|
|
|
fireEvent.click(cancelButton);
|
|
|
|
expect(getByText('Cancel transaction')).toBeInTheDocument();
|
|
|
|
});
|
2023-05-24 13:40:58 +02:00
|
|
|
|
|
|
|
it('should have a custodian Tx and show the custody icon', () => {
|
|
|
|
useSelector.mockImplementation(
|
|
|
|
generateUseSelectorRouter({
|
|
|
|
balance: '2AA1EFB94E0000',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
const newTransactionGroup = {
|
|
|
|
...transactionGroup,
|
|
|
|
...(transactionGroup.primaryTransaction.custodyId = '1'),
|
|
|
|
};
|
|
|
|
|
|
|
|
const { queryByTestId } = renderWithProvider(
|
|
|
|
<TransactionListItem transactionGroup={newTransactionGroup} />,
|
|
|
|
);
|
|
|
|
expect(queryByTestId('custody-icon')).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should click the custody list item and view the send screen', () => {
|
|
|
|
const store = mockStore(mockState);
|
|
|
|
|
|
|
|
useSelector.mockImplementation(
|
|
|
|
generateUseSelectorRouter({
|
|
|
|
balance: '2AA1EFB94E0000',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
const newTransactionGroup = {
|
|
|
|
...transactionGroup,
|
|
|
|
...(transactionGroup.primaryTransaction.custodyId = '1'),
|
|
|
|
};
|
|
|
|
|
|
|
|
const { queryByTestId } = renderWithProvider(
|
|
|
|
<TransactionListItem transactionGroup={newTransactionGroup} />,
|
|
|
|
store,
|
|
|
|
);
|
|
|
|
|
|
|
|
const custodyListItem = queryByTestId('custody-icon');
|
|
|
|
fireEvent.click(custodyListItem);
|
|
|
|
|
|
|
|
const sendTextExists = screen.queryAllByText('Send');
|
|
|
|
expect(sendTextExists).toBeTruthy();
|
|
|
|
});
|
2021-09-15 17:59:51 +02:00
|
|
|
});
|
|
|
|
});
|