Skip to content

Commit

Permalink
Merge branch 'main' into fix/stx-banner-typo
Browse files Browse the repository at this point in the history
  • Loading branch information
httpJunkie authored Feb 19, 2025
2 parents 3b4075f + de4d322 commit 565dff3
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Approve should render transaction approval 1`] = `
exports[`Approve renders transaction approval 1`] = `
<View
style={
{
Expand Down Expand Up @@ -722,6 +722,7 @@ exports[`Approve should render transaction approval 1`] = `
"flexDirection": "column",
}
}
testID="view-transaction-details"
>
<View
style={
Expand Down
117 changes: 76 additions & 41 deletions app/components/Views/confirmations/ApproveView/Approve/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react-native';
import { merge } from 'lodash';
import { act, render, screen, fireEvent } from '@testing-library/react-native';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import { Store } from 'redux';
Expand All @@ -10,6 +11,8 @@ import Approve from './index';
import { ThemeContext, mockTheme } from '../../../../../util/theme';
import initialRootState from '../../../../../util/test/initial-root-state';
import Routes from '../../../../../constants/navigation/Routes';
// eslint-disable-next-line import/no-namespace
import * as TransactionController from '../../../../../util/transaction-controller';

const TRANSACTION_ID_MOCK = '123';
jest.mock('../../../../../selectors/smartTransactionsController', () => ({
Expand All @@ -36,12 +39,26 @@ jest.mock('../../../../../core/Engine', () => ({
},
rejectPendingApproval: jest.fn(),
context: {
AccountsController: {
state: {
internalAccounts: {
accounts: {
'30786334-3935-4563-b064-363339643939': {
address: '0xc4955c0d639d99699bfd7ec54d9fafee40e4d272',
},
},
},
},
},
AssetsContractController: {
getERC20BalanceOf: jest.fn().mockResolvedValue(null),
},
KeyringController: {
getOrAddQRKeyring: jest.fn(),
},
TransactionController: {
getNonceLock: jest.fn().mockResolvedValue({ nextNonce: 2, releaseLock: jest.fn() }),
},
},
}));

Expand Down Expand Up @@ -135,28 +152,28 @@ describe('Approve', () => {
});
});

it('should render transaction approval', () => {
it('renders transaction approval', () => {
const wrapper = renderComponent({ store });
expect(wrapper).toMatchSnapshot();
});

it('should navigate on confirm to the change in simulation modal when the transaction marked with isUpdatedAfterSecurityCheck as true', () => {
const storeWithUpdatedTransaction = mockStore({
...initialRootState,
transaction: {
id: TRANSACTION_ID_MOCK,
},
settings: {
primaryCurrency: 'Fiat',
},
alert: {
isVisible: false,
},
it('renders transaction approval details', async () => {
const { findByText, findByTestId } = renderComponent({ store });

await act(async () => {
fireEvent.press(await findByTestId('view-transaction-details'));
});

expect(await findByText('Transaction Details')).toBeTruthy();
expect(await findByText('Approve asset:')).toBeTruthy();
expect(await findByText('undefined (#1110)')).toBeTruthy();
});

it('navigates on confirm to the change in simulation modal when the transaction marked with isUpdatedAfterSecurityCheck as true', async () => {
const storeWithUpdatedTransaction = mockStore(merge({}, store.getState(), {
engine: {
backgroundState: {
...initialRootState.engine.backgroundState,
TransactionController: {
...initialRootState.engine.backgroundState.TransactionController,
transactions: [
{
id: TRANSACTION_ID_MOCK,
Expand All @@ -166,41 +183,59 @@ describe('Approve', () => {
},
],
},
AccountsController: {
...initialRootState.engine.backgroundState.AccountsController,
internalAccounts: {
...initialRootState.engine.backgroundState.AccountsController
.internalAccounts,
selectedAccount: '30786334-3935-4563-b064-363339643939',
accounts: {
'30786334-3935-4563-b064-363339643939': {
address: '0xc4955c0d639d99699bfd7ec54d9fafee40e4d272',
},
},
},
},
TokensController: {
...initialRootState.engine.backgroundState.TokensController,
allTokens: {
...initialRootState.engine.backgroundState.TokensController
.allTokens,
'0x1': {
'0xc4955c0d639d99699bfd7ec54d9fafee40e4d272': [],
},
},
},
},
},
});
}));

renderComponent({ store: storeWithUpdatedTransaction });

fireEvent.press(screen.getByTestId('Confirm'));
await act(async () => {
fireEvent.press(screen.getByTestId('Confirm'));
});

expect(navigationPropMock.navigate).toHaveBeenCalledWith(
Routes.MODAL.ROOT_MODAL_FLOW,
expect.objectContaining({
screen: Routes.SHEET.CHANGE_IN_SIMULATION_MODAL,
}),
);
});

it('displays the latest nonce from transaction.transaction when showCustomNonce is true', async () => {
const getNetworkNonceSpy = jest.spyOn(TransactionController, 'getNetworkNonce');

const storeWithTransaction = mockStore(merge({}, store.getState(), {
settings: {
showCustomNonce: true,
},
engine: {
backgroundState: {
TransactionController: {
transactions: [{
id: TRANSACTION_ID_MOCK,
transaction: {
from: '0xfrom',
to: '0xto',
nonce: '0x2',
},
mode: 'edit',
}],
},
},
},
transaction: {
mode: 'edit' // Add mode to transaction state
}
}));

renderComponent({
store: storeWithTransaction,
});

expect(getNetworkNonceSpy).toHaveBeenCalledWith({'id': TRANSACTION_ID_MOCK, 'mode': 'edit'}, undefined);
const nonceSpyResult = await getNetworkNonceSpy.mock.results[0].value;
expect(nonceSpyResult).toBe(2);

getNetworkNonceSpy.mockRestore();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ exports[`ApproveTransactionModal render matches snapshot 1`] = `
"flexDirection": "column",
}
}
testID="view-transaction-details"
>
<View
style={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ class ApproveTransactionReview extends PureComponent {
componentDidMount = async () => {
const { chainId } = this.props;
const {
// We need to extract transaction.transaction here to retrieve up-to-date nonce
transaction: { origin, to, data, from, transaction },
setTransactionObject,
tokenList,
Expand Down Expand Up @@ -1074,6 +1075,7 @@ class ApproveTransactionReview extends PureComponent {
<TouchableOpacity
style={styles.actionTouchable}
onPress={this.toggleViewDetails}
testID="view-transaction-details"
>
<View style={styles.iconContainer}>
<Text reset style={styles.viewDetailsText}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { StyleSheet } from 'react-native';

import { Theme } from '../../../../../../util/theme/models';

const styleSheet = (params: {
theme: Theme;
vars: { confirmDisabled: boolean };
vars: { confirmDisabled: boolean; isStakingConfirmation: boolean };
}) => {
const {
theme,
vars: { confirmDisabled },
vars: { confirmDisabled, isStakingConfirmation },
} = params;

return StyleSheet.create({
Expand All @@ -25,11 +24,21 @@ const styleSheet = (params: {
},
buttonsContainer: {
flexDirection: 'row',
paddingVertical: 16,
paddingTop: 16,
paddingBottom: isStakingConfirmation ? 6 : 16,
},
buttonDivider: {
width: 8,
},
linkText: {
textDecorationLine: 'underline',
},
textContainer: {
flexDirection: 'row',
justifyContent: 'center',
flexWrap: 'wrap',
marginBottom: 24,
},
});
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { TransactionType } from '@metamask/transaction-controller';
import React from 'react';
import { View } from 'react-native';

import { Linking, View } from 'react-native';
import { ConfirmationFooterSelectorIDs } from '../../../../../../../e2e/selectors/Confirmation/ConfirmationView.selectors';
import { strings } from '../../../../../../../locales/i18n';
import Button, {
ButtonSize,
ButtonVariants,
ButtonWidthTypes,
} from '../../../../../../component-library/components/Buttons/Button';
import Text, { TextVariant } from '../../../../../../component-library/components/Texts/Text';
import { useStyles } from '../../../../../../component-library/hooks';
import AppConstants from '../../../../../../core/AppConstants';
import { useQRHardwareContext } from '../../../context/QRHardwareContext/QRHardwareContext';
import { useScrollContext } from '../../../context/ScrollContext';
import { useConfirmActions } from '../../../hooks/useConfirmActions';
import { useSecurityAlertResponse } from '../../../hooks/useSecurityAlertResponse';
import { useQRHardwareContext } from '../../../context/QRHardwareContext/QRHardwareContext';
import { useTransactionMetadataRequest } from '../../../hooks/useTransactionMetadataRequest';
import { ResultType } from '../../BlockaidBanner/BlockaidBanner.types';
import styleSheet from './Footer.styles';
import { useScrollContext } from '../../../context/ScrollContext';

const Footer = () => {
const { onConfirm, onReject } = useConfirmActions();
Expand All @@ -23,35 +26,76 @@ const Footer = () => {
const { securityAlertResponse } = useSecurityAlertResponse();
const { isScrollToBottomNeeded } = useScrollContext();
const confirmDisabled = needsCameraPermission || isScrollToBottomNeeded;
const { styles } = useStyles(styleSheet, { confirmDisabled });
const transactionMetadata = useTransactionMetadataRequest();
const isStakingConfirmation = [
TransactionType.stakingDeposit,
TransactionType.stakingUnstake,
TransactionType.stakingClaim,
].includes(transactionMetadata?.type as TransactionType);
const { styles } = useStyles(styleSheet, { confirmDisabled, isStakingConfirmation });

return (
<View style={styles.buttonsContainer}>
<Button
onPress={onReject}
label={strings('confirm.reject')}
style={styles.rejectButton}
size={ButtonSize.Lg}
testID={ConfirmationFooterSelectorIDs.CANCEL_BUTTON}
variant={ButtonVariants.Secondary}
width={ButtonWidthTypes.Full}
/>
<View style={styles.buttonDivider} />
<Button
onPress={onConfirm}
label={
isQRSigningInProgress
? strings('confirm.qr_get_sign')
: strings('confirm.confirm')
}
style={styles.confirmButton}
size={ButtonSize.Lg}
testID={ConfirmationFooterSelectorIDs.CONFIRM_BUTTON}
variant={ButtonVariants.Primary}
width={ButtonWidthTypes.Full}
isDanger={securityAlertResponse?.result_type === ResultType.Malicious}
disabled={confirmDisabled}
/>
<View>
<View style={styles.buttonsContainer}>
<Button
onPress={onReject}
label={strings('confirm.reject')}
style={styles.rejectButton}
size={ButtonSize.Lg}
testID={ConfirmationFooterSelectorIDs.CANCEL_BUTTON}
variant={ButtonVariants.Secondary}
width={ButtonWidthTypes.Full}
/>
<View style={styles.buttonDivider} />
<Button
onPress={onConfirm}
label={
isQRSigningInProgress
? strings('confirm.qr_get_sign')
: strings('confirm.confirm')
}
style={styles.confirmButton}
size={ButtonSize.Lg}
testID={ConfirmationFooterSelectorIDs.CONFIRM_BUTTON}
variant={ButtonVariants.Primary}
width={ButtonWidthTypes.Full}
isDanger={securityAlertResponse?.result_type === ResultType.Malicious}
disabled={confirmDisabled}
/>
</View>
{isStakingConfirmation && (
<View style={styles.textContainer}>
<Text
variant={TextVariant.BodySM}
>
{strings('confirm.staking_footer.part1')}
</Text>
<Text
variant={TextVariant.BodySM}
style={styles.linkText}
onPress={() => Linking.openURL(AppConstants.URLS.TERMS_OF_USE)}
>
{strings('confirm.staking_footer.terms_of_use')}
</Text>
<Text
variant={TextVariant.BodySM}
>
{strings('confirm.staking_footer.part2')}
</Text>
<Text
variant={TextVariant.BodySM}
style={styles.linkText}
onPress={() => Linking.openURL(AppConstants.URLS.STAKING_RISK_DISCLOSURE)}
>
{strings('confirm.staking_footer.risk_disclosure')}
</Text>
<Text
variant={TextVariant.BodySM}
>
{strings('confirm.staking_footer.part3')}
</Text>
</View>
)}
</View>
);
};
Expand Down
Loading

0 comments on commit 565dff3

Please sign in to comment.