mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
[MMI] Added code fencing in transaction list (#18071)
* Added code fencing in transaction list * Fixed import * Fixed tests * Fixed indentation * Fixed code fences * Removed custody icon in favor of svg * Fix prettier * lint * Fixed prettier issue * adds check before set state with variable _mounted * lint * check for address in selectedIdentity * review fix * lint * updates test * lint * clean up * prettier * adds missing locale * Added tests and improved code * Fixed code --------- Co-authored-by: Antonio Regadas <antonio.regadas@consensys.net>
This commit is contained in:
parent
832ce634fd
commit
00bad7b8a8
3
app/_locales/en/messages.json
generated
3
app/_locales/en/messages.json
generated
@ -4933,6 +4933,9 @@
|
||||
"viewOnOpensea": {
|
||||
"message": "View on Opensea"
|
||||
},
|
||||
"viewinCustodianApp": {
|
||||
"message": "View in custodian app"
|
||||
},
|
||||
"viewinExplorer": {
|
||||
"message": "View $1 in explorer",
|
||||
"description": "$1 is the action type. e.g (Account, Transaction, Swap)"
|
||||
|
@ -11,6 +11,11 @@ import Button from '../../ui/button';
|
||||
import Tooltip from '../../ui/tooltip';
|
||||
import CancelButton from '../cancel-button';
|
||||
import Popover from '../../ui/popover';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import Box from '../../ui/box/box';
|
||||
import { Icon, IconName, Text } from '../../component-library';
|
||||
import { IconColor } from '../../../helpers/constants/design-system';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import { SECOND } from '../../../../shared/constants/time';
|
||||
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
|
||||
import { TransactionType } from '../../../../shared/constants/transaction';
|
||||
@ -52,10 +57,18 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
isCustomNetwork: PropTypes.bool,
|
||||
history: PropTypes.object,
|
||||
blockExplorerLinkText: PropTypes.object,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getCustodianTransactionDeepLink: PropTypes.func,
|
||||
selectedIdentity: PropTypes.object,
|
||||
transactionNote: PropTypes.string,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
|
||||
state = {
|
||||
justCopied: false,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
custodyTransactionDeepLink: null,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
|
||||
handleBlockExplorerClick = () => {
|
||||
@ -124,16 +137,57 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { recipientAddress, tryReverseResolveAddress } = this.props;
|
||||
const {
|
||||
recipientAddress,
|
||||
tryReverseResolveAddress,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
selectedIdentity,
|
||||
transactionGroup,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = this.props;
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
this._mounted = true;
|
||||
const address = selectedIdentity?.address;
|
||||
const custodyId = transactionGroup?.primaryTransaction?.custodyId;
|
||||
|
||||
if (this._mounted && address && custodyId) {
|
||||
this.getCustodianTransactionDeepLink(address, custodyId);
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
if (recipientAddress) {
|
||||
tryReverseResolveAddress(recipientAddress);
|
||||
}
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getCustodianTransactionDeepLink = async (address, custodyId) => {
|
||||
const { getCustodianTransactionDeepLink } = this.props;
|
||||
|
||||
const custodyTransactionDeepLink = await getCustodianTransactionDeepLink(
|
||||
address,
|
||||
custodyId,
|
||||
);
|
||||
|
||||
if (custodyTransactionDeepLink && this._mounted) {
|
||||
this.setState({ custodyTransactionDeepLink });
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
this._mounted = false;
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
render() {
|
||||
const { t } = this.context;
|
||||
const { justCopied } = this.state;
|
||||
const {
|
||||
justCopied,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
custodyTransactionDeepLink,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = this.state;
|
||||
const {
|
||||
transactionGroup,
|
||||
primaryCurrency,
|
||||
@ -152,6 +206,9 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
showCancel,
|
||||
transactionStatus: TransactionStatus,
|
||||
blockExplorerLinkText,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
transactionNote,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = this.props;
|
||||
const {
|
||||
primaryTransaction: transaction,
|
||||
@ -229,6 +286,30 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
custodyTransactionDeepLink &&
|
||||
custodyTransactionDeepLink.url && (
|
||||
<Tooltip
|
||||
wrapperClassName="transaction-list-item-details__header-button"
|
||||
containerClassName="transaction-list-item-details__header-button-tooltip-container"
|
||||
title={t('viewinCustodianApp')}
|
||||
>
|
||||
<Button
|
||||
type="raised"
|
||||
onClick={() => {
|
||||
window.open(custodyTransactionDeepLink.url);
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={IconName.Custody}
|
||||
color={IconColor.primaryDefault}
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="transaction-list-item-details__body">
|
||||
@ -281,6 +362,20 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
primaryCurrency={primaryCurrency}
|
||||
className="transaction-list-item-details__transaction-breakdown"
|
||||
/>
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
transactionNote && transactionNote.length !== 0 && (
|
||||
<Box className="transaction-list-item-details__transaction-breakdown">
|
||||
<Text as="h4" className="transaction-breakdown__title">
|
||||
{t('transactionNote')}
|
||||
</Text>
|
||||
<Text as="p" className="transaction-breakdown__description">
|
||||
{transactionNote}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
{transactionGroup.initialTransaction.type !==
|
||||
TransactionType.incoming && (
|
||||
<Disclosure title={t('activityLog')} size="small">
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { TransactionStatus } from '../../../../shared/constants/transaction';
|
||||
import { GAS_LIMITS } from '../../../../shared/constants/gas';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
@ -13,6 +14,14 @@ jest.mock('../../../store/actions.ts', () => ({
|
||||
addPollingTokenToAppState: jest.fn(),
|
||||
}));
|
||||
|
||||
let mockGetCustodianTransactionDeepLink = jest.fn();
|
||||
|
||||
jest.mock('../../../store/institutional/institution-background', () => ({
|
||||
mmiActionsFactory: () => ({
|
||||
getCustodianTransactionDeepLink: () => mockGetCustodianTransactionDeepLink,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('TransactionListItemDetails Component', () => {
|
||||
const transaction = {
|
||||
history: [],
|
||||
@ -26,6 +35,10 @@ describe('TransactionListItemDetails Component', () => {
|
||||
to: '0x2',
|
||||
value: '0x2386f26fc10000',
|
||||
},
|
||||
metadata: {
|
||||
note: 'some note',
|
||||
},
|
||||
custodyId: '1',
|
||||
};
|
||||
|
||||
const transactionGroup = {
|
||||
@ -58,7 +71,7 @@ describe('TransactionListItemDetails Component', () => {
|
||||
rpcPrefs,
|
||||
};
|
||||
|
||||
it('should render title with title prop', () => {
|
||||
it('should render title with title prop', async () => {
|
||||
const mockStore = configureMockStore([thunk])(mockState);
|
||||
|
||||
const { queryByText } = renderWithProvider(
|
||||
@ -66,7 +79,9 @@ describe('TransactionListItemDetails Component', () => {
|
||||
mockStore,
|
||||
);
|
||||
|
||||
expect(queryByText(props.title)).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(queryByText(props.title)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Retry button', () => {
|
||||
@ -122,4 +137,55 @@ describe('TransactionListItemDetails Component', () => {
|
||||
expect(queryByTestId('speedup-button')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Institutional', () => {
|
||||
it('should render correctly if custodyTransactionDeepLink has a url', async () => {
|
||||
mockGetCustodianTransactionDeepLink = jest
|
||||
.fn()
|
||||
.mockReturnValue({ url: 'https://url.com' });
|
||||
|
||||
const mockStore = configureMockStore([thunk])(mockState);
|
||||
|
||||
renderWithProvider(<TransactionListItemDetails {...props} />, mockStore);
|
||||
|
||||
await waitFor(() => {
|
||||
const custodianViewButton = document.querySelector(
|
||||
'[data-original-title="View in custodian app"]',
|
||||
);
|
||||
|
||||
// Assert that the custodian view button is rendered
|
||||
expect(custodianViewButton).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render correctly if transactionNote is provided', async () => {
|
||||
const newTransaction = {
|
||||
...transaction,
|
||||
metadata: {
|
||||
note: 'some note',
|
||||
},
|
||||
custodyId: '1',
|
||||
};
|
||||
|
||||
const newTransactionGroup = {
|
||||
...transactionGroup,
|
||||
transactions: [newTransaction],
|
||||
primaryTransaction: newTransaction,
|
||||
initialTransaction: newTransaction,
|
||||
};
|
||||
const mockStore = configureMockStore([thunk])(mockState);
|
||||
|
||||
const { queryByText } = renderWithProvider(
|
||||
<TransactionListItemDetails
|
||||
{...props}
|
||||
transactionGroup={newTransactionGroup}
|
||||
/>,
|
||||
mockStore,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(queryByText('some note')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,9 @@ import { connect } from 'react-redux';
|
||||
import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { tryReverseResolveAddress } from '../../../store/actions';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import {
|
||||
getAddressBook,
|
||||
getBlockExplorerLinkText,
|
||||
@ -11,6 +14,10 @@ import {
|
||||
getAccountName,
|
||||
getMetadataContractName,
|
||||
getMetaMaskIdentities,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getSelectedIdentity,
|
||||
getKnownMethodData,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../selectors';
|
||||
import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils';
|
||||
import TransactionListItemDetails from './transaction-list-item-details.component';
|
||||
@ -40,6 +47,13 @@ const mapStateToProps = (state, ownProps) => {
|
||||
|
||||
const isCustomNetwork = getIsCustomNetwork(state);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const data = ownProps.transactionGroup?.primaryTransaction?.txParams?.data;
|
||||
const methodData = getKnownMethodData(state, data) || {};
|
||||
const transactionNote =
|
||||
ownProps.transactionGroup?.primaryTransaction?.metadata?.note;
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
return {
|
||||
rpcPrefs,
|
||||
recipientEns,
|
||||
@ -49,14 +63,29 @@ const mapStateToProps = (state, ownProps) => {
|
||||
blockExplorerLinkText: getBlockExplorerLinkText(state),
|
||||
recipientName,
|
||||
recipientMetadataName,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
methodData,
|
||||
transactionNote,
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const mmiActions = mmiActionsFactory();
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
return {
|
||||
tryReverseResolveAddress: (address) => {
|
||||
return dispatch(tryReverseResolveAddress(address));
|
||||
},
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getCustodianTransactionDeepLink: (address, txId) => {
|
||||
return dispatch(
|
||||
mmiActions.getCustodianTransactionDeepLink(address, txId),
|
||||
);
|
||||
},
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -67,4 +67,13 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
&__icon-badge {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 18px;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
|
@ -13,6 +13,10 @@ import { CONFIRM_TRANSACTION_ROUTE } from '../../../helpers/constants/routes';
|
||||
import { useShouldShowSpeedUp } from '../../../hooks/useShouldShowSpeedUp';
|
||||
import TransactionStatusLabel from '../transaction-status-label/transaction-status-label';
|
||||
import TransactionIcon from '../transaction-icon';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import { IconColor } from '../../../helpers/constants/design-system';
|
||||
import { Icon, IconName, IconSize } from '../../component-library';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
|
||||
import {
|
||||
TransactionGroupCategory,
|
||||
@ -125,6 +129,9 @@ function TransactionListItemInner({
|
||||
const isApproval = category === TransactionGroupCategory.approval;
|
||||
const isUnapproved = status === TransactionStatus.unapproved;
|
||||
const isSwap = category === TransactionGroupCategory.swap;
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const isCustodian = Boolean(transactionGroup.primaryTransaction.custodyId);
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
const className = classnames('transaction-list-item', {
|
||||
'transaction-list-item--unconfirmed':
|
||||
@ -144,10 +151,29 @@ function TransactionListItemInner({
|
||||
setShowDetails((prev) => !prev);
|
||||
}, [isUnapproved, history, id]);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const debugTransactionMeta = {
|
||||
'data-hash': transactionGroup.primaryTransaction.hash,
|
||||
...(isCustodian
|
||||
? {
|
||||
'data-custodiantransactionid':
|
||||
transactionGroup.primaryTransaction.custodyId,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
const speedUpButton = useMemo(() => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
if (isCustodian) {
|
||||
return null;
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
if (!shouldShowSpeedUp || !isPending || isUnapproved) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
@ -165,10 +191,32 @@ function TransactionListItemInner({
|
||||
hasCancelled,
|
||||
retryTransaction,
|
||||
cancelTransaction,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
isCustodian,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
]);
|
||||
|
||||
const showCancelButton = !hasCancelled && isPending && !isUnapproved;
|
||||
const showBorder = process.env.MULTICHAIN;
|
||||
let showCancelButton = !hasCancelled && isPending && !isUnapproved;
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
showCancelButton = showCancelButton && !isCustodian;
|
||||
const PENDING_COLOR = IconColor.iconAlternative;
|
||||
const OK_COLOR = IconColor.primaryDefault;
|
||||
const FAIL_COLOR = IconColor.errorDefault;
|
||||
const getTransactionColor = (tsStatus) => {
|
||||
switch (tsStatus) {
|
||||
case TransactionStatus.signed:
|
||||
return PENDING_COLOR;
|
||||
case TransactionStatus.rejected:
|
||||
case TransactionStatus.failed:
|
||||
case TransactionStatus.dropped:
|
||||
return FAIL_COLOR;
|
||||
default:
|
||||
return OK_COLOR;
|
||||
}
|
||||
};
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -177,7 +225,26 @@ function TransactionListItemInner({
|
||||
className={className}
|
||||
title={title}
|
||||
icon={
|
||||
<TransactionIcon category={category} status={displayedStatusKey} />
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
isCustodian ? (
|
||||
<div style={{ position: 'relative' }} data-testid="custody-icon">
|
||||
<TransactionIcon
|
||||
category={category}
|
||||
status={displayedStatusKey}
|
||||
/>
|
||||
<Icon
|
||||
name={IconName.Custody}
|
||||
className="transaction-list-item__icon-badge"
|
||||
color={getTransactionColor(status)}
|
||||
size={IconSize.Xs}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
<TransactionIcon category={category} status={displayedStatusKey} />
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
showBorder={showBorder}
|
||||
subtitle={
|
||||
@ -188,6 +255,12 @@ function TransactionListItemInner({
|
||||
error={err}
|
||||
date={date}
|
||||
status={displayedStatusKey}
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
custodyStatus={transactionGroup.primaryTransaction.custodyStatus}
|
||||
custodyStatusDisplayText={
|
||||
transactionGroup.primaryTransaction.custodyStatusDisplayText
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
/>
|
||||
{subtitleContainsOrigin ? (
|
||||
<SiteOrigin siteOrigin={subtitle} />
|
||||
@ -224,6 +297,11 @@ function TransactionListItemInner({
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
<a {...debugTransactionMeta} className="test-transaction-meta" />
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
</ListItem>
|
||||
{showDetails && (
|
||||
<TransactionListItemDetails
|
||||
@ -234,11 +312,28 @@ function TransactionListItemInner({
|
||||
senderAddress={senderAddress}
|
||||
recipientAddress={recipientAddress}
|
||||
onRetry={retryTransaction}
|
||||
showRetry={status === TransactionStatus.failed && !isSwap}
|
||||
showSpeedUp={shouldShowSpeedUp}
|
||||
showRetry={
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
!isCustodian &&
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
status === TransactionStatus.failed &&
|
||||
!isSwap
|
||||
}
|
||||
showSpeedUp={
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
!isCustodian &&
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
shouldShowSpeedUp
|
||||
}
|
||||
isEarliestNonce={isEarliestNonce}
|
||||
onCancel={cancelTransaction}
|
||||
showCancel={isPending && !hasCancelled}
|
||||
showCancel={
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
!isCustodian &&
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
isPending &&
|
||||
!hasCancelled
|
||||
}
|
||||
transactionStatus={() => (
|
||||
<TransactionStatusLabel
|
||||
isPending={isPending}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import mockState from '../../../../test/data/mock-state.json';
|
||||
import transactionGroup from '../../../../test/data/mock-pending-transaction-data.json';
|
||||
import {
|
||||
getConversionRate,
|
||||
@ -72,6 +74,20 @@ jest.mock('react', () => {
|
||||
};
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
const generateUseSelectorRouter = (opts) => (selector) => {
|
||||
if (selector === getConversionRate) {
|
||||
return 1;
|
||||
@ -146,5 +162,49 @@ describe('TransactionListItem', () => {
|
||||
fireEvent.click(cancelButton);
|
||||
expect(getByText('Cancel transaction')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -123,23 +123,36 @@ export default function TransactionList({
|
||||
<div className="transaction-list__header">
|
||||
{`${t('queue')} (${pendingTransactions.length})`}
|
||||
</div>
|
||||
{pendingTransactions.map((transactionGroup, index) =>
|
||||
transactionGroup.initialTransaction.transactionType ===
|
||||
TransactionType.smart ? (
|
||||
<SmartTransactionListItem
|
||||
isEarliestNonce={index === 0}
|
||||
smartTransaction={transactionGroup.initialTransaction}
|
||||
transactionGroup={transactionGroup}
|
||||
key={`${transactionGroup.nonce}:${index}`}
|
||||
/>
|
||||
) : (
|
||||
<TransactionListItem
|
||||
isEarliestNonce={index === 0}
|
||||
transactionGroup={transactionGroup}
|
||||
key={`${transactionGroup.nonce}:${index}`}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{pendingTransactions
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
.sort(
|
||||
(a, b) => b.primaryTransaction.time - a.primaryTransaction.time,
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
.map((transactionGroup, index) => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
if (
|
||||
transactionGroup.initialTransaction.transactionType ===
|
||||
TransactionType.smart
|
||||
) {
|
||||
return (
|
||||
<SmartTransactionListItem
|
||||
isEarliestNonce={index === 0}
|
||||
smartTransaction={transactionGroup.initialTransaction}
|
||||
transactionGroup={transactionGroup}
|
||||
key={`${transactionGroup.nonce}:${index}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
return (
|
||||
<TransactionListItem
|
||||
isEarliestNonce={index === 0}
|
||||
transactionGroup={transactionGroup}
|
||||
key={`${transactionGroup.nonce}:${index}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="transaction-list__completed-transactions">
|
||||
@ -148,6 +161,11 @@ export default function TransactionList({
|
||||
) : null}
|
||||
{completedTransactions.length > 0 ? (
|
||||
completedTransactions
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
.sort(
|
||||
(a, b) => b.primaryTransaction.time - a.primaryTransaction.time,
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
.slice(0, limit)
|
||||
.map((transactionGroup, index) =>
|
||||
transactionGroup.initialTransaction?.transactionType ===
|
||||
|
Loading…
Reference in New Issue
Block a user