diff --git a/app/component-library/components/Pickers/PickerAccount/PickerAccount.styles.ts b/app/component-library/components/Pickers/PickerAccount/PickerAccount.styles.ts
index 9bf03d0adbc..d4d99c40475 100644
--- a/app/component-library/components/Pickers/PickerAccount/PickerAccount.styles.ts
+++ b/app/component-library/components/Pickers/PickerAccount/PickerAccount.styles.ts
@@ -3,7 +3,6 @@ import { StyleSheet, ViewStyle } from 'react-native';
// External dependencies.
import { Theme } from '../../../../util/theme/models';
-import { fontStyles } from '../../../../styles/common';
// Internal dependencies.
import { PickerAccountStyleSheetVars } from './PickerAccount.types';
@@ -24,34 +23,39 @@ const styleSheet = (params: {
const { colors } = theme;
const { style, cellAccountContainerStyle } = vars;
return StyleSheet.create({
- base: Object.assign({} as ViewStyle, style) as ViewStyle,
+ base: {
+ ...(style as ViewStyle),
+ flexDirection: 'row',
+ padding: 0,
+ borderWidth: 0,
+ },
accountAvatar: {
- marginRight: 16,
+ marginRight: 8,
},
accountAddressLabel: {
color: colors.text.alternative,
+ textAlign: 'center',
},
cellAccount: {
- flex: 1,
flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
...cellAccountContainerStyle,
},
accountNameLabel: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ accountNameAvatar: {
flexDirection: 'row',
alignItems: 'center',
- justifyContent: 'flex-start',
},
- accountNameLabelText: {
- marginTop: 4,
- marginHorizontal: 5,
- paddingHorizontal: 5,
- ...fontStyles.bold,
- color: colors.text.alternative,
- borderWidth: 1,
- borderRadius: 10,
- borderColor: colors.border.default,
+ pickerAccountContainer: {
justifyContent: 'center',
- textAlign: 'center',
+ alignItems: 'center',
+ },
+ dropDownIcon: {
+ marginLeft: 8,
},
});
};
diff --git a/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx b/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx
index 4093e7da1ae..0bec81483f0 100644
--- a/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx
+++ b/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx
@@ -9,6 +9,7 @@ import Avatar, { AvatarSize, AvatarVariant } from '../../Avatars/Avatar';
import Text, { TextVariant } from '../../Texts/Text';
import { formatAddress } from '../../../../util/address';
import { useStyles } from '../../../hooks';
+import { IconSize } from '../../Icons/Icon';
// Internal dependencies.
import PickerBase from '../PickerBase';
@@ -25,7 +26,6 @@ const PickerAccount: React.ForwardRefRenderFunction<
accountAddress,
accountName,
accountAvatarType,
- accountTypeLabel,
showAddress = true,
cellAccountContainerStyle = {},
...props
@@ -40,33 +40,46 @@ const PickerAccount: React.ForwardRefRenderFunction<
const renderCellAccount = () => (
-
-
- {accountName}
-
- {showAddress && (
-
- {shortenedAddress}
+
+
+
+ {accountName}
- )}
+
);
return (
-
- {renderCellAccount()}
-
+
+
+ {renderCellAccount()}
+
+ {showAddress && (
+
+ {shortenedAddress}
+
+ )}
+
);
};
diff --git a/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap b/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap
index 0afdb8affeb..a078072c4da 100644
--- a/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap
+++ b/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap
@@ -1,223 +1,242 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PickerAccount should render correctly 1`] = `
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+ }
+ testID="account-label"
+ >
+ Orangefox.eth
+
+
-
-
- Orangefox.eth
-
-
- 0x2990...a21a
-
-
-
-
+
+
-
+ >
+ 0x2990...a21a
+
+
`;
diff --git a/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.test.tsx b/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.test.tsx
index 948721d6ac4..65666c2e125 100644
--- a/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.test.tsx
+++ b/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.test.tsx
@@ -45,6 +45,20 @@ describe('PickerNetwork', () => {
).toBeNull();
});
+ it('shows network name when hideNetworkName is false', () => {
+ const { queryByTestId } = render(
+ ,
+ );
+
+ expect(
+ queryByTestId(WalletViewSelectorsIDs.NAVBAR_NETWORK_TEXT),
+ ).not.toBeNull();
+ });
+
it('calls onPress when pressed', () => {
const onPress = jest.fn();
const { getByTestId } = render(
diff --git a/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.tsx b/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.tsx
index 1b4642ba967..29c48def333 100644
--- a/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.tsx
+++ b/app/component-library/components/Pickers/PickerNetwork/PickerNetwork.tsx
@@ -34,6 +34,8 @@ const PickerNetwork = ({
size={AvatarSize.Xs}
name={label}
imageSource={imageSource}
+ testID={WalletViewSelectorsIDs.NAVBAR_NETWORK_PICKER}
+ accessibilityLabel={label}
/>
{hideNetworkName ? null : (
diff --git a/app/component-library/components/Pickers/PickerNetwork/__snapshots__/PickerNetwork.test.tsx.snap b/app/component-library/components/Pickers/PickerNetwork/__snapshots__/PickerNetwork.test.tsx.snap
index f59fab74933..c2965c38bb0 100644
--- a/app/component-library/components/Pickers/PickerNetwork/__snapshots__/PickerNetwork.test.tsx.snap
+++ b/app/component-library/components/Pickers/PickerNetwork/__snapshots__/PickerNetwork.test.tsx.snap
@@ -19,6 +19,7 @@ exports[`PickerNetwork renders correctly 1`] = `
style={null}
>
{
{
};
});
+const mockNavigate = jest.fn();
+
+jest.mock('@react-navigation/native', () => ({
+ ...jest.requireActual('@react-navigation/native'),
+ useNavigation: () => ({
+ navigate: mockNavigate,
+ }),
+}));
+
const initialState = {
engine: {
backgroundState: {
diff --git a/app/components/UI/AccountSelectorList/AccountSelectorList.tsx b/app/components/UI/AccountSelectorList/AccountSelectorList.tsx
index 8d990dee513..30b8241836f 100644
--- a/app/components/UI/AccountSelectorList/AccountSelectorList.tsx
+++ b/app/components/UI/AccountSelectorList/AccountSelectorList.tsx
@@ -1,15 +1,18 @@
// Third party dependencies.
import React, { useCallback, useRef } from 'react';
-import { Alert, ListRenderItem, View } from 'react-native';
+import { Alert, ListRenderItem, View, ViewStyle } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import { useSelector } from 'react-redux';
+import { useNavigation } from '@react-navigation/native';
import { KeyringTypes } from '@metamask/keyring-controller';
import type { Hex } from '@metamask/utils';
// External dependencies.
+import { selectInternalAccounts } from '../../../selectors/accountsController';
import Cell, {
CellVariant,
} from '../../../component-library/components/Cells/Cell';
+import { InternalAccount } from '@metamask/keyring-api';
import { useStyles } from '../../../component-library/hooks';
import { TextColor } from '../../../component-library/components/Texts/Text';
import SensitiveText, {
@@ -28,11 +31,13 @@ import { AvatarVariant } from '../../../component-library/components/Avatars/Ava
import { Account, Assets } from '../../hooks/useAccounts';
import UntypedEngine from '../../../core/Engine';
import { removeAccountsFromPermissions } from '../../../core/Permissions';
+import Routes from '../../../constants/navigation/Routes';
// Internal dependencies.
import { AccountSelectorListProps } from './AccountSelectorList.types';
import styleSheet from './AccountSelectorList.styles';
import { AccountListViewSelectorsIDs } from '../../../../e2e/selectors/AccountListView.selectors';
+import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors';
const AccountSelectorList = ({
onSelectAccount,
@@ -49,6 +54,7 @@ const AccountSelectorList = ({
privacyMode = false,
...props
}: AccountSelectorListProps) => {
+ const { navigate } = useNavigation();
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Engine = UntypedEngine as any;
@@ -64,6 +70,8 @@ const AccountSelectorList = ({
? AvatarAccountType.Blockies
: AvatarAccountType.JazzIcon,
);
+
+ const internalAccounts = useSelector(selectInternalAccounts);
const getKeyExtractor = ({ address }: Account) => address;
const renderAccountBalances = useCallback(
@@ -169,6 +177,23 @@ const AccountSelectorList = ({
],
);
+ const onNavigateToAccountActions = useCallback(
+ (selectedAccount: string) => {
+ const account = internalAccounts.find(
+ (accountData: InternalAccount) =>
+ accountData.address.toLowerCase() === selectedAccount.toLowerCase(),
+ );
+
+ if (!account) return;
+
+ navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
+ screen: Routes.SHEET.ACCOUNT_ACTIONS,
+ params: { selectedAccount: account },
+ });
+ },
+ [navigate, internalAccounts],
+ );
+
const renderAccountItem: ListRenderItem = useCallback(
({
item: { name, address, assets, type, isSelected, balanceError },
@@ -182,7 +207,7 @@ const AccountSelectorList = ({
const isDisabled = !!balanceError || isLoading || isSelectionDisabled;
const cellVariant = isMultiSelect
? CellVariant.MultiSelect
- : CellVariant.Select;
+ : CellVariant.SelectWithMenu;
let isSelectedAccount = isSelected;
if (selectedAddresses) {
const lowercasedSelectedAddresses = selectedAddresses.map(
@@ -193,12 +218,16 @@ const AccountSelectorList = ({
);
}
- const cellStyle = {
+ const cellStyle: ViewStyle = {
opacity: isLoading ? 0.5 : 1,
};
+ if (!isMultiSelect) {
+ cellStyle.alignItems = 'center';
+ }
return (
{
onLongPress({
address,
@@ -211,6 +240,7 @@ const AccountSelectorList = ({
isSelected={isSelectedAccount}
title={accountName}
secondaryText={shortAddress}
+ showSecondaryTextIcon={false}
tertiaryText={balanceError}
onPress={() => onSelectAccount?.(address, isSelectedAccount)}
avatarProps={{
@@ -221,6 +251,10 @@ const AccountSelectorList = ({
tagLabel={tagLabel}
disabled={isDisabled}
style={cellStyle}
+ buttonProps={{
+ onButtonClick: () => onNavigateToAccountActions(address),
+ buttonTestId: `${WalletViewSelectorsIDs.ACCOUNT_ACTIONS}-${index}`,
+ }}
>
{renderRightAccessory?.(address, accountName) ||
(assets && renderAccountBalances(assets, address))}
@@ -228,6 +262,7 @@ const AccountSelectorList = ({
);
},
[
+ onNavigateToAccountActions,
accountAvatarType,
onSelectAccount,
renderAccountBalances,
diff --git a/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap b/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap
index 036a2be8d53..dd4954812d8 100644
--- a/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap
+++ b/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap
@@ -57,591 +57,772 @@ exports[`AccountSelectorList renders all accounts with balances 1`] = `
onLayout={[Function]}
style={null}
>
-
-
-
-
-
-
+
-
-
-
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
-
-
-
- Account 1
-
-
- 0xC495...D272
-
-
-
- $3200.00
+ Account 1
-
- 1 ETH
-
+
+ 0xC495...D272
+
+
+ |
+
+
+
+ $3200.00
+
+
+ 1 ETH
+
+
-
+
+
+
+
-
+ testID="main-wallet-account-actions-0"
+ >
+
+
-
+
-
-
-
-
-
-
+
-
-
-
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
-
-
-
- Account 2
-
-
- 0xd018...78E7
-
-
-
- $6400.00
+ Account 2
-
- 2 ETH
-
+
+ 0xd018...78E7
+
+
+
+
+
+
+ $6400.00
+
+
+ 2 ETH
+
+
+
+
+
+
+
-
+
@@ -704,492 +885,672 @@ exports[`AccountSelectorList renders all accounts with right accessory 1`] = `
onLayout={[Function]}
style={null}
>
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- Account 1
-
-
+ Account 1
+
+
+
+ 0xC495...D272
+
+
+
+
- 0xC495...D272
-
-
-
-
- 0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272 - Account 1
+
+ 0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272 - Account 1
+
+
+
+
+
+
-
+
-
-
-
-
-
-
+
-
-
-
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
-
-
-
- Account 2
-
-
+ Account 2
+
+
+
+ 0xd018...78E7
+
+
+
+
- 0xd018...78E7
-
-
-
-
- 0xd018538C87232FF95acbCe4870629b75640a78E7 - Account 2
+
+ 0xd018538C87232FF95acbCe4870629b75640a78E7 - Account 2
+
+
+
+
+
+
-
+
@@ -1252,591 +1613,772 @@ exports[`AccountSelectorList renders correctly 1`] = `
onLayout={[Function]}
style={null}
>
-
-
-
-
-
-
+
-
-
-
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
-
-
-
- Account 1
-
-
- 0xC495...D272
-
-
-
- $3200.00
+ Account 1
-
- 1 ETH
-
+
+ 0xC495...D272
+
+
+
+
+
+
+ $3200.00
+
+
+ 1 ETH
+
+
-
+
+
+
+
-
+ testID="main-wallet-account-actions-0"
+ >
+
+
-
+
-
-
-
-
-
-
+
-
-
-
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
-
-
-
- Account 2
-
-
- 0xd018...78E7
-
-
-
- $6400.00
+ Account 2
-
- 2 ETH
-
+
+ 0xd018...78E7
+
+
+
+
+
+
+ $6400.00
+
+
+ 2 ETH
+
+
+
+
+
+
+
-
+
@@ -1896,309 +2438,490 @@ exports[`AccountSelectorList should render all accounts but only the balance for
onLayout={[Function]}
style={null}
>
-
-
-
-
-
-
- Account 1
-
-
- 0xC495...D272
-
-
-
+ style={
+ {
+ "flex": 1,
+ }
+ }
+ />
+
- $3200.00
+ Account 1
-
- 1 ETH
-
+
+ 0xC495...D272
+
+
+
+
+
+
+ $3200.00
+
+
+ 1 ETH
+
+
-
+
+
+
+
-
+ testID="main-wallet-account-actions-0"
+ >
+
+
-
+
-
-
-
-
-
-
- Account 2
-
-
+
+
- 0xd018...78E7
-
+
+ Account 2
+
+
+
+ 0xd018...78E7
+
+
+
+
+
+
+
+
-
+
diff --git a/app/components/UI/AddressCopy/AddressCopy.styles.ts b/app/components/UI/AddressCopy/AddressCopy.styles.ts
index 089c48d5136..46d5ba9d066 100644
--- a/app/components/UI/AddressCopy/AddressCopy.styles.ts
+++ b/app/components/UI/AddressCopy/AddressCopy.styles.ts
@@ -1,26 +1,14 @@
import { StyleSheet } from 'react-native';
-// External dependencies.
-import { Theme } from '../../../util/theme/models';
-const styleSheet = (params: { theme: Theme }) => {
- const { theme } = params;
- const { colors } = theme;
-
- return StyleSheet.create({
+const styleSheet = () =>
+ StyleSheet.create({
address: {
flexDirection: 'row',
alignItems: 'center',
},
copyButton: {
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: colors.primary.muted,
- borderRadius: 20,
- paddingHorizontal: 12,
padding: 4,
- marginLeft: 12,
},
- icon: { marginLeft: 4 },
});
-};
+
export default styleSheet;
diff --git a/app/components/UI/AddressCopy/AddressCopy.tsx b/app/components/UI/AddressCopy/AddressCopy.tsx
index d295c5f75ad..a99e46a4727 100644
--- a/app/components/UI/AddressCopy/AddressCopy.tsx
+++ b/app/components/UI/AddressCopy/AddressCopy.tsx
@@ -3,12 +3,7 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
// External dependencies
-import Text, {
- TextColor,
- TextVariant,
-} from '../../../component-library/components/Texts/Text';
import { TouchableOpacity } from 'react-native-gesture-handler';
-import { formatAddress } from '../../../util/address';
import Icon, {
IconColor,
IconName,
@@ -25,12 +20,11 @@ import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletV
// Internal dependencies
import styleSheet from './AddressCopy.styles';
-import { AddressCopyProps } from './AddressCopy.types';
import { selectSelectedInternalAccount } from '../../../selectors/accountsController';
import { useMetrics } from '../../../components/hooks/useMetrics';
import { toChecksumHexAddress } from '@metamask/controller-utils';
-const AddressCopy = ({ formatAddressType = 'full' }: AddressCopyProps) => {
+const AddressCopy = () => {
const { styles } = useStyles(styleSheet, {});
const dispatch = useDispatch();
@@ -69,28 +63,15 @@ const AddressCopy = ({ formatAddressType = 'full' }: AddressCopyProps) => {
};
return (
-
- {strings('asset_overview.address')}:
-
-
- {selectedInternalAccount
- ? formatAddress(selectedInternalAccount.address, formatAddressType)
- : null}
-
diff --git a/app/components/UI/AddressCopy/AddressCopy.types.ts b/app/components/UI/AddressCopy/AddressCopy.types.ts
deleted file mode 100644
index 6efa05bbea0..00000000000
--- a/app/components/UI/AddressCopy/AddressCopy.types.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export interface AddressCopyProps {
- formatAddressType?: 'short' | 'mid' | 'full';
-}
diff --git a/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx b/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx
index ed1a2f5fdc3..99054057d7e 100644
--- a/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx
+++ b/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.test.tsx
@@ -44,6 +44,7 @@ jest.mock('../../../util/navigation/navUtils', () => ({
ticker: 'ETH',
addTokenList: jest.fn(),
}),
+ createNavigationDetails: jest.fn(),
}));
const mockUseBalanceInitialValue: Partial> = {
@@ -101,14 +102,12 @@ describe('ConfirmAddAsset', () => {
expect(getByText('USDT')).toBeTruthy();
expect(getByText('$27.02')).toBeTruthy();
});
-
it('handles cancel button click', () => {
const { getByText } = renderWithProvider(, {
state: mockInitialState,
});
const cancelButton = getByText('Cancel');
fireEvent.press(cancelButton);
-
expect(getByText('Are you sure you want to exit?')).toBeTruthy();
expect(
getByText('Your search information will not be saved.'),
diff --git a/app/components/UI/ManageNetworks/__snapshots__/ManageNetworks.test.js.snap b/app/components/UI/ManageNetworks/__snapshots__/ManageNetworks.test.js.snap
index 4d89a952349..3aef46b9799 100644
--- a/app/components/UI/ManageNetworks/__snapshots__/ManageNetworks.test.js.snap
+++ b/app/components/UI/ManageNetworks/__snapshots__/ManageNetworks.test.js.snap
@@ -87,6 +87,7 @@ exports[`ManageNetworks should render correctly 1`] = `
style={null}
>
{
@@ -95,7 +101,7 @@ const styles = StyleSheet.create({
disabled: {
opacity: 0.3,
},
- leftButtonContainer: {
+ rightElementContainer: {
marginRight: 12,
flexDirection: 'row',
alignItems: 'flex-end',
@@ -113,16 +119,11 @@ const styles = StyleSheet.create({
metamaskNameWrapper: {
marginLeft: Device.isAndroid() ? 20 : 0,
},
- fox: {
- width: 24,
- height: 24,
+ leftElementContainer: {
marginLeft: 16,
},
notificationsWrapper: {
- position: 'relative',
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center',
+ marginHorizontal: 4,
},
notificationsBadge: {
width: 8,
@@ -133,6 +134,9 @@ const styles = StyleSheet.create({
top: 2,
right: 10,
},
+ addressCopyWrapper: {
+ marginHorizontal: 4,
+ },
});
const metamask_name = require('../../../images/metamask-name.png'); // eslint-disable-line
@@ -903,12 +907,28 @@ export function getOfflineModalNavbar() {
}
/**
- * Function that returns the navigation options
- * for our wallet screen,
+ * Function that returns the navigation options for the wallet screen.
*
- * @returns {Object} - Corresponding navbar options containing headerTitle, headerTitle and headerTitle
+ * @param {Object} accountActionsRef - The ref object for the account actions
+ * @param {string} selectedAddress - The currently selected Ethereum address
+ * @param {string} accountName - The name of the currently selected account
+ * @param {string} accountAvatarType - The type of avatar for the currently selected account
+ * @param {string} networkName - The name of the current network
+ * @param {Object} networkImageSource - The image source for the network icon
+ * @param {Function} onPressTitle - Callback function when the title is pressed
+ * @param {Object} navigation - The navigation object
+ * @param {Object} themeColors - The theme colors object
+ * @param {boolean} isNotificationEnabled - Whether notifications are enabled
+ * @param {boolean | null} isProfileSyncingEnabled - Whether profile syncing is enabled
+ * @param {number} unreadNotificationCount - The number of unread notifications
+ * @param {number} readNotificationCount - The number of read notifications
+ * @returns {Object} An object containing the navbar options for the wallet screen
*/
export function getWalletNavbarOptions(
+ accountActionsRef,
+ selectedAddress,
+ accountName,
+ accountAvatarType,
networkName,
networkImageSource,
onPressTitle,
@@ -921,7 +941,7 @@ export function getWalletNavbarOptions(
) {
const innerStyles = StyleSheet.create({
headerStyle: {
- backgroundColor: themeColors.background.default,
+ backgroundColor: themeColors.background,
shadowColor: importedColors.transparent,
elevation: 0,
},
@@ -1005,24 +1025,40 @@ export function getWalletNavbarOptions(
return {
headerTitle: () => (
+ {
+ navigation.navigate(...createAccountSelectorNavDetails({}));
+ }}
+ accountTypeLabel={getLabelTextByAddress(selectedAddress) || undefined}
+ showAddress
+ cellAccountContainerStyle={styles.account}
+ testID={WalletViewSelectorsIDs.ACCOUNT_ICON}
+ />
+
+ ),
+ headerLeft: () => (
+
),
- headerLeft: () => (
-
- ),
headerRight: () => (
-
+
+
+
+
{isNotificationsFeatureEnabled() && (
diff --git a/app/components/UI/NetworkModal/__snapshots__/index.test.tsx.snap b/app/components/UI/NetworkModal/__snapshots__/index.test.tsx.snap
index 8099909473b..fa6b7d2bf5b 100644
--- a/app/components/UI/NetworkModal/__snapshots__/index.test.tsx.snap
+++ b/app/components/UI/NetworkModal/__snapshots__/index.test.tsx.snap
@@ -233,6 +233,7 @@ exports[`NetworkDetails renders correctly 1`] = `
style={null}
>
{
expect(toJSON()).toMatchSnapshot();
});
- it('shows the account address', () => {
- const { getByTestId } = renderWithProvider(, {
- state: mockInitialState,
- });
- expect(getByTestId(WalletViewSelectorsIDs.ACCOUNT_ADDRESS)).toBeDefined();
- });
-
it('copies the account address to the clipboard when the copy button is pressed', async () => {
const { getByTestId } = renderWithProvider(, {
state: mockInitialState,
diff --git a/app/components/UI/WalletAccount/WalletAccount.tsx b/app/components/UI/WalletAccount/WalletAccount.tsx
index 8aa6f499ea6..d42f757971b 100644
--- a/app/components/UI/WalletAccount/WalletAccount.tsx
+++ b/app/components/UI/WalletAccount/WalletAccount.tsx
@@ -96,7 +96,7 @@ const WalletAccount = ({ style }: WalletAccountProps, ref: React.Ref) => {
/>
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+ }
+ testID="account-label"
+ >
+ Account 2
+
+
-
-
- Account 2
-
-
-
-
-
+ width={12}
+ />
+
+
-
- Address
- :
-
-
- 0xC496...a756
-
diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx
index 02655b77080..730761ea373 100644
--- a/app/components/Views/AccountActions/AccountActions.test.tsx
+++ b/app/components/Views/AccountActions/AccountActions.test.tsx
@@ -66,6 +66,18 @@ jest.mock('@react-navigation/native', () => {
navigate: mockNavigate,
goBack: mockGoBack,
}),
+ useRoute: () => ({
+ params: {
+ selectedAccount: {
+ address: '0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756',
+ metadata: {
+ keyring: {
+ type: 'HD Key Tree',
+ },
+ },
+ },
+ },
+ }),
};
});
@@ -130,7 +142,7 @@ describe('AccountActions', () => {
expect(mockNavigate).toHaveBeenCalledWith('Webview', {
screen: 'SimpleWebview',
params: {
- url: 'https://etherscan.io/address/0xc4966c0d659d99699bfd7eb54d8fafee40e4a756',
+ url: 'https://etherscan.io/address/0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756',
title: 'etherscan.io',
},
});
@@ -144,7 +156,7 @@ describe('AccountActions', () => {
fireEvent.press(getByTestId(AccountActionsModalSelectorsIDs.SHARE_ADDRESS));
expect(Share.open).toHaveBeenCalledWith({
- message: '0xc4966c0d659d99699bfd7eb54d8fafee40e4a756',
+ message: '0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756',
});
});
@@ -162,6 +174,14 @@ describe('AccountActions', () => {
{
credentialName: 'private_key',
shouldUpdateNav: true,
+ selectedAccount: {
+ address: '0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756',
+ metadata: {
+ keyring: {
+ type: 'HD Key Tree',
+ },
+ },
+ },
},
);
});
@@ -173,7 +193,16 @@ describe('AccountActions', () => {
fireEvent.press(getByTestId(AccountActionsModalSelectorsIDs.EDIT_ACCOUNT));
- expect(mockNavigate).toHaveBeenCalledWith('EditAccountName');
+ expect(mockNavigate).toHaveBeenCalledWith('EditAccountName', {
+ selectedAccount: {
+ address: '0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756',
+ metadata: {
+ keyring: {
+ type: 'HD Key Tree',
+ },
+ },
+ },
+ });
});
describe('clicks remove account', () => {
diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx
index 99547d6cfda..c44a1bc31a1 100644
--- a/app/components/Views/AccountActions/AccountActions.tsx
+++ b/app/components/Views/AccountActions/AccountActions.tsx
@@ -1,11 +1,17 @@
// Third party dependencies.
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { Alert, View, Text } from 'react-native';
-import { useNavigation } from '@react-navigation/native';
+import {
+ useNavigation,
+ RouteProp,
+ ParamListBase,
+ useRoute,
+} from '@react-navigation/native';
import { useDispatch, useSelector } from 'react-redux';
import Share from 'react-native-share';
-// External dependencies.
+// External dependencies
+import { InternalAccount } from '@metamask/keyring-api';
import BottomSheet, {
BottomSheetRef,
} from '../../../component-library/components/BottomSheets/BottomSheet';
@@ -25,7 +31,6 @@ import {
selectNetworkConfigurations,
selectProviderConfig,
} from '../../../selectors/networkController';
-import { selectSelectedInternalAccount } from '../../../selectors/accountsController';
import { strings } from '../../../../locales/i18n';
// Internal dependencies
import styleSheet from './AccountActions.styles';
@@ -50,7 +55,13 @@ import BlockingActionModal from '../../UI/BlockingActionModal';
import { useTheme } from '../../../util/theme';
import { Hex } from '@metamask/utils';
+interface AccountActionsParams {
+ selectedAccount: InternalAccount;
+}
+
const AccountActions = () => {
+ const route = useRoute>();
+ const { selectedAccount } = route.params as AccountActionsParams;
const { colors } = useTheme();
const styles = styleSheet(colors);
const sheetRef = useRef(null);
@@ -67,7 +78,6 @@ const AccountActions = () => {
const providerConfig = useSelector(selectProviderConfig);
- const selectedAccount = useSelector(selectSelectedInternalAccount);
const selectedAddress = selectedAccount?.address;
const keyring = selectedAccount?.metadata.keyring;
@@ -140,6 +150,7 @@ const AccountActions = () => {
navigate(Routes.SETTINGS.REVEAL_PRIVATE_CREDENTIAL, {
credentialName: 'private_key',
shouldUpdateNav: true,
+ selectedAccount,
});
});
};
@@ -305,7 +316,7 @@ const AccountActions = () => {
]);
const goToEditAccountName = () => {
- navigate('EditAccountName');
+ navigate('EditAccountName', { selectedAccount });
};
const isExplorerVisible = Boolean(
diff --git a/app/components/Views/AccountPermissions/__snapshots__/AccountPermissions.test.tsx.snap b/app/components/Views/AccountPermissions/__snapshots__/AccountPermissions.test.tsx.snap
index 226158d5611..8fbd535e84c 100644
--- a/app/components/Views/AccountPermissions/__snapshots__/AccountPermissions.test.tsx.snap
+++ b/app/components/Views/AccountPermissions/__snapshots__/AccountPermissions.test.tsx.snap
@@ -281,6 +281,7 @@ exports[`AccountPermissions renders correctly 1`] = `
style={null}
>
{
const actualReactNavigation = jest.requireActual('@react-navigation/native');
return {
@@ -69,6 +80,7 @@ jest.mock('@react-navigation/native', () => {
setOptions: mockSetOptions,
goBack: mockGoBack,
}),
+ useRoute: () => mockRoute,
};
});
diff --git a/app/components/Views/EditAccountName/EditAccountName.tsx b/app/components/Views/EditAccountName/EditAccountName.tsx
index d6b4a043e95..2d6c0bacc22 100644
--- a/app/components/Views/EditAccountName/EditAccountName.tsx
+++ b/app/components/Views/EditAccountName/EditAccountName.tsx
@@ -1,10 +1,16 @@
// Third party dependencies
import React, { useCallback, useEffect, useState } from 'react';
-import { useNavigation } from '@react-navigation/native';
+import {
+ useRoute,
+ useNavigation,
+ RouteProp,
+ ParamListBase,
+} from '@react-navigation/native';
import { useSelector } from 'react-redux';
import { SafeAreaView } from 'react-native';
// External dependencies
+import { InternalAccount } from '@metamask/keyring-api';
import Text from '../../../component-library/components/Texts/Text/Text';
import { View } from 'react-native-animatable';
import { TextVariant } from '../../../component-library/components/Texts/Text';
@@ -24,7 +30,6 @@ import { getEditAccountNameNavBarOptions } from '../../../components/UI/Navbar';
import Engine from '../../../core/Engine';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { selectChainId } from '../../../selectors/networkController';
-import { selectSelectedInternalAccount } from '../../../selectors/accountsController';
import {
doENSReverseLookup,
isDefaultAccountName,
@@ -37,7 +42,20 @@ import styleSheet from './EditAccountName.styles';
import { getDecimalChainId } from '../../../util/networks';
import { useMetrics } from '../../../components/hooks/useMetrics';
+interface RootNavigationParamList extends ParamListBase {
+ EditAccountName: {
+ selectedAccount: InternalAccount;
+ };
+}
+
+type EditAccountNameRouteProp = RouteProp<
+ RootNavigationParamList,
+ 'EditAccountName'
+>;
+
const EditAccountName = () => {
+ const route = useRoute();
+ const { selectedAccount } = route.params;
const { colors } = useTheme();
const { trackEvent } = useMetrics();
const { styles } = useStyles(styleSheet, {});
@@ -45,10 +63,8 @@ const EditAccountName = () => {
const [accountName, setAccountName] = useState();
const [ens, setEns] = useState();
- const selectedInternalAccount = useSelector(selectSelectedInternalAccount);
-
- const selectedChecksummedAddress = selectedInternalAccount?.address
- ? toChecksumHexAddress(selectedInternalAccount.address)
+ const selectedChecksummedAddress = selectedAccount?.address
+ ? toChecksumHexAddress(selectedAccount.address)
: undefined;
const chainId = useSelector(selectChainId);
@@ -80,28 +96,22 @@ const EditAccountName = () => {
}, [updateNavBar]);
useEffect(() => {
- const name = selectedInternalAccount?.metadata.name;
+ const name = selectedAccount?.metadata.name;
setAccountName(isDefaultAccountName(name) && ens ? ens : name);
- }, [ens, selectedInternalAccount?.metadata.name]);
+ }, [ens, selectedAccount?.metadata.name]);
const onChangeName = (name: string) => {
setAccountName(name);
};
const saveAccountName = async () => {
- if (
- accountName &&
- accountName.length > 0 &&
- selectedInternalAccount?.address
- ) {
- Engine.setAccountLabel(selectedInternalAccount?.address, accountName);
+ if (accountName && accountName.length > 0 && selectedAccount?.address) {
+ Engine.setAccountLabel(selectedAccount?.address, accountName);
navigate('WalletView');
try {
const analyticsProperties = async () => {
- const accountType = getAddressAccountType(
- selectedInternalAccount?.address,
- );
+ const accountType = getAddressAccountType(selectedAccount?.address);
const account_type = accountType === 'QR' ? 'hardware' : accountType;
return { account_type, chain_id: getDecimalChainId(chainId) };
};
@@ -131,13 +141,10 @@ const EditAccountName = () => {
{strings('address_book.address')}
- {selectedInternalAccount?.address ? (
+ {selectedAccount?.address ? (
) : null}
diff --git a/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap b/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap
index 5de9ca67544..9e6a8d2bc18 100644
--- a/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap
+++ b/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap
@@ -87,7 +87,7 @@ exports[`EditAccountName should render correctly 1`] = `
}
}
testID="account-name-input"
- value="Account 1"
+ value="Test Account"
/>
diff --git a/app/components/Views/OnboardingSuccess/OnboardingGeneralSettings/__snapshots__/index.test.tsx.snap b/app/components/Views/OnboardingSuccess/OnboardingGeneralSettings/__snapshots__/index.test.tsx.snap
index 04b33ca2b84..c2d68ea9cf5 100644
--- a/app/components/Views/OnboardingSuccess/OnboardingGeneralSettings/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/OnboardingSuccess/OnboardingGeneralSettings/__snapshots__/index.test.tsx.snap
@@ -185,6 +185,7 @@ exports[`OnboardingGeneralSettings should render correctly 1`] = `
style={null}
>
;
+
interface IRevealPrivateCredentialProps {
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
navigation: any;
credentialName: string;
cancel: () => void;
- // TODO: Replace "any" with type
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- route: any;
+ route: RevealPrivateCredentialRouteProp;
}
const RevealPrivateCredential = ({
@@ -91,9 +104,10 @@ const RevealPrivateCredential = ({
const [clipboardEnabled, setClipboardEnabled] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
- const selectedAddress = useSelector(
+ const checkSummedAddress = useSelector(
selectSelectedInternalAccountChecksummedAddress,
);
+
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const passwordSet = useSelector((state: any) => state.user.passwordSet);
@@ -106,6 +120,8 @@ const RevealPrivateCredential = ({
const styles = createStyles(theme);
const credentialSlug = credentialName || route?.params.credentialName;
+ const selectedAddress =
+ route?.params?.selectedAccount?.address || checkSummedAddress;
const isPrivateKey = credentialSlug === PRIVATE_KEY;
const updateNavBar = () => {
diff --git a/app/components/Views/RevealPrivateCredential/__snapshots__/index.test.tsx.snap b/app/components/Views/RevealPrivateCredential/__snapshots__/index.test.tsx.snap
index 3ae0062fa7f..4a8e888f4a9 100644
--- a/app/components/Views/RevealPrivateCredential/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/RevealPrivateCredential/__snapshots__/index.test.tsx.snap
@@ -1266,3 +1266,376 @@ exports[`RevealPrivateCredential renders reveal private key correctly 1`] = `
`;
+
+exports[`RevealPrivateCredential renders with a custom selectedAddress 1`] = `
+
+
+
+
+
+
+ Save it somewhere safe and secret.
+
+
+
+
+
+
+ Never disclose this key. Anyone with your private key can fully control your account, including transferring away any of your funds.
+
+
+
+
+
+ Enter password to continue
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
+ Next
+
+
+
+
+
+
+
+
+
+`;
diff --git a/app/components/Views/RevealPrivateCredential/index.test.tsx b/app/components/Views/RevealPrivateCredential/index.test.tsx
index 9057bf751d9..6e9d90b7916 100644
--- a/app/components/Views/RevealPrivateCredential/index.test.tsx
+++ b/app/components/Views/RevealPrivateCredential/index.test.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
+import { InternalAccount } from '@metamask/keyring-api';
import { backgroundState } from '../../../util/test/initial-root-state';
import { RevealPrivateCredential } from './';
import { ThemeContext, mockTheme } from '../../../util/theme';
@@ -43,8 +44,10 @@ describe('RevealPrivateCredential', () => {
const { toJSON } = renderWithProviders(
{
const { toJSON } = renderWithProviders(
{
const { toJSON } = renderWithProviders(
{
const { getByPlaceholderText, getByTestId } = renderWithProviders(
{
const { getByPlaceholderText, getByTestId } = renderWithProviders(
{
).toBeTruthy();
});
});
+
+ it('renders with a custom selectedAddress', async () => {
+ const mockInternalAccount: InternalAccount = {
+ type: 'eip155:eoa',
+ id: 'unique-account-id-1',
+ address: '0x1234567890123456789012345678901234567890',
+ options: {
+ someOption: 'optionValue',
+ anotherOption: 42,
+ },
+ methods: [
+ 'personal_sign',
+ 'eth_sign',
+ 'eth_signTransaction',
+ 'eth_sendTransaction',
+ ],
+ metadata: {
+ name: 'Test Account',
+ importTime: Date.now(),
+ keyring: {
+ type: 'HD Key Tree',
+ },
+ nameLastUpdatedAt: Date.now(),
+ snap: {
+ id: 'npm:@metamask/test-snap',
+ name: 'Test Snap',
+ enabled: true,
+ },
+ lastSelected: Date.now(),
+ },
+ };
+
+ const { toJSON } = renderWithProviders(
+ null}
+ credentialName={PRIV_KEY_CREDENTIAL}
+ />,
+ );
+ expect(toJSON()).toMatchSnapshot();
+ });
});
diff --git a/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap b/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap
index 7822a45d4f8..9514928b1a6 100644
--- a/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap
@@ -54,7 +54,16 @@ exports[`Wallet should render correctly 1`] = `
collapsable={false}
style={
{
- "backgroundColor": "#ffffff",
+ "backgroundColor": {
+ "alternative": "#f2f4f6",
+ "alternativeHover": "#e7ebee",
+ "alternativePressed": "#dbe0e6",
+ "default": "#ffffff",
+ "defaultHover": "#f5f5f5",
+ "defaultPressed": "#ebebeb",
+ "hover": "#0000000a",
+ "pressed": "#00000014",
+ },
"borderBottomColor": "rgb(216, 216, 216)",
"elevation": 0,
"flex": 1,
@@ -115,42 +124,11 @@ exports[`Wallet should render correctly 1`] = `
"top": 0,
}
}
- >
-
-
-
@@ -170,9 +148,14 @@ exports[`Wallet should render correctly 1`] = `
testID="open-networks-button"
>
-
- Ethereum Mainnet
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Account 2
+
+
+
+
+
+
+
+ 0xC496...a756
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -294,9 +567,9 @@ exports[`Wallet should render correctly 1`] = `
testID="wallet-scan-button"
>
-
-
-
-
-
-
- An error occurred
-
-
- Your information can't be shown. Don’t worry, your wallet and funds are safe.
-
-
+ }
+ >
-
- View: Wallet
-TypeError: Cannot read properties of undefined (reading 'internalAccounts')
-
-
-
-
-
-
-
-
-
- Try again
-
-
-
-
-
-
- Please report this issue so we can fix it:
-
-
+ width={24}
+ />
+
-
-
-
-
- Take a screenshot of this screen.
-
-
-
-
-
-
-
- Copy
-
-
- the error message to clipboard.
+ Basic functionality is off
-
-
-
-
- Submit a ticket
-
-
- here.
-
-
- Please include the error message and the screenshot.
-
-
-
-
-
-
- Send us a bug report
-
-
- here.
-
-
- Please include details about what happened.
-
-
-
- If this error persists,
-
-
- save your Secret Recovery Phrase
+ Turn on basic functionality
-
- & re-install the app. Note: you can NOT restore your wallet without your Secret Recovery Phrase.
-
+
-
-
+
+
diff --git a/app/components/Views/Wallet/index.test.tsx b/app/components/Views/Wallet/index.test.tsx
index 87ef6fe0502..a7baa2653b3 100644
--- a/app/components/Views/Wallet/index.test.tsx
+++ b/app/components/Views/Wallet/index.test.tsx
@@ -5,59 +5,67 @@ import { act, screen } from '@testing-library/react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import Routes from '../../../constants/navigation/Routes';
import { backgroundState } from '../../../util/test/initial-root-state';
-import { createMockAccountsControllerState } from '../../../util/test/accountsControllerTestUtils';
+import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils';
import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors';
-import { CommonSelectorsIDs } from '../../../../e2e/selectors/Common.selectors';
import { useAccountSyncing } from '../../../util/notifications/hooks/useAccountSyncing';
import { AppState } from 'react-native';
const MOCK_ADDRESS = '0xc4955c0d639d99699bfd7ec54d9fafee40e4d272';
-const MOCK_ACCOUNTS_CONTROLLER_STATE = createMockAccountsControllerState([
- MOCK_ADDRESS,
-]);
-
-jest.mock('../../../core/Engine', () => ({
- getTotalFiatAccountBalance: jest.fn(),
- context: {
- NftController: {
- allNfts: {
- [MOCK_ADDRESS]: {
- [MOCK_ADDRESS]: [],
+jest.mock('../../../util/address', () => {
+ const actual = jest.requireActual('../../../util/address');
+ return {
+ ...actual,
+ getLabelTextByAddress: jest.fn(),
+ };
+});
+
+jest.mock('../../../core/Engine', () => {
+ const { MOCK_ACCOUNTS_CONTROLLER_STATE: mockAccountsControllerState } =
+ jest.requireActual('../../../util/test/accountsControllerTestUtils');
+ return {
+ getTotalFiatAccountBalance: jest.fn(),
+ context: {
+ NftController: {
+ allNfts: {
+ [MOCK_ADDRESS]: {
+ [MOCK_ADDRESS]: [],
+ },
},
+ allNftContracts: {
+ [MOCK_ADDRESS]: {
+ [MOCK_ADDRESS]: [],
+ },
+ },
+ },
+ TokenRatesController: {
+ poll: jest.fn(),
+ },
+ TokenDetectionController: {
+ detectTokens: jest.fn(),
},
- allNftContracts: {
- [MOCK_ADDRESS]: {
- [MOCK_ADDRESS]: [],
+ NftDetectionController: {
+ detectNfts: jest.fn(),
+ },
+ AccountTrackerController: {
+ refresh: jest.fn(),
+ },
+ KeyringController: {
+ state: {
+ keyrings: [
+ {
+ accounts: ['0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272'],
+ },
+ ],
},
},
- },
- TokenRatesController: {
- poll: jest.fn(),
- },
- TokenDetectionController: {
- detectTokens: jest.fn(),
- },
- NftDetectionController: {
- detectNfts: jest.fn(),
- },
- AccountTrackerController: {
- refresh: jest.fn(),
- },
- KeyringController: {
- state: {
- keyrings: [
- {
- accounts: ['0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272'],
- },
- ],
+ AccountsController: {
+ ...mockAccountsControllerState,
+ state: mockAccountsControllerState,
},
},
- AccountsController: {
- ...MOCK_ACCOUNTS_CONTROLLER_STATE,
- },
- },
-}));
+ };
+});
const mockInitialState = {
networkOnboarded: {
@@ -117,6 +125,21 @@ jest.mock('../../../util/notifications/hooks/useAccountSyncing', () => ({
}),
}));
+jest.mock('../../../util/address', () => ({
+ ...jest.requireActual('../../../util/address'),
+ getInternalAccountByAddress: jest.fn().mockReturnValue({
+ address: MOCK_ADDRESS,
+ balance: '0x0',
+ name: 'Account 1',
+ type: 'default',
+ metadata: {
+ keyring: {
+ type: 'HD Key Tree',
+ },
+ },
+ }),
+}));
+
const render = (Component: React.ComponentType) =>
renderScreen(
Component,
@@ -147,11 +170,21 @@ describe('Wallet', () => {
render(Wallet);
expect(ScrollableTabView).toHaveBeenCalled();
});
- it('should render fox icon', () => {
+ it('should render the address copy button', () => {
+ //@ts-expect-error we are ignoring the navigation params on purpose because we do not want to mock setOptions to test the navbar
+ render(Wallet);
+ const addressCopyButton = screen.getByTestId(
+ WalletViewSelectorsIDs.NAVBAR_ADDRESS_COPY_BUTTON,
+ );
+ expect(addressCopyButton).toBeDefined();
+ });
+ it('should render the account picker', () => {
//@ts-expect-error we are ignoring the navigation params on purpose because we do not want to mock setOptions to test the navbar
render(Wallet);
- const foxIcon = screen.getByTestId(CommonSelectorsIDs.FOX_ICON);
- expect(foxIcon).toBeDefined();
+ const accountPicker = screen.getByTestId(
+ WalletViewSelectorsIDs.ACCOUNT_ICON,
+ );
+ expect(accountPicker).toBeDefined();
});
it('dispatches account syncing on mount', () => {
jest.clearAllMocks();
diff --git a/app/components/Views/Wallet/index.tsx b/app/components/Views/Wallet/index.tsx
index 5f04c86d930..864beb3876d 100644
--- a/app/components/Views/Wallet/index.tsx
+++ b/app/components/Views/Wallet/index.tsx
@@ -35,6 +35,7 @@ import {
ToastContext,
ToastVariants,
} from '../../../component-library/components/Toast';
+import { AvatarAccountType } from '../../../component-library/components/Avatars/Avatar/variants/AvatarAccount';
import NotificationsService from '../../../util/notifications/services/NotificationService';
import Engine from '../../../core/Engine';
import CollectibleContracts from '../../UI/CollectibleContracts';
@@ -66,7 +67,6 @@ import {
ParamListBase,
useNavigation,
} from '@react-navigation/native';
-import { WalletAccount } from '../../../components/UI/WalletAccount';
import {
selectConversionRate,
selectCurrentCurrency,
@@ -94,6 +94,7 @@ import {
} from '../../../selectors/notifications';
import { ButtonVariants } from '../../../component-library/components/Buttons/Button';
import { useListNotifications } from '../../../util/notifications/hooks/useNotifications';
+import { useAccountName } from '../../hooks/useAccountName';
import { useAccountSyncing } from '../../../util/notifications/hooks/useAccountSyncing';
import { PortfolioBalance } from '../../UI/Tokens/TokenList/PortfolioBalance';
@@ -230,6 +231,14 @@ const Wallet = ({
const currentToast = toastRef?.current;
+ const accountName = useAccountName();
+
+ const accountAvatarType = useSelector((state: RootState) =>
+ state.settings.useBlockieIcon
+ ? AvatarAccountType.Blockies
+ : AvatarAccountType.JazzIcon,
+ );
+
useEffect(() => {
if (
isDataCollectionForMarketingEnabled === null &&
@@ -438,6 +447,10 @@ const Wallet = ({
useEffect(() => {
navigation.setOptions(
getWalletNavbarOptions(
+ walletRef,
+ selectedAddress || '',
+ accountName,
+ accountAvatarType,
networkName,
networkImageSource,
onTitlePress,
@@ -451,6 +464,9 @@ const Wallet = ({
);
/* eslint-disable-next-line */
}, [
+ selectedAddress,
+ accountName,
+ accountAvatarType,
navigation,
colors,
networkName,
@@ -560,9 +576,6 @@ const Wallet = ({
/>
) : null}
- {selectedAddress ? (
-
- ) : null}
<>
{accountBalanceByChainId && }
({
+ useSelector: jest.fn(),
+}));
+jest.mock('../../components/hooks/useEnsNameByAddress', () => jest.fn());
+jest.mock('../../util/ENSUtils', () => ({
+ isDefaultAccountName: jest.fn(),
+}));
+
+describe('useAccountName', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return the ENS name when default name is a default account name', () => {
+ (useSelector as jest.Mock).mockReturnValue({
+ metadata: { name: 'Account 1' },
+ address: '0x1234567890123456789012345678901234567890',
+ });
+ (useEnsNameByAddress as jest.Mock).mockReturnValue({ ensName: 'test.eth' });
+ (isDefaultAccountName as jest.Mock).mockReturnValue(true);
+
+ const { result } = renderHook(() => useAccountName());
+ expect(result.current).toBe('test.eth');
+ });
+
+ it('should return the default name when it is not a default account name', () => {
+ (useSelector as jest.Mock).mockReturnValue({
+ metadata: { name: 'My Custom Account' },
+ address: '0x1234567890123456789012345678901234567890',
+ });
+ (useEnsNameByAddress as jest.Mock).mockReturnValue({ ensName: 'test.eth' });
+ (isDefaultAccountName as jest.Mock).mockReturnValue(false);
+
+ const { result } = renderHook(() => useAccountName());
+ expect(result.current).toBe('My Custom Account');
+ });
+
+ it('should return an empty string when both default name and ENS name are undefined', () => {
+ (useSelector as jest.Mock).mockReturnValue({
+ metadata: { name: undefined },
+ address: '0x1234567890123456789012345678901234567890',
+ });
+ (useEnsNameByAddress as jest.Mock).mockReturnValue({ ensName: undefined });
+ (isDefaultAccountName as jest.Mock).mockReturnValue(false);
+
+ const { result } = renderHook(() => useAccountName());
+ expect(result.current).toBe('');
+ });
+
+ it('should return an empty string when default name is undefined and ENS name is available', () => {
+ (useSelector as jest.Mock).mockReturnValue({
+ metadata: { name: undefined },
+ address: '0x1234567890123456789012345678901234567890',
+ });
+ (useEnsNameByAddress as jest.Mock).mockReturnValue({ ensName: 'test.eth' });
+ (isDefaultAccountName as jest.Mock).mockReturnValue(false);
+
+ const { result } = renderHook(() => useAccountName());
+ expect(result.current).toBe('');
+ });
+
+ it('should return the ENS name when default name is a default account name and ENS name is available', () => {
+ (useSelector as jest.Mock).mockReturnValue({
+ metadata: { name: 'Account 1' },
+ address: '0x1234567890123456789012345678901234567890',
+ });
+ (useEnsNameByAddress as jest.Mock).mockReturnValue({ ensName: 'test.eth' });
+ (isDefaultAccountName as jest.Mock).mockReturnValue(true);
+
+ const { result } = renderHook(() => useAccountName());
+ expect(result.current).toBe('test.eth');
+ });
+});
diff --git a/app/components/hooks/useAccountName.ts b/app/components/hooks/useAccountName.ts
new file mode 100644
index 00000000000..72ec9e8dd53
--- /dev/null
+++ b/app/components/hooks/useAccountName.ts
@@ -0,0 +1,18 @@
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import { selectSelectedInternalAccount } from '../../selectors/accountsController';
+import useEnsNameByAddress from '../../components/hooks/useEnsNameByAddress';
+import { isDefaultAccountName } from '../../util/ENSUtils';
+
+export const useAccountName = () => {
+ const selectedAccount = useSelector(selectSelectedInternalAccount);
+ const { ensName } = useEnsNameByAddress(selectedAccount?.address);
+ const defaultName = selectedAccount?.metadata?.name;
+
+ return useMemo(
+ () =>
+ (isDefaultAccountName(defaultName) && ensName ? ensName : defaultName) ||
+ '',
+ [defaultName, ensName],
+ );
+};
diff --git a/e2e/pages/AccountListView.js b/e2e/pages/AccountListView.js
index 9bcc8daa11a..4665817b0b4 100644
--- a/e2e/pages/AccountListView.js
+++ b/e2e/pages/AccountListView.js
@@ -3,6 +3,7 @@ import {
AccountListViewSelectorsIDs,
AccountListViewSelectorsText,
} from '../selectors/AccountListView.selectors';
+import { WalletViewSelectorsIDs } from '../selectors/wallet/WalletView.selectors';
import { ConnectAccountBottomSheetSelectorsIDs } from '../selectors/Browser/ConnectAccountBottomSheet.selectors';
import Matchers from '../utils/Matchers';
import Gestures from '../utils/Gestures';
@@ -19,6 +20,10 @@ class AccountListView {
);
}
+ get accountTagLabel() {
+ return Matchers.getElementByID(CellModalSelectorsIDs.TAG_LABEL);
+ }
+
get title() {
return Matchers.getElementByText(
AccountListViewSelectorsText.ACCOUNTS_LIST_TITLE,
@@ -57,10 +62,25 @@ class AccountListView {
getSelectElement(index) {
return Matchers.getElementByID(CellModalSelectorsIDs.SELECT, index);
}
+
getMultiselectElement(index) {
return Matchers.getElementByID(CellModalSelectorsIDs.MULTISELECT, index);
}
+ getSelectWithMenuElement(index) {
+ return Matchers.getElementByID(
+ CellModalSelectorsIDs.SELECT_WITH_MENU,
+ index,
+ );
+ }
+
+ async tapEditAccountActionsAtIndex(index) {
+ const accountActionsButton = Matchers.getElementByID(
+ `${WalletViewSelectorsIDs.ACCOUNT_ACTIONS}-${index}`,
+ );
+ await Gestures.waitAndTap(accountActionsButton);
+ }
+
async accountNameInList(accountName) {
return Matchers.getElementByText(accountName, 1);
}
@@ -69,7 +89,11 @@ class AccountListView {
}
async tapToSelectActiveAccountAtIndex(index) {
- await Gestures.tap(this.getSelectElement(index));
+ await Gestures.tap(this.getSelectWithMenuElement(index));
+ }
+
+ async longPressAccountAtIndex(index) {
+ await Gestures.tapAndLongPress(this.getSelectWithMenuElement(index));
}
async tapAddAccountButton() {
diff --git a/e2e/pages/wallet/WalletView.js b/e2e/pages/wallet/WalletView.js
index 7443eaf2c67..1f054e25ec2 100644
--- a/e2e/pages/wallet/WalletView.js
+++ b/e2e/pages/wallet/WalletView.js
@@ -2,7 +2,6 @@ import {
WalletViewSelectorsIDs,
WalletViewSelectorsText,
} from '../../selectors/wallet/WalletView.selectors';
-import { CommonSelectorsText } from '../../selectors/Common.selectors';
import Gestures from '../../utils/Gestures';
import Matchers from '../../utils/Matchers';
@@ -35,6 +34,12 @@ class WalletView {
);
}
+ async getNavbarNetworkPicker() {
+ return Matchers.getElementByID(
+ WalletViewSelectorsIDs.NAVBAR_NETWORK_PICKER,
+ );
+ }
+
get nftTab() {
return Matchers.getElementByText(WalletViewSelectorsText.NFTS_TAB);
}
@@ -75,12 +80,14 @@ class WalletView {
return Matchers.getElementByText(WalletViewSelectorsText.HIDE_TOKENS);
}
- get mainWalletAccountActions() {
- return Matchers.getElementByID(WalletViewSelectorsIDs.ACCOUNT_ACTIONS);
+ get currentMainWalletAccountActions() {
+ return Matchers.getElementByID(
+ WalletViewSelectorsIDs.ACCOUNT_NAME_LABEL_TEXT,
+ );
}
- async tapMainWalletAccountActions() {
- await Gestures.waitAndTap(this.mainWalletAccountActions);
+ async tapCurrentMainWalletAccountActions() {
+ await Gestures.waitAndTap(this.currentMainWalletAccountActions);
}
async tapOnToken(token) {
diff --git a/e2e/selectors/wallet/WalletView.selectors.js b/e2e/selectors/wallet/WalletView.selectors.js
index 265a13cd0b1..5b02e02cf55 100644
--- a/e2e/selectors/wallet/WalletView.selectors.js
+++ b/e2e/selectors/wallet/WalletView.selectors.js
@@ -23,13 +23,14 @@ export const WalletViewSelectorsIDs = {
ACCOUNT_OVERVIEW: 'account-overview',
ACCOUNT_ACTIONS: 'main-wallet-account-actions',
ACCOUNT_COPY_BUTTON: 'wallet-account-copy-button',
- ACCOUNT_ADDRESS: 'wallet-account-address',
TEST_COLLECTIBLE: 'collectible-Test Dapp NFTs #1-1',
COLLECTIBLE_FALLBACK: 'fallback-nft-with-token-id',
+ NAVBAR_ADDRESS_COPY_BUTTON: 'navbar-address-copy-button',
IMPORT_TOKEN_FOOTER_LINK: 'import-token-footer-link',
SORT_DECLINING_BALANCE: 'sort-declining-balance',
SORT_ALPHABETICAL: 'sort-alphabetical',
SORT_BY: 'sort-by',
+ NAVBAR_NETWORK_PICKER: 'network-avatar-picker',
};
export const WalletViewSelectorsText = {
diff --git a/e2e/specs/accounts/change-account-name.spec.js b/e2e/specs/accounts/change-account-name.spec.js
index f20c6ee3356..229c201d8cc 100644
--- a/e2e/specs/accounts/change-account-name.spec.js
+++ b/e2e/specs/accounts/change-account-name.spec.js
@@ -20,14 +20,21 @@ import Assertions from '../../utils/Assertions';
import TabBarComponent from '../../pages/TabBarComponent';
import SettingsView from '../../pages/Settings/SettingsView';
import LoginView from '../../pages/LoginView';
+import AccountListView from '../../pages/AccountListView';
const fixtureServer = new FixtureServer();
const NEW_ACCOUNT_NAME = 'Edited Name';
+const NEW_IMPORTED_ACCOUNT_NAME = 'New Imported Account';
+const MAIN_ACCOUNT_INDEX = 0;
+const IMPORTED_ACCOUNT_INDEX = 1;
describe(Regression('Change Account Name'), () => {
beforeAll(async () => {
await TestHelpers.reverseServerPort();
- const fixture = new FixtureBuilder().withGanacheNetwork().build();
+ const fixture = new FixtureBuilder()
+ .withGanacheNetwork()
+ .withImportedAccountKeyringController()
+ .build();
await startFixtureServer(fixtureServer);
await loadFixture(fixtureServer, { fixture });
await device.launchApp({
@@ -43,7 +50,9 @@ describe(Regression('Change Account Name'), () => {
it('renames an account and verifies the new name persists after locking and unlocking the wallet', async () => {
// Open account actions and edit account name
- await WalletView.tapMainWalletAccountActions();
+ await TabBarComponent.tapWallet();
+ await WalletView.tapIdenticon();
+ await AccountListView.tapEditAccountActionsAtIndex(MAIN_ACCOUNT_INDEX);
await AccountActionsModal.tapEditAccount();
await Gestures.clearField(EditAccountNameView.accountNameInput);
await TestHelpers.typeTextAndHideKeyboard(
@@ -73,4 +82,44 @@ describe(Regression('Change Account Name'), () => {
NEW_ACCOUNT_NAME,
);
});
+
+ it('import an account, edits the name, and verifies the new name persists after locking and unlocking the wallet', async () => {
+ // Open account actions bottom sheet and choose imported account
+ await WalletView.tapIdenticon();
+ await AccountListView.tapToSelectActiveAccountAtIndex(
+ IMPORTED_ACCOUNT_INDEX,
+ );
+
+ // Edit imported account name
+ await WalletView.tapIdenticon();
+ await AccountListView.tapEditAccountActionsAtIndex(IMPORTED_ACCOUNT_INDEX);
+ await AccountActionsModal.tapEditAccount();
+ await Gestures.clearField(EditAccountNameView.accountNameInput);
+ await TestHelpers.typeTextAndHideKeyboard(
+ EditAccountNameSelectorIDs.ACCOUNT_NAME_INPUT,
+ NEW_IMPORTED_ACCOUNT_NAME,
+ );
+ await EditAccountNameView.tapSave();
+
+ // Verify updated name
+ await Assertions.checkIfElementToHaveText(
+ WalletView.accountName,
+ NEW_IMPORTED_ACCOUNT_NAME,
+ );
+
+ // Lock wallet
+ await Assertions.checkIfVisible(TabBarComponent.tabBarSettingButton);
+ await TabBarComponent.tapSettings();
+ await SettingsView.scrollToLockButton();
+ await SettingsView.tapLock();
+ await SettingsView.tapYesAlertButton();
+ await Assertions.checkIfVisible(LoginView.container);
+
+ // Unlock wallet and verify updated name persists
+ await loginToApp();
+ await Assertions.checkIfElementToHaveText(
+ WalletView.accountName,
+ NEW_IMPORTED_ACCOUNT_NAME,
+ );
+ });
});
diff --git a/e2e/specs/accounts/imported-account-remove-and-import.spec.js b/e2e/specs/accounts/imported-account-remove-and-import.spec.js
index 84847df1141..6dccfbf6032 100644
--- a/e2e/specs/accounts/imported-account-remove-and-import.spec.js
+++ b/e2e/specs/accounts/imported-account-remove-and-import.spec.js
@@ -24,6 +24,7 @@ const fixtureServer = new FixtureServer();
// It should NEVER hold any eth or token
const TEST_PRIVATE_KEY =
'cbfd798afcfd1fd8ecc48cbecb6dc7e876543395640b758a90e11d986e758ad1';
+const ACCOUNT_INDEX = 1;
describe(
Regression('removes and reimports an account using a private key'),
@@ -50,7 +51,7 @@ describe(
await WalletView.tapIdenticon();
// Remove the imported account
- await AccountListView.longPressImportedAccount();
+ await AccountListView.longPressAccountAtIndex(ACCOUNT_INDEX);
await AccountListView.tapYesToRemoveImportedAccountAlertButton();
await Assertions.checkIfNotVisible(AccountListView.accountTypeLabel);
@@ -61,8 +62,14 @@ describe(
await ImportAccountView.enterPrivateKey(TEST_PRIVATE_KEY);
await Assertions.checkIfVisible(SuccessImportAccountView.container);
await SuccessImportAccountView.tapCloseButton();
- await Assertions.checkIfElementToHaveText(
- AccountListView.accountTypeLabel,
+
+ const tagElement = await AccountListView.accountTagLabel;
+ const tagElementAttribute = await tagElement.getAttributes();
+ const tagLabel = tagElementAttribute.label;
+
+ // Check if the account type label is visible
+ await Assertions.checkIfTextMatches(
+ tagLabel,
AccountListViewSelectorsText.ACCOUNT_TYPE_LABEL_TEXT,
);
});
diff --git a/e2e/specs/accounts/reveal-private-key.spec.js b/e2e/specs/accounts/reveal-private-key.spec.js
index 42e1dfe1c6a..790f1b9098b 100644
--- a/e2e/specs/accounts/reveal-private-key.spec.js
+++ b/e2e/specs/accounts/reveal-private-key.spec.js
@@ -27,7 +27,8 @@ const HD_ACCOUNT_1_PRIVATE_KEY =
'242251a690016cfcf8af43fb1ad7ff4c66c269bbca03f9f076ee8db93c191594';
const IMPORTED_ACCOUNT_2_PRIVATE_KEY =
'cbfd798afcfd1fd8ecc48cbecb6dc7e876543395640b758a90e11d986e758ad1';
-const IMPORTED_ACCOUNT_2_INDEX = 1;
+const IMPORTED_ACCOUNT_0_INDEX = 0;
+const IMPORTED_ACCOUNT_1_INDEX = 1;
describe(Regression('reveal private key'), () => {
const PASSWORD = '123123123';
@@ -85,15 +86,43 @@ describe(Regression('reveal private key'), () => {
);
});
- it('reveals the correct private key for an imported account from the account menu ', async () => {
+ it('reveals the correct private key for the first account in the account list ', async () => {
await TabBarComponent.tapWallet();
await WalletView.tapIdenticon();
- await AccountListView.tapToSelectActiveAccountAtIndex(
- IMPORTED_ACCOUNT_2_INDEX,
+ await AccountListView.tapEditAccountActionsAtIndex(
+ IMPORTED_ACCOUNT_0_INDEX,
);
+
+ await AccountActionsModal.tapShowPrivateKey();
+ await RevealPrivateKey.enterPasswordToRevealSecretCredential(PASSWORD);
+ await RevealPrivateKey.tapToReveal();
+ await Assertions.checkIfVisible(RevealPrivateKey.container);
+ await Assertions.checkIfTextIsDisplayed(
+ RevealSeedViewSelectorsText.REVEAL_CREDENTIAL_PRIVATE_KEY_TITLE_TEXT,
+ );
+ await Assertions.checkIfTextIsDisplayed(HD_ACCOUNT_1_PRIVATE_KEY);
+
+ // Copy to clipboard
+ // Android devices running OS version < 11 (API level 29) will not see the copy to clipboard button presented
+ // This will cause the following step to fail if e2e were being run on an older android OS prior to our minimum API level 29
+ // See details here: https://github.com/MetaMask/metamask-mobile/pull/4170
+ await RevealPrivateKey.tapToCopyCredentialToClipboard();
+ await RevealPrivateKey.tapToRevealPrivateCredentialQRCode();
+ await Assertions.checkIfVisible(
+ RevealPrivateKey.revealCredentialQRCodeImage,
+ );
+ await RevealPrivateKey.scrollToDone();
+ await RevealPrivateKey.tapDoneButton();
await Assertions.checkIfVisible(WalletView.container);
+ });
+
+ it('reveals the correct private key for the second account in the account list which is also an imported account', async () => {
+ await TabBarComponent.tapWallet();
+ await WalletView.tapIdenticon();
+ await AccountListView.tapEditAccountActionsAtIndex(
+ IMPORTED_ACCOUNT_1_INDEX,
+ );
- await WalletView.tapMainWalletAccountActions();
await AccountActionsModal.tapShowPrivateKey();
await RevealPrivateKey.enterPasswordToRevealSecretCredential(PASSWORD);
await RevealPrivateKey.tapToReveal();
diff --git a/e2e/specs/networks/add-custom-rpc.spec.js b/e2e/specs/networks/add-custom-rpc.spec.js
index a780f52d3ae..f9cec1f88ac 100644
--- a/e2e/specs/networks/add-custom-rpc.spec.js
+++ b/e2e/specs/networks/add-custom-rpc.spec.js
@@ -102,8 +102,9 @@ describe(Regression('Custom RPC Tests'), () => {
await NetworkEducationModal.tapGotItButton();
await Assertions.checkIfNotVisible(NetworkEducationModal.container);
await Assertions.checkIfVisible(WalletView.container);
- await Assertions.checkIfElementToHaveText(
- WalletView.navbarNetworkText,
+ const networkPicker = await WalletView.getNavbarNetworkPicker();
+ await Assertions.checkIfElementHasLabel(
+ networkPicker,
CustomNetworks.Gnosis.providerConfig.nickname,
);
});
@@ -113,8 +114,9 @@ describe(Regression('Custom RPC Tests'), () => {
await WalletView.tapNetworksButtonOnNavBar();
await Assertions.checkIfVisible(NetworkListModal.networkScroll);
- await Assertions.checkIfElementToHaveText(
- WalletView.navbarNetworkText,
+ const networkPicker = await WalletView.getNavbarNetworkPicker();
+ await Assertions.checkIfElementHasLabel(
+ networkPicker,
CustomNetworks.Gnosis.providerConfig.nickname,
);
});
@@ -126,22 +128,28 @@ describe(Regression('Custom RPC Tests'), () => {
CustomNetworks.Sepolia.providerConfig.nickname,
);
await Assertions.checkIfVisible(NetworkEducationModal.container);
- await Assertions.checkIfElementToHaveText(
- NetworkEducationModal.networkName,
- CustomNetworks.Sepolia.providerConfig.nickname,
- );
+
await NetworkEducationModal.tapGotItButton();
await Assertions.checkIfNotVisible(NetworkEducationModal.container);
await Assertions.checkIfVisible(WalletView.container);
+ const networkPicker = await WalletView.getNavbarNetworkPicker();
+
+ await Assertions.checkIfElementHasLabel(
+ networkPicker,
+ CustomNetworks.Sepolia.providerConfig.nickname,
+ );
});
it('should switch back to Gnosis', async () => {
await WalletView.tapNetworksButtonOnNavBar();
await NetworkListModal.scrollToBottomOfNetworkList();
- await Assertions.checkIfElementToHaveText(
- WalletView.navbarNetworkText,
+
+ const networkPicker = await WalletView.getNavbarNetworkPicker();
+ await Assertions.checkIfElementHasLabel(
+ networkPicker,
CustomNetworks.Sepolia.providerConfig.nickname,
);
+
await Assertions.checkIfVisible(NetworkListModal.networkScroll);
await NetworkListModal.scrollToTopOfNetworkList();
// Change to back to Gnosis Network
@@ -149,8 +157,8 @@ describe(Regression('Custom RPC Tests'), () => {
CustomNetworks.Gnosis.providerConfig.nickname,
);
await Assertions.checkIfVisible(WalletView.container);
- await Assertions.checkIfElementToHaveText(
- WalletView.navbarNetworkText,
+ await Assertions.checkIfElementHasLabel(
+ networkPicker,
CustomNetworks.Gnosis.providerConfig.nickname,
);
await Assertions.checkIfNotVisible(NetworkEducationModal.container);
diff --git a/e2e/specs/networks/connect-test-network.spec.js b/e2e/specs/networks/connect-test-network.spec.js
index 88710a09a92..71680d2cbb8 100644
--- a/e2e/specs/networks/connect-test-network.spec.js
+++ b/e2e/specs/networks/connect-test-network.spec.js
@@ -55,8 +55,10 @@ describe(Regression('Connect to a Test Network'), () => {
await NetworkEducationModal.tapGotItButton();
await Assertions.checkIfNotVisible(NetworkEducationModal.container);
await Assertions.checkIfVisible(WalletView.container);
- await Assertions.checkIfElementToHaveText(
- WalletView.navbarNetworkText,
+
+ const networkPicker = await WalletView.getNavbarNetworkPicker();
+ await Assertions.checkIfElementHasLabel(
+ networkPicker,
CustomNetworks.Sepolia.providerConfig.nickname,
);
});
@@ -80,10 +82,9 @@ describe(Regression('Connect to a Test Network'), () => {
await NetworkEducationModal.tapGotItButton();
await Assertions.checkIfNotVisible(NetworkEducationModal.container);
await Assertions.checkIfVisible(WalletView.container);
- await Assertions.checkIfElementToHaveText(
- WalletView.navbarNetworkText,
- ETHEREUM,
- );
+
+ const networkPicker = await WalletView.getNavbarNetworkPicker();
+ await Assertions.checkIfElementHasLabel(networkPicker, ETHEREUM);
});
it('should toggle off the Test Network switch', async () => {
diff --git a/e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js b/e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js
index 5d2f3cb2b16..30cadefba5e 100644
--- a/e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js
+++ b/e2e/specs/notifications/account-syncing/sync-after-adding-custom-name-account.spec.js
@@ -63,6 +63,7 @@ describe(SmokeNotifications('Account syncing'), () => {
);
await WalletView.tapIdenticon();
+
await Assertions.checkIfVisible(AccountListView.accountList);
for (const accountName of decryptedAccountNames) {
@@ -74,8 +75,10 @@ describe(SmokeNotifications('Account syncing'), () => {
await AccountListView.tapAddAccountButton();
await AddAccountModal.tapCreateAccount();
await AccountListView.swipeToDismissAccountsModal();
+ await TestHelpers.delay(2000);
+ await WalletView.tapCurrentMainWalletAccountActions();
- await WalletView.tapMainWalletAccountActions();
+ await AccountListView.tapEditAccountActionsAtIndex(2);
await AccountActionsModal.renameActiveAccount(NEW_ACCOUNT_NAME);
await Assertions.checkIfElementToHaveText(
diff --git a/e2e/utils/Assertions.js b/e2e/utils/Assertions.js
index e3b15781d21..ea2935fad2c 100644
--- a/e2e/utils/Assertions.js
+++ b/e2e/utils/Assertions.js
@@ -129,6 +129,27 @@ class Assertions {
static async checkIfToggleIsOff(elementID) {
return expect(await elementID).toHaveToggleValue(false);
}
+
+ /**
+ * Check if two text values match exactly.
+ * @param {string} actualText - The actual text value to check.
+ * @param {string} expectedText - The expected text value to match against.
+ */
+ static async checkIfTextMatches(actualText, expectedText) {
+ try {
+ if (!actualText || !expectedText) {
+ throw new Error('Both actual and expected text must be provided');
+ }
+
+ return expect(actualText).toBe(expectedText);
+ } catch (error) {
+ if (actualText !== expectedText) {
+ throw new Error(
+ `Text matching failed.\nExpected: "${expectedText}"\nActual: "${actualText}"`,
+ );
+ }
+ }
+ }
}
export default Assertions;