mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-28 23:06:37 +01:00
512 lines
16 KiB
JavaScript
512 lines
16 KiB
JavaScript
import React from 'react';
|
|
import configureMockStore from 'redux-mock-store';
|
|
import thunk from 'redux-thunk';
|
|
import { fireEvent } from '@testing-library/react';
|
|
|
|
import { NetworkType } from '@metamask/controller-utils';
|
|
import { NetworkStatus } from '@metamask/network-controller';
|
|
import { renderWithProvider } from '../../../test/lib/render-helpers';
|
|
import { setBackgroundConnection } from '../../../test/jest';
|
|
import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../test/jest/mocks';
|
|
import { GasEstimateTypes } from '../../../shared/constants/gas';
|
|
import { KeyringType } from '../../../shared/constants/keyring';
|
|
import { CHAIN_IDS } from '../../../shared/constants/network';
|
|
import {
|
|
TransactionStatus,
|
|
TransactionType,
|
|
} from '../../../shared/constants/transaction';
|
|
import { domainInitialState } from '../../ducks/domains';
|
|
|
|
import ConfirmTransactionBase from './confirm-transaction-base.container';
|
|
|
|
const middleware = [thunk];
|
|
|
|
setBackgroundConnection({
|
|
getGasFeeTimeEstimate: jest.fn(),
|
|
getGasFeeEstimatesAndStartPolling: jest.fn(),
|
|
promisifiedBackground: jest.fn(),
|
|
tryReverseResolveAddress: jest.fn(),
|
|
getNextNonce: jest.fn(),
|
|
});
|
|
|
|
const mockNetworkId = '5';
|
|
|
|
const mockTxParamsFromAddress = '0x123456789';
|
|
|
|
const mockTxParamsToAddress = '0x85c1685cfceaa5c0bdb1609fc536e9a8387dd65e';
|
|
const mockTxParamsToAddressConcat = '0x85c...D65e';
|
|
|
|
const mockParsedTxDataToAddressWithout0x =
|
|
'e57e7847fd3661a9b7c86aaf1daea08d9da5750a';
|
|
const mockParsedTxDataToAddress = '0xe57...750A';
|
|
|
|
const mockPropsToAddress = '0x33m1685cfceaa5c0bdb1609fc536e9a8387dd567';
|
|
const mockPropsToAddressConcat = '0x33m...d567';
|
|
|
|
const mockTxParams = {
|
|
from: mockTxParamsFromAddress,
|
|
to: mockTxParamsToAddress,
|
|
value: '0x5af3107a4000',
|
|
gas: '0x5208',
|
|
maxFeePerGas: '0x59682f16',
|
|
maxPriorityFeePerGas: '0x59682f00',
|
|
type: '0x2',
|
|
data: `0xa22cb465000000000000000000000000${mockParsedTxDataToAddressWithout0x}0000000000000000000000000000000000000000000000000000000000000001`,
|
|
};
|
|
|
|
const baseStore = {
|
|
send: {
|
|
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
|
currentTransactionUUID: null,
|
|
draftTransactions: {},
|
|
},
|
|
DNS: domainInitialState,
|
|
gas: {
|
|
customData: { limit: null, price: null },
|
|
},
|
|
history: { mostRecentOverviewPage: '/' },
|
|
metamask: {
|
|
unapprovedTxs: {
|
|
1: {
|
|
id: 1,
|
|
metamaskNetworkId: mockNetworkId,
|
|
txParams: { ...mockTxParams },
|
|
},
|
|
},
|
|
gasEstimateType: GasEstimateTypes.legacy,
|
|
gasFeeEstimates: {
|
|
low: '0',
|
|
medium: '1',
|
|
fast: '2',
|
|
},
|
|
selectedAddress: mockTxParamsFromAddress,
|
|
keyrings: [
|
|
{
|
|
type: KeyringType.hdKeyTree,
|
|
accounts: ['0x0'],
|
|
},
|
|
],
|
|
networkId: mockNetworkId,
|
|
selectedNetworkClientId: NetworkType.mainnet,
|
|
networksMetadata: {
|
|
[NetworkType.mainnet]: {
|
|
EIPS: {},
|
|
status: NetworkStatus.Available,
|
|
},
|
|
},
|
|
tokens: [],
|
|
preferences: {
|
|
useNativeCurrencyAsPrimaryCurrency: false,
|
|
},
|
|
currentCurrency: 'USD',
|
|
providerConfig: {
|
|
chainId: CHAIN_IDS.GOERLI,
|
|
},
|
|
nativeCurrency: 'ETH',
|
|
featureFlags: {
|
|
sendHexData: false,
|
|
},
|
|
addressBook: {
|
|
[CHAIN_IDS.GOERLI]: [],
|
|
},
|
|
cachedBalances: {
|
|
[CHAIN_IDS.GOERLI]: {},
|
|
},
|
|
accounts: {
|
|
[mockTxParamsFromAddress]: {
|
|
balance: '0x0',
|
|
address: mockTxParamsFromAddress,
|
|
},
|
|
},
|
|
identities: {
|
|
[mockTxParamsFromAddress]: { address: mockTxParamsFromAddress },
|
|
[mockTxParamsToAddress]: {
|
|
name: 'Test Address 1',
|
|
},
|
|
},
|
|
tokenAddress: '0x32e6c34cd57087abbd59b5a4aecc4cb495924356',
|
|
tokenList: {},
|
|
ensResolutionsByAddress: {},
|
|
snaps: {},
|
|
},
|
|
confirmTransaction: {
|
|
txData: {
|
|
id: 1,
|
|
metamaskNetworkId: mockNetworkId,
|
|
txParams: { ...mockTxParams },
|
|
time: 1675012496170,
|
|
status: TransactionStatus.unapproved,
|
|
originalGasEstimate: '0x5208',
|
|
userEditedGasLimit: false,
|
|
chainId: '0x5',
|
|
loadingDefaults: false,
|
|
dappSuggestedGasFees: null,
|
|
sendFlowHistory: [],
|
|
origin: 'metamask',
|
|
actionId: 1675012496153.2039,
|
|
type: 'simpleSend',
|
|
history: [],
|
|
userFeeLevel: 'medium',
|
|
defaultGasEstimates: {
|
|
estimateType: 'medium',
|
|
gas: '0x5208',
|
|
maxFeePerGas: '0x59682f16',
|
|
maxPriorityFeePerGas: '0x59682f00',
|
|
},
|
|
},
|
|
tokenData: {},
|
|
tokenProps: {},
|
|
fiatTransactionAmount: '0.16',
|
|
fiatTransactionFee: '0',
|
|
fiatTransactionTotal: '0.16',
|
|
ethTransactionAmount: '0.0001',
|
|
ethTransactionFee: '0',
|
|
ethTransactionTotal: '0.0001',
|
|
hexTransactionAmount: '0x5af3107a4000',
|
|
hexTransactionFee: '0x0',
|
|
hexTransactionTotal: '0x5af3107a4000',
|
|
nonce: '',
|
|
},
|
|
appState: {
|
|
sendInputCurrencySwitched: false,
|
|
},
|
|
};
|
|
|
|
const mockedStore = jest.mocked(baseStore);
|
|
|
|
const mockedStoreWithConfirmTxParams = (_mockTxParams = mockTxParams) => {
|
|
mockedStore.metamask.unapprovedTxs[1].txParams = { ..._mockTxParams };
|
|
mockedStore.confirmTransaction.txData.txParams = { ..._mockTxParams };
|
|
};
|
|
|
|
const sendToRecipientSelector =
|
|
'.sender-to-recipient__party--recipient .sender-to-recipient__name';
|
|
|
|
describe('Confirm Transaction Base', () => {
|
|
it('should match snapshot', () => {
|
|
const store = configureMockStore(middleware)(baseStore);
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
expect(container).toMatchSnapshot();
|
|
});
|
|
|
|
it('should not contain L1 L2 fee details for chains that are not optimism', () => {
|
|
const store = configureMockStore(middleware)(baseStore);
|
|
const { queryByText } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
expect(queryByText('Layer 1 fees')).not.toBeInTheDocument();
|
|
expect(queryByText('Layer 2 gas fee')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('should contain L1 L2 fee details for optimism', () => {
|
|
mockedStore.metamask.providerConfig.chainId = CHAIN_IDS.OPTIMISM;
|
|
mockedStore.confirmTransaction.txData.chainId = CHAIN_IDS.OPTIMISM;
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { queryByText } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
expect(queryByText('Layer 1 fees')).toBeInTheDocument();
|
|
expect(queryByText('Layer 2 gas fee')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render NoteToTrader when isNoteToTraderSupported is true', () => {
|
|
mockedStore.metamask.custodyAccountDetails = {
|
|
[mockTxParamsFromAddress]: {
|
|
address: mockTxParamsFromAddress,
|
|
details: 'details',
|
|
custodyType: 'testCustody - Saturn',
|
|
custodianName: 'saturn-dev',
|
|
},
|
|
};
|
|
|
|
mockedStore.metamask.mmiConfiguration = {
|
|
custodians: [
|
|
{
|
|
name: 'saturn-dev',
|
|
displayName: 'Saturn Custody',
|
|
isNoteToTraderSupported: true,
|
|
},
|
|
],
|
|
};
|
|
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { getByTestId } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
|
|
expect(getByTestId('note-tab')).toBeInTheDocument();
|
|
});
|
|
|
|
it('handleMainSubmit calls sendTransaction correctly', async () => {
|
|
const newMockedStore = {
|
|
...mockedStore,
|
|
appState: {
|
|
...mockedStore.appState,
|
|
gasLoadingAnimationIsShowing: false,
|
|
},
|
|
metamask: {
|
|
...mockedStore.metamask,
|
|
accounts: {
|
|
[mockTxParamsFromAddress]: {
|
|
balance: '0x1000000000000000000',
|
|
address: mockTxParamsFromAddress,
|
|
},
|
|
},
|
|
gasEstimateType: GasEstimateTypes.feeMarket,
|
|
selectedNetworkClientId: NetworkType.mainnet,
|
|
networksMetadata: {
|
|
...mockedStore.metamask.networksMetadata,
|
|
[NetworkType.mainnet]: {
|
|
EIPS: { 1559: true },
|
|
status: NetworkStatus.Available,
|
|
},
|
|
},
|
|
customGas: {
|
|
gasLimit: '0x5208',
|
|
gasPrice: '0x59682f00',
|
|
},
|
|
noGasPrice: false,
|
|
},
|
|
send: {
|
|
...mockedStore.send,
|
|
gas: {
|
|
...mockedStore.send.gas,
|
|
gasEstimateType: GasEstimateTypes.legacy,
|
|
gasFeeEstimates: {
|
|
low: '0',
|
|
medium: '1',
|
|
high: '2',
|
|
},
|
|
},
|
|
hasSimulationError: false,
|
|
userAcknowledgedGasMissing: false,
|
|
submitting: false,
|
|
hardwareWalletRequiresConnection: false,
|
|
gasIsLoading: false,
|
|
gasFeeIsCustom: true,
|
|
},
|
|
};
|
|
const store = configureMockStore(middleware)(newMockedStore);
|
|
const sendTransaction = jest.fn().mockResolvedValue();
|
|
|
|
const { getByTestId } = renderWithProvider(
|
|
<ConfirmTransactionBase
|
|
actionKey="confirm"
|
|
sendTransaction={sendTransaction}
|
|
toAddress={mockPropsToAddress}
|
|
toAccounts={[{ address: mockPropsToAddress }]}
|
|
/>,
|
|
store,
|
|
);
|
|
const confirmButton = getByTestId('page-container-footer-next');
|
|
fireEvent.click(confirmButton);
|
|
expect(sendTransaction).toHaveBeenCalled();
|
|
});
|
|
|
|
it('handleMMISubmit calls sendTransaction correctly and then showCustodianDeepLink', async () => {
|
|
const newMockedStore = {
|
|
...mockedStore,
|
|
appState: {
|
|
...mockedStore.appState,
|
|
gasLoadingAnimationIsShowing: false,
|
|
},
|
|
confirmTransaction: {
|
|
...mockedStore.confirmTransaction,
|
|
txData: {
|
|
...mockedStore.confirmTransaction.txData,
|
|
custodyStatus: true,
|
|
},
|
|
},
|
|
metamask: {
|
|
...mockedStore.metamask,
|
|
accounts: {
|
|
[mockTxParamsFromAddress]: {
|
|
balance: '0x1000000000000000000',
|
|
address: mockTxParamsFromAddress,
|
|
},
|
|
},
|
|
gasEstimateType: GasEstimateTypes.feeMarket,
|
|
selectedNetworkClientId: NetworkType.mainnet,
|
|
networksMetadata: {
|
|
...mockedStore.metamask.networksMetadata,
|
|
[NetworkType.mainnet]: {
|
|
EIPS: {
|
|
1559: true,
|
|
},
|
|
status: NetworkStatus.Available,
|
|
},
|
|
},
|
|
customGas: {
|
|
gasLimit: '0x5208',
|
|
gasPrice: '0x59682f00',
|
|
},
|
|
noGasPrice: false,
|
|
},
|
|
send: {
|
|
...mockedStore.send,
|
|
gas: {
|
|
...mockedStore.send.gas,
|
|
gasEstimateType: GasEstimateTypes.legacy,
|
|
gasFeeEstimates: {
|
|
low: '0',
|
|
medium: '1',
|
|
high: '2',
|
|
},
|
|
},
|
|
hasSimulationError: false,
|
|
userAcknowledgedGasMissing: false,
|
|
submitting: false,
|
|
hardwareWalletRequiresConnection: false,
|
|
gasIsLoading: false,
|
|
gasFeeIsCustom: true,
|
|
},
|
|
};
|
|
const store = configureMockStore(middleware)(newMockedStore);
|
|
const sendTransaction = jest
|
|
.fn()
|
|
.mockResolvedValue(newMockedStore.confirmTransaction.txData);
|
|
const showCustodianDeepLink = jest.fn();
|
|
const setWaitForConfirmDeepLinkDialog = jest.fn();
|
|
|
|
const { getByTestId } = renderWithProvider(
|
|
<ConfirmTransactionBase
|
|
actionKey="confirm"
|
|
sendTransaction={sendTransaction}
|
|
showCustodianDeepLink={showCustodianDeepLink}
|
|
setWaitForConfirmDeepLinkDialog={setWaitForConfirmDeepLinkDialog}
|
|
toAddress={mockPropsToAddress}
|
|
toAccounts={[{ address: mockPropsToAddress }]}
|
|
isMainBetaFlask={false}
|
|
/>,
|
|
store,
|
|
);
|
|
const confirmButton = getByTestId('page-container-footer-next');
|
|
fireEvent.click(confirmButton);
|
|
expect(setWaitForConfirmDeepLinkDialog).toHaveBeenCalled();
|
|
await expect(sendTransaction).toHaveBeenCalled();
|
|
expect(showCustodianDeepLink).toHaveBeenCalled();
|
|
});
|
|
|
|
describe('when rendering the recipient value', () => {
|
|
describe(`when the transaction is a ${TransactionType.simpleSend} type`, () => {
|
|
it(`should use txParams.to address`, () => {
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
|
|
const recipientElem = container.querySelector(sendToRecipientSelector);
|
|
expect(recipientElem).toHaveTextContent(mockTxParamsToAddressConcat);
|
|
});
|
|
|
|
it(`should use txParams.to address even if there is no amount sent`, () => {
|
|
mockedStoreWithConfirmTxParams({
|
|
...mockTxParams,
|
|
value: '0x0',
|
|
});
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
|
|
const recipientElem = container.querySelector(sendToRecipientSelector);
|
|
expect(recipientElem).toHaveTextContent(mockTxParamsToAddressConcat);
|
|
});
|
|
});
|
|
describe(`when the transaction is NOT a ${TransactionType.simpleSend} type`, () => {
|
|
beforeEach(() => {
|
|
mockedStore.confirmTransaction.txData.type =
|
|
TransactionType.contractInteraction;
|
|
});
|
|
|
|
describe('when there is an amount being sent (it should be treated as a general contract intereaction rather than custom one)', () => {
|
|
it('should use txParams.to address (contract address)', () => {
|
|
mockedStoreWithConfirmTxParams({
|
|
...mockTxParams,
|
|
value: '0x45666',
|
|
});
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
|
|
const recipientElem = container.querySelector(
|
|
sendToRecipientSelector,
|
|
);
|
|
expect(recipientElem).toHaveTextContent(mockTxParamsToAddressConcat);
|
|
});
|
|
});
|
|
|
|
describe(`when there is no amount being sent`, () => {
|
|
it('should use propToAddress (toAddress passed as prop)', () => {
|
|
mockedStoreWithConfirmTxParams({
|
|
...mockTxParams,
|
|
value: '0x0',
|
|
});
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase
|
|
// we want to test toAddress provided by ownProps in mapStateToProps, but this
|
|
// currently overrides toAddress this should pan out fine when we refactor the
|
|
// component into a functional component and remove the container.js file
|
|
toAddress={mockPropsToAddress}
|
|
actionKey="confirm"
|
|
/>,
|
|
store,
|
|
);
|
|
|
|
const recipientElem = container.querySelector(
|
|
sendToRecipientSelector,
|
|
);
|
|
expect(recipientElem).toHaveTextContent(mockPropsToAddressConcat);
|
|
});
|
|
|
|
it('should use address parsed from transaction data if propToAddress is not provided', () => {
|
|
mockedStoreWithConfirmTxParams({
|
|
...mockTxParams,
|
|
value: '0x0',
|
|
});
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
|
|
const recipientElem = container.querySelector(
|
|
sendToRecipientSelector,
|
|
);
|
|
expect(recipientElem).toHaveTextContent(mockParsedTxDataToAddress);
|
|
});
|
|
|
|
it('should use txParams.to if neither propToAddress is not provided nor the transaction data to address were provided', () => {
|
|
mockedStoreWithConfirmTxParams({
|
|
...mockTxParams,
|
|
data: '0x',
|
|
value: '0x0',
|
|
});
|
|
const store = configureMockStore(middleware)(mockedStore);
|
|
const { container } = renderWithProvider(
|
|
<ConfirmTransactionBase actionKey="confirm" />,
|
|
store,
|
|
);
|
|
|
|
const recipientElem = container.querySelector(
|
|
sendToRecipientSelector,
|
|
);
|
|
expect(recipientElem).toHaveTextContent(mockTxParamsToAddressConcat);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|