diff --git a/.github/workflows/bump-version-name.yml b/.github/workflows/bump-version-name.yml
index 9e1de2231b3..96b899d71b4 100644
--- a/.github/workflows/bump-version-name.yml
+++ b/.github/workflows/bump-version-name.yml
@@ -7,7 +7,7 @@ on:
types: [opened]
jobs:
bump-version-name:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
if: "contains(github.head_ref, 'release/')"
permissions:
contents: write
diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml
index 93758bae919..0f7977000c6 100644
--- a/.github/workflows/cla.yml
+++ b/.github/workflows/cla.yml
@@ -8,7 +8,7 @@ on:
jobs:
CLABot:
if: github.event_name == 'pull_request_target' || contains(github.event.comment.html_url, '/pull/')
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml
index bb7c5767b5b..28b9070eccc 100644
--- a/.github/workflows/create-release-pr.yml
+++ b/.github/workflows/create-release-pr.yml
@@ -15,7 +15,7 @@ on:
jobs:
create-release-pr:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
diff --git a/.github/workflows/crowdin_action.yml b/.github/workflows/crowdin_action.yml
index 24cf9152bd6..119b13af130 100644
--- a/.github/workflows/crowdin_action.yml
+++ b/.github/workflows/crowdin_action.yml
@@ -13,7 +13,7 @@ on:
jobs:
synchronize-with-crowdin:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 639d6fb58a2..3ab51e57c83 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,33 @@
## Current Main Branch
+## 6.5.0 - May 4, 2023
+- [#5743](https://github.com/MetaMask/metamask-mobile/pull/5743): [FEATURE] On-ramp: Add buy-crypto deeplink
+- [#6201](https://github.com/MetaMask/metamask-mobile/pull/6201): [FIX] [SDK] Missing redirect breaking backward compatibility
+- [#6232](https://github.com/MetaMask/metamask-mobile/pull/6232): [FIX] bottom margin for detecting end of the page
+- [#6166](https://github.com/MetaMask/metamask-mobile/pull/6166): [FEATURE] trigger walletconnect modal using approval controller
+- [#6223](https://github.com/MetaMask/metamask-mobile/pull/6223): [IMPROVEMENT] Update to Node.js v16
+- [#6051](https://github.com/MetaMask/metamask-mobile/pull/6051): [FEATURE] Total balance and portfolio button changed
+- [#6156](https://github.com/MetaMask/metamask-mobile/pull/6156): [IMPROVEMENT] On-ramp: Use dynamic list of networks
+- [#6145](https://github.com/MetaMask/metamask-mobile/pull/6145): [IMPROVEMENT] Synced and optimized icons
+- [#6138](https://github.com/MetaMask/metamask-mobile/pull/6138): [FEATURE] On-ramp: Add orderProcessor exponential backoff for orders
+- [#6139](https://github.com/MetaMask/metamask-mobile/pull/6139): [FEATURE] On-ramp: Add same amount rendering as the order details to the order list
+- [#6189](https://github.com/MetaMask/metamask-mobile/pull/6189): [FEATURE] On-ramp: Remove hiding the provider modal when quotes refresh
+- [#6216](https://github.com/MetaMask/metamask-mobile/pull/6216): [IMPROVEMENT] account icon matches user's preferred identicon
+- [#5956](https://github.com/MetaMask/metamask-mobile/pull/5956): [IMPROVEMENT] Show token symbol in verify contract details
+- [#5458](https://github.com/MetaMask/metamask-mobile/pull/5458): [IMPROVEMENT] Support sepolia network
+- [#6185](https://github.com/MetaMask/metamask-mobile/pull/6185): [FIX] remove pubnub package and associated sync with extension code
+- [#6181](https://github.com/MetaMask/metamask-mobile/pull/6181): [IMPROVEMENT] Componentize Header Component
+- [#6153](https://github.com/MetaMask/metamask-mobile/pull/6153): [IMPROVEMENT] On-ramp: Refactor order selector by id
+- [#6044](https://github.com/MetaMask/metamask-mobile/pull/6044): [IMPROVEMENT] Componentize Badge and Badge Wrapper
+- [#6180](https://github.com/MetaMask/metamask-mobile/pull/6180): [IMPROVEMENT] Componentized Overlay Component
+- [#6173](https://github.com/MetaMask/metamask-mobile/pull/6173): [REFACTOR] Auto Lock section
+- [#6174](https://github.com/MetaMask/metamask-mobile/pull/6174): [IMPROVEMENT] Update Tab bar styles
+- [#6056](https://github.com/MetaMask/metamask-mobile/pull/6056): [IMPROVEMENT] Show Identicon for unknown token and if token icon is unknown
+- [#6076](https://github.com/MetaMask/metamask-mobile/pull/6076): [BUGFIX] Fixes WalletConnect deep links (wc:// schema) not working properly
+- [#6157](https://github.com/MetaMask/metamask-mobile/pull/6157): [REFACTOR] Change Password setting
+- [#5718](https://github.com/MetaMask/metamask-mobile/pull/5718): [FIX] Nonce Too Low for Approve Transaction
+
## 6.4.0 - Apr 20, 2023
- [#6144](https://github.com/MetaMask/metamask-mobile/pull/6144): [FEATURE] New Crowdin translations by Github Action
- [#6143](https://github.com/MetaMask/metamask-mobile/pull/6143): [UPDATE] Crowdin token to use METAMASKBOT_CROWDIN_TOKEN
diff --git a/README.md b/README.md
index 389292fe0e8..b09b8937a92 100644
--- a/README.md
+++ b/README.md
@@ -217,7 +217,7 @@ For local testing, the wallet is created using the secret recovery phrase from t
##### iOS
All tests live within the e2e/specs folder.
-Prequisites for running tests:
+Prerequisites for running tests:
- Make sure to install `detox-cli` by referring to the instructions mentioned [here](https://wix.github.io/Detox/docs/introduction/getting-started/#detox-prerequisites).
- Additionally, install `applesimutils` by following the guidelines provided [here](https://github.com/wix/AppleSimulatorUtils).
- Before running any tests, it's recommended to refer to the `iOS section` above and check the latest simulator device specified under `Install the correct simulator`.
@@ -246,7 +246,12 @@ If you have already built the application for Detox and want to run a specific t
```bash
yarn test:e2e:ios:debug:single e2e/specs/TEST_NAME.spec.js
```
+To run tests associated with a certain tag, you can do so using the `--testNamePattern` flag. For example:
+```bash
+yarn test:e2e:ios:debug --testNamePattern="Smoke"
+```
+This runs all tests that are tagged "Smoke"
##### Android
All android tests live within the wdio/feature folder.
@@ -286,7 +291,7 @@ To get a better understanding of the internal architecture of this app take a lo
### Storybook
-We have begun documenting our components using storybook please read the [Documentation Guidelines](./storybook/DOCUMENTATION_GUIDELINES.md) to get up and running.
+We have begun documenting our components using Storybook. Please read the [Documentation Guidelines](./storybook/DOCUMENTATION_GUIDELINES.md) to get up and running.
### Other Docs
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 0fb4fbb2e70..7fcedf5fe91 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -98,7 +98,7 @@ def enableSeparateBuildPerCPUArchitecture = false
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
-def enableProguardInReleaseBuilds = false
+def enableProguardInReleaseBuilds = true
/**
* The preferred build flavor of JavaScriptCore (JSC)
@@ -123,6 +123,14 @@ def reactNativeArchitectures() {
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
+/**
+* Adding function that will retuen the Bitrise ndkPath if it is a QA or Production Build
+*/
+def ndkPath() {
+ return System.getenv('METAMASK_ENVIRONMENT') == 'qa' || System.getenv('METAMASK_ENVIRONMENT') == 'production' ? rootProject.ext.bitriseNdkPath : ""
+}
+
+
android {
ndkVersion rootProject.ext.ndkVersion
@@ -148,24 +156,24 @@ android {
}
signingConfigs {
- release {
- storeFile file('../keystores/release.keystore')
- storePassword System.getenv("BITRISEIO_ANDROID_KEYSTORE_PASSWORD")
- keyAlias System.getenv("BITRISEIO_ANDROID_KEYSTORE_ALIAS")
- keyPassword System.getenv("BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD")
- }
- qa {
- storeFile file('../keystores/internalRelease.keystore')
- storePassword System.getenv("BITRISEIO_ANDROID_QA_KEYSTORE_PASSWORD")
- keyAlias System.getenv("BITRISEIO_ANDROID_QA_KEYSTORE_ALIAS")
- keyPassword System.getenv("BITRISEIO_ANDROID_QA_KEYSTORE_PRIVATE_KEY_PASSWORD")
- }
- debug {
- storeFile file('debug.keystore')
- storePassword 'android'
- keyAlias 'androiddebugkey'
- keyPassword 'android'
- }
+ release {
+ storeFile file('../keystores/release.keystore')
+ storePassword System.getenv("BITRISEIO_ANDROID_KEYSTORE_PASSWORD")
+ keyAlias System.getenv("BITRISEIO_ANDROID_KEYSTORE_ALIAS")
+ keyPassword System.getenv("BITRISEIO_ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD")
+ }
+ qa {
+ storeFile file('../keystores/internalRelease.keystore')
+ storePassword System.getenv("BITRISEIO_ANDROID_QA_KEYSTORE_PASSWORD")
+ keyAlias System.getenv("BITRISEIO_ANDROID_QA_KEYSTORE_ALIAS")
+ keyPassword System.getenv("BITRISEIO_ANDROID_QA_KEYSTORE_PRIVATE_KEY_PASSWORD")
+ }
+ debug {
+ storeFile file('debug.keystore')
+ storePassword 'android'
+ keyAlias 'androiddebugkey'
+ keyPassword 'android'
+ }
}
splits {
@@ -184,9 +192,6 @@ android {
}
release {
manifestPlaceholders.isDebug = false
- // Caution! In production, you need to generate your own keystore file.
- // see https://reactnative.dev/docs/signed-apk-android.
- signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
diff --git a/android/build.gradle b/android/build.gradle
index 4c94dcd1772..e9d938304cd 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -8,7 +8,8 @@ buildscript {
compileSdkVersion = 33
targetSdkVersion = 33
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
- ndkVersion = "23.1.7779620"
+ ndkVersion = "24.0.8215888"
+ bitriseNdkPath = "/usr/local/share/android-sdk/ndk-bundle"
kotlin_version = "1.7.0"
kotlinVersion = "$kotlin_version"
supportLibVersion = "28.0.0"
diff --git a/app/component-library/components/Modals/ModalMandatory/ModalMandatory.tsx b/app/component-library/components/Modals/ModalMandatory/ModalMandatory.tsx
index e3db0bb24b6..9751ea6f02b 100644
--- a/app/component-library/components/Modals/ModalMandatory/ModalMandatory.tsx
+++ b/app/component-library/components/Modals/ModalMandatory/ModalMandatory.tsx
@@ -234,7 +234,7 @@ const ModalMandatory = ({ route }: MandatoryModalProps) => {
activeOpacity={1}
{...generateTestId(Platform, TERMS_OF_USE_CHECKBOX_ICON_ID)}
>
-
+
{checkboxText}
{
);
+ const EditAccountNameFlow = () => (
+
+
+
+ );
+
return (
// do not render unless a route is defined
(route && (
@@ -612,6 +619,10 @@ const App = ({ userLoggedIn }) => {
component={ConnectQRHardwareFlow}
options={{ animationEnabled: true }}
/>
+
{renderSplash()}
diff --git a/app/components/UI/ApproveTransactionReview/index.js b/app/components/UI/ApproveTransactionReview/index.js
index 29d97b96884..4be08fc2d76 100644
--- a/app/components/UI/ApproveTransactionReview/index.js
+++ b/app/components/UI/ApproveTransactionReview/index.js
@@ -1,10 +1,5 @@
import React, { PureComponent } from 'react';
-import {
- View,
- TouchableOpacity,
- InteractionManager,
- Linking,
-} from 'react-native';
+import { View, TouchableOpacity, InteractionManager } from 'react-native';
import Eth from 'ethjs-query';
import ActionView from '../../UI/ActionView';
import PropTypes from 'prop-types';
@@ -45,13 +40,11 @@ import { WALLET_CONNECT_ORIGIN } from '../../../util/walletconnect';
import { withNavigation } from '@react-navigation/compat';
import {
isTestNet,
- isMainnetByChainId,
isMultiLayerFeeNetwork,
fetchEstimatedMultiLayerL1Fee,
} from '../../../util/networks';
import EditPermission from './EditPermission';
import Logger from '../../../util/Logger';
-import InfoModal from '../Swaps/components/InfoModal';
import ButtonLink from '../../../component-library/components/Buttons/Button/variants/ButtonLink';
import { getTokenList } from '../../../reducers/tokens';
import TransactionReview from '../../UI/TransactionReview/TransactionReviewEIP1559Update';
@@ -258,7 +251,6 @@ class ApproveTransactionReview extends PureComponent {
spenderAddress: '0x...',
transaction: this.props.transaction,
token: {},
- showGasTooltip: false,
gasTransactionObject: {},
multiLayerL1FeeTotal: '0x0',
fetchingUpdateDone: false,
@@ -560,46 +552,6 @@ class ApproveTransactionReview extends PureComponent {
);
};
- openLinkAboutGas = () =>
- Linking.openURL(
- 'https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172',
- );
-
- toggleGasTooltip = () =>
- this.setState((state) => ({ showGasTooltip: !state.showGasTooltip }));
-
- renderGasTooltip = () => {
- const isMainnet = isMainnetByChainId(this.props.chainId);
- return (
-
-
- {strings('transaction.gas_education_1')}
- {strings(
- `transaction.gas_education_2${isMainnet ? '_ethereum' : ''}`,
- )}{' '}
- {strings('transaction.gas_education_3')}
-
-
- {strings('transaction.gas_education_4')}
-
-
-
- {strings('transaction.gas_education_learn_more')}
-
-
-
- }
- />
- );
- };
-
renderEditPermission = () => {
const {
host,
@@ -863,7 +815,6 @@ class ApproveTransactionReview extends PureComponent {
- {this.renderGasTooltip()}
>
);
};
diff --git a/app/components/UI/CollectibleOverview/index.js b/app/components/UI/CollectibleOverview/index.js
index 2162e5cbe2a..5028fc1e562 100644
--- a/app/components/UI/CollectibleOverview/index.js
+++ b/app/components/UI/CollectibleOverview/index.js
@@ -13,6 +13,7 @@ import {
SafeAreaView,
TouchableWithoutFeedback,
} from 'react-native';
+
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { baseStyles } from '../../../styles/common';
@@ -28,6 +29,7 @@ import { toLocaleDate } from '../../../util/date';
import { renderFromWei } from '../../../util/number';
import { renderShortAddress } from '../../../util/address';
import { isMainNet } from '../../../util/networks';
+import { isLinkSafe } from '../../../util/linkCheck';
import etherscanLink from '@metamask/etherscan-link';
import {
addFavoriteCollectible,
@@ -131,6 +133,10 @@ const createStyles = (colors) =>
},
});
+const FieldType = {
+ Link: 'Link',
+ Text: 'Text',
+};
/**
* View that displays the information of a specific ERC-721 Token
*/
@@ -166,11 +172,10 @@ const CollectibleOverview = ({
}, [collectible.description]);
const renderCollectibleInfoRow = useCallback(
- (key, value, onPress) => {
+ ({ key, value, onPress, type }) => {
if (!value) return null;
-
- if (value.toLowerCase().includes('javascript')) {
- return null;
+ if (type === FieldType.Link) {
+ if (!isLinkSafe(value)) return null;
}
return (
@@ -203,42 +208,50 @@ const CollectibleOverview = ({
);
const renderCollectibleInfo = () => [
- renderCollectibleInfoRow(
- strings('collectible.collectible_token_standard'),
- collectible?.standard,
- ),
- renderCollectibleInfoRow(
- strings('collectible.collectible_last_sold'),
- collectible?.lastSale?.event_timestamp &&
+ renderCollectibleInfoRow({
+ key: strings('collectible.collectible_token_standard'),
+ value: collectible?.standard,
+ type: FieldType.Text,
+ }),
+ renderCollectibleInfoRow({
+ key: strings('collectible.collectible_last_sold'),
+ value:
+ collectible?.lastSale?.event_timestamp &&
toLocaleDate(
new Date(collectible?.lastSale?.event_timestamp),
).toString(),
- ),
- renderCollectibleInfoRow(
- strings('collectible.collectible_last_price_sold'),
- collectible?.lastSale?.total_price &&
+ type: FieldType.Text,
+ }),
+ renderCollectibleInfoRow({
+ key: strings('collectible.collectible_last_price_sold'),
+ value:
+ collectible?.lastSale?.total_price &&
`${renderFromWei(collectible?.lastSale?.total_price)} ETH`,
- ),
- renderCollectibleInfoRow(
- strings('collectible.collectible_source'),
- collectible?.imageOriginal,
- () => openLink(collectible?.imageOriginal),
- ),
- renderCollectibleInfoRow(
- strings('collectible.collectible_link'),
- collectible?.externalLink,
- () => openLink(collectible?.externalLink),
- ),
- renderCollectibleInfoRow(
- strings('collectible.collectible_asset_contract'),
- renderShortAddress(collectible?.address),
- () => {
+ type: FieldType.Text,
+ }),
+ renderCollectibleInfoRow({
+ key: strings('collectible.collectible_source'),
+ value: collectible?.imageOriginal,
+ onPress: () => openLink(collectible?.imageOriginal),
+ type: FieldType.Link,
+ }),
+ renderCollectibleInfoRow({
+ key: strings('collectible.collectible_link'),
+ value: collectible?.externalLink,
+ onPress: () => openLink(collectible?.externalLink),
+ type: FieldType.Link,
+ }),
+ renderCollectibleInfoRow({
+ key: strings('collectible.collectible_asset_contract'),
+ value: renderShortAddress(collectible?.address),
+ onPress: () => {
if (isMainNet(chainId))
openLink(
etherscanLink.createTokenTrackerLink(collectible?.address, chainId),
);
},
- ),
+ type: FieldType.Text,
+ }),
];
const collectibleToFavorites = useCallback(() => {
diff --git a/app/components/UI/FiatOnRampAggregator/Views/AmountToBuy.tsx b/app/components/UI/FiatOnRampAggregator/Views/AmountToBuy.tsx
index 4bd1abbf398..65152f27ae5 100644
--- a/app/components/UI/FiatOnRampAggregator/Views/AmountToBuy.tsx
+++ b/app/components/UI/FiatOnRampAggregator/Views/AmountToBuy.tsx
@@ -663,7 +663,9 @@ const AmountToBuy = () => {
description={strings(
'fiat_on_ramp_aggregator.no_tokens_available',
{
- network: NETWORKS_NAMES[selectedChainId],
+ network:
+ NETWORKS_NAMES[selectedChainId] ||
+ strings('fiat_on_ramp_aggregator.this_network'),
region: selectedRegion?.name,
},
)}
diff --git a/app/components/UI/FiatOnRampAggregator/Views/Checkout.tsx b/app/components/UI/FiatOnRampAggregator/Views/Checkout.tsx
index 47aeee70091..25a6f9cf073 100644
--- a/app/components/UI/FiatOnRampAggregator/Views/Checkout.tsx
+++ b/app/components/UI/FiatOnRampAggregator/Views/Checkout.tsx
@@ -1,40 +1,32 @@
import React, { useCallback, useEffect, useState } from 'react';
import { View } from 'react-native';
-import { useDispatch, useSelector } from 'react-redux';
+import { useDispatch } from 'react-redux';
import { parseUrl } from 'query-string';
import { WebView, WebViewNavigation } from 'react-native-webview';
import { useNavigation } from '@react-navigation/native';
-import { CryptoCurrency, Order, Provider } from '@consensys/on-ramp-sdk';
+import { Provider } from '@consensys/on-ramp-sdk';
import { baseStyles } from '../../../../styles/common';
import { useTheme } from '../../../../util/theme';
import { getFiatOnRampAggNavbar } from '../../Navbar';
-import { NATIVE_ADDRESS } from '../../../../constants/on-ramp';
import { useFiatOnRampSDK, SDK } from '../sdk';
import {
addFiatCustomIdData,
- addFiatOrder,
- FiatOrder,
removeFiatCustomIdData,
} from '../../../../reducers/fiatOrders';
import { CustomIdData } from '../../../../reducers/fiatOrders/types';
-import Engine from '../../../../core/Engine';
-import { toLowerCaseEquals } from '../../../../util/general';
import {
createNavigationDetails,
useParams,
} from '../../../../util/navigation/navUtils';
-import { hexToBN } from '../../../../util/number';
-import { protectWalletModalVisible } from '../../../../actions/user';
import { aggregatorOrderToFiatOrder } from '../orderProcessor/aggregator';
import { createCustomOrderIdData } from '../orderProcessor/customOrderId';
-import { getNotificationDetails } from '..';
-import NotificationManager from '../../../../core/NotificationManager';
import ScreenLayout from '../components/ScreenLayout';
import ErrorView from '../components/ErrorView';
import ErrorViewWithReporting from '../components/ErrorViewWithReporting';
import useAnalytics from '../hooks/useAnalytics';
import { strings } from '../../../../../locales/i18n';
import Routes from '../../../../constants/navigation/Routes';
+import useHandleSuccessfulOrder from '../hooks/useHandleSuccessfulOrder';
interface CheckoutParams {
url: string;
@@ -58,10 +50,7 @@ const CheckoutWebView = () => {
const navigation = useNavigation();
const params = useParams();
const { colors } = useTheme();
- const accounts = useSelector(
- (state: any) =>
- state.engine.backgroundState.AccountTrackerController.accounts,
- );
+ const handleSuccessfulOrder = useHandleSuccessfulOrder();
const { url: uri, customOrderId, provider } = params;
@@ -97,88 +86,6 @@ const CheckoutWebView = () => {
dispatch(addFiatCustomIdData(customOrderIdData));
}, [customOrderId, dispatch, selectedAddress, selectedChainId]);
- const addTokenToTokensController = useCallback(
- async (token: CryptoCurrency) => {
- if (!token) return;
-
- const { address, symbol, decimals, network, name } = token;
- const chainId = network?.chainId;
-
- if (
- Number(chainId) !== Number(selectedChainId) ||
- address === NATIVE_ADDRESS
- ) {
- return;
- }
-
- // @ts-expect-error Engine context typing
- const { TokensController } = Engine.context;
-
- if (
- !TokensController.state.tokens.includes((t: any) =>
- toLowerCaseEquals(t.address, address),
- )
- ) {
- await TokensController.addToken(address, symbol, decimals, null, name);
- }
- },
- [selectedChainId],
- );
-
- const handleAddFiatOrder = useCallback(
- (order) => {
- dispatch(addFiatOrder(order));
- },
- [dispatch],
- );
-
- const handleDispatchUserWalletProtection = useCallback(() => {
- dispatch(protectWalletModalVisible());
- }, [dispatch]);
-
- const handleSuccessfulOrder = useCallback(
- async (order) => {
- // add the order to the redux global store
- handleAddFiatOrder(order);
- // register the token automatically
- await addTokenToTokensController((order as any)?.data?.cryptoCurrency);
-
- // prompt user to protect his/her wallet
- handleDispatchUserWalletProtection();
- // close the checkout webview
- // @ts-expect-error navigation prop mismatch
- navigation.dangerouslyGetParent()?.pop();
- NotificationManager.showSimpleNotification(
- getNotificationDetails(order as any),
- );
- trackEvent('ONRAMP_PURCHASE_SUBMITTED', {
- provider_onramp: ((order as FiatOrder)?.data as Order)?.provider?.name,
- payment_method_id: ((order as FiatOrder)?.data as Order)?.paymentMethod
- ?.id,
- currency_source: ((order as FiatOrder)?.data as Order)?.fiatCurrency
- .symbol,
- currency_destination: ((order as FiatOrder)?.data as Order)
- ?.cryptoCurrency.symbol,
- chain_id_destination: selectedChainId,
- order_type: (order as FiatOrder)?.orderType,
- is_apple_pay: false,
- has_zero_native_balance: accounts[selectedAddress]?.balance
- ? (hexToBN(accounts[selectedAddress].balance) as any)?.isZero?.()
- : undefined,
- });
- },
- [
- accounts,
- addTokenToTokensController,
- handleAddFiatOrder,
- handleDispatchUserWalletProtection,
- navigation,
- selectedAddress,
- selectedChainId,
- trackEvent,
- ],
- );
-
const handleNavigationStateChange = async (navState: WebViewNavigation) => {
if (
!isRedirectionHandled &&
diff --git a/app/components/UI/FiatOnRampAggregator/containers/ApplePayButton.tsx b/app/components/UI/FiatOnRampAggregator/containers/ApplePayButton.tsx
index 1108f1580ee..20e40e87f60 100644
--- a/app/components/UI/FiatOnRampAggregator/containers/ApplePayButton.tsx
+++ b/app/components/UI/FiatOnRampAggregator/containers/ApplePayButton.tsx
@@ -1,24 +1,19 @@
import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { useNavigation } from '@react-navigation/native';
-import { Order, QuoteResponse } from '@consensys/on-ramp-sdk';
-import { protectWalletModalNotVisible } from '../../../../actions/user';
+import { QuoteResponse } from '@consensys/on-ramp-sdk';
import {
addAuthenticationUrl,
- addFiatOrder,
FiatOrder,
} from '../../../../reducers/fiatOrders';
import ApplePayButtonComponent from '../components/ApplePayButton';
import useApplePay, { ABORTED } from '../hooks/useApplePay';
-import useAnalytics from '../hooks/useAnalytics';
import Logger from '../../../../util/Logger';
import { strings } from '../../../../../locales/i18n';
import { setLockTime } from '../../../../actions/settings';
import { aggregatorOrderToFiatOrder } from '../orderProcessor/aggregator';
-import { getNotificationDetails } from '..';
import NotificationManager from '../../../../core/NotificationManager';
-import { hexToBN } from '../../../../util/number';
import { useFiatOnRampSDK } from '../sdk';
+import useHandleSuccessfulOrder from '../hooks/useHandleSuccessfulOrder';
function buildAuthenticationUrl(url: string, redirectUrl: string) {
const urlObject = new URL(url);
@@ -36,66 +31,13 @@ const ApplePayButton = ({
quote: QuoteResponse;
label: string;
}) => {
- const navigation = useNavigation();
- const dispatch = useDispatch();
- const trackEvent = useAnalytics();
const { selectedAddress, selectedChainId, callbackBaseUrl } =
useFiatOnRampSDK();
- const accounts = useSelector(
- (state: any) =>
- state.engine.backgroundState.AccountTrackerController.accounts,
- );
-
+ const dispatch = useDispatch();
const [pay] = useApplePay(quote);
+ const handleSuccessfulOrder = useHandleSuccessfulOrder();
const lockTime = useSelector((state: any) => state.settings.lockTime);
- const addOrder = useCallback(
- (order) => dispatch(addFiatOrder(order)),
- [dispatch],
- );
- const protectWalletModalVisible = useCallback(
- () => dispatch(protectWalletModalNotVisible()),
- [dispatch],
- );
-
- const handleSuccessfulOrder = useCallback(
- (order) => {
- const fiatOrder: FiatOrder = {
- ...aggregatorOrderToFiatOrder(order),
- network: selectedChainId,
- account: selectedAddress,
- };
- addOrder(fiatOrder);
- // @ts-expect-error pop is not defined
- navigation.dangerouslyGetParent()?.pop();
- protectWalletModalVisible();
- NotificationManager.showSimpleNotification(
- getNotificationDetails(fiatOrder),
- );
- trackEvent('ONRAMP_PURCHASE_SUBMITTED', {
- provider_onramp: (fiatOrder?.data as Order)?.provider?.name,
- payment_method_id: (fiatOrder?.data as Order)?.paymentMethod?.id,
- currency_source: (fiatOrder?.data as Order)?.fiatCurrency.symbol,
- currency_destination: (fiatOrder?.data as Order)?.cryptoCurrency.symbol,
- chain_id_destination: selectedChainId,
- is_apple_pay: true,
- order_type: fiatOrder.orderType,
- has_zero_native_balance: accounts[selectedAddress]?.balance
- ? (hexToBN(accounts[selectedAddress].balance) as any)?.isZero?.()
- : undefined,
- });
- },
- [
- accounts,
- addOrder,
- selectedChainId,
- navigation,
- protectWalletModalVisible,
- selectedAddress,
- trackEvent,
- ],
- );
-
const handlePress = useCallback(async () => {
const prevLockTime = lockTime;
dispatch(setLockTime(-1));
@@ -109,8 +51,14 @@ const ApplePayButton = ({
);
dispatch(addAuthenticationUrl(authenticationUrl));
}
-
- handleSuccessfulOrder(paymentResult.order);
+ if (paymentResult.order) {
+ const fiatOrder: FiatOrder = {
+ ...aggregatorOrderToFiatOrder(paymentResult.order),
+ network: selectedChainId,
+ account: selectedAddress,
+ };
+ handleSuccessfulOrder(fiatOrder, { isApplePay: true });
+ }
}
} catch (error: any) {
NotificationManager.showSimpleNotification({
@@ -130,6 +78,8 @@ const ApplePayButton = ({
dispatch,
pay,
callbackBaseUrl,
+ selectedChainId,
+ selectedAddress,
handleSuccessfulOrder,
quote.crypto?.symbol,
]);
diff --git a/app/components/UI/FiatOnRampAggregator/hooks/useHandleSuccessfulOrder.ts b/app/components/UI/FiatOnRampAggregator/hooks/useHandleSuccessfulOrder.ts
new file mode 100644
index 00000000000..f4c680f303a
--- /dev/null
+++ b/app/components/UI/FiatOnRampAggregator/hooks/useHandleSuccessfulOrder.ts
@@ -0,0 +1,118 @@
+import { CryptoCurrency, Order } from '@consensys/on-ramp-sdk';
+import { hexToBN } from '@metamask/controller-utils';
+import { useNavigation } from '@react-navigation/native';
+import { useCallback } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { getNotificationDetails } from '..';
+import { protectWalletModalVisible } from '../../../../actions/user';
+import { NATIVE_ADDRESS } from '../../../../constants/on-ramp';
+import Engine from '../../../../core/Engine';
+import NotificationManager from '../../../../core/NotificationManager';
+import { addFiatOrder, FiatOrder } from '../../../../reducers/fiatOrders';
+import { toLowerCaseEquals } from '../../../../util/general';
+import useThunkDispatch from '../../../hooks/useThunkDispatch';
+import { useFiatOnRampSDK } from '../sdk';
+import { stateHasOrder } from '../utils';
+import useAnalytics from './useAnalytics';
+
+function useHandleSuccessfulOrder() {
+ const { selectedChainId, selectedAddress } = useFiatOnRampSDK();
+ const navigation = useNavigation();
+ const dispatch = useDispatch();
+ const dispatchThunk = useThunkDispatch();
+ const trackEvent = useAnalytics();
+ const accounts = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.AccountTrackerController.accounts,
+ );
+
+ const addTokenToTokensController = useCallback(
+ async (token: CryptoCurrency) => {
+ if (!token) return;
+
+ const { address, symbol, decimals, network, name } = token;
+ const chainId = network?.chainId;
+
+ if (
+ Number(chainId) !== Number(selectedChainId) ||
+ address === NATIVE_ADDRESS
+ ) {
+ return;
+ }
+
+ const { TokensController } = Engine.context;
+
+ if (
+ !TokensController.state.tokens.includes((t: any) =>
+ toLowerCaseEquals(t.address, address),
+ )
+ ) {
+ await TokensController.addToken(address, symbol, decimals, null, name);
+ }
+ },
+ [selectedChainId],
+ );
+
+ const handleDispatchUserWalletProtection = useCallback(() => {
+ dispatch(protectWalletModalVisible());
+ }, [dispatch]);
+
+ const handleAddFiatOrder = useCallback(
+ (order) => {
+ dispatch(addFiatOrder(order));
+ },
+ [dispatch],
+ );
+
+ const handleSuccessfulOrder = useCallback(
+ async (
+ order: FiatOrder,
+ params?: {
+ isApplePay?: boolean;
+ },
+ ) => {
+ await addTokenToTokensController((order as any)?.data?.cryptoCurrency);
+ handleDispatchUserWalletProtection();
+ // @ts-expect-error navigation prop mismatch
+ navigation.dangerouslyGetParent()?.pop();
+
+ dispatchThunk((_, getState) => {
+ const state = getState();
+ if (stateHasOrder(state, order)) {
+ return;
+ }
+ handleAddFiatOrder(order);
+ NotificationManager.showSimpleNotification(
+ getNotificationDetails(order as any),
+ );
+ trackEvent('ONRAMP_PURCHASE_SUBMITTED', {
+ provider_onramp: (order?.data as Order)?.provider?.name,
+ payment_method_id: (order?.data as Order)?.paymentMethod?.id,
+ currency_source: (order?.data as Order)?.fiatCurrency.symbol,
+ currency_destination: (order?.data as Order)?.cryptoCurrency.symbol,
+ chain_id_destination: selectedChainId,
+ order_type: order?.orderType,
+ is_apple_pay: Boolean(params?.isApplePay),
+ has_zero_native_balance: accounts[selectedAddress]?.balance
+ ? (hexToBN(accounts[selectedAddress].balance) as any)?.isZero?.()
+ : undefined,
+ });
+ });
+ },
+ [
+ accounts,
+ addTokenToTokensController,
+ dispatchThunk,
+ handleAddFiatOrder,
+ handleDispatchUserWalletProtection,
+ navigation,
+ selectedAddress,
+ selectedChainId,
+ trackEvent,
+ ],
+ );
+
+ return handleSuccessfulOrder;
+}
+
+export default useHandleSuccessfulOrder;
diff --git a/app/components/UI/FiatOnRampAggregator/hooks/useInAppBrowser.ts b/app/components/UI/FiatOnRampAggregator/hooks/useInAppBrowser.ts
index 2b0e7b04829..cc2c58a92a3 100644
--- a/app/components/UI/FiatOnRampAggregator/hooks/useInAppBrowser.ts
+++ b/app/components/UI/FiatOnRampAggregator/hooks/useInAppBrowser.ts
@@ -2,8 +2,7 @@ import { useCallback } from 'react';
import { Linking } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import InAppBrowser from 'react-native-inappbrowser-reborn';
-import { useNavigation } from '@react-navigation/native';
-import { Order, OrderStatusEnum, Provider } from '@consensys/on-ramp-sdk';
+import { OrderStatusEnum, Provider } from '@consensys/on-ramp-sdk';
import BuyAction from '@consensys/on-ramp-sdk/dist/regions/BuyAction';
import useAnalytics from './useAnalytics';
import { callbackBaseDeeplink, SDK, useFiatOnRampSDK } from '../sdk';
@@ -11,16 +10,12 @@ import { createCustomOrderIdData } from '../orderProcessor/customOrderId';
import { aggregatorOrderToFiatOrder } from '../orderProcessor/aggregator';
import {
addFiatCustomIdData,
- addFiatOrder,
FiatOrder,
removeFiatCustomIdData,
} from '../../../../reducers/fiatOrders';
import { setLockTime } from '../../../../actions/settings';
-import { getNotificationDetails } from '..';
-import { protectWalletModalVisible } from '../../../../actions/user';
-import NotificationManager from '../../../../core/NotificationManager';
-import { hexToBN } from '../../../../util/number';
import Logger from '../../../../util/Logger';
+import useHandleSuccessfulOrder from './useHandleSuccessfulOrder';
export default function useInAppBrowser() {
const {
@@ -29,60 +24,11 @@ export default function useInAppBrowser() {
selectedAsset,
selectedChainId,
} = useFiatOnRampSDK();
- const navigation = useNavigation();
+
const dispatch = useDispatch();
const trackEvent = useAnalytics();
const lockTime = useSelector((state: any) => state.settings.lockTime);
- const accounts = useSelector(
- (state: any) =>
- state.engine.backgroundState.AccountTrackerController.accounts,
- );
-
- const handleSuccessfulOrder = useCallback(
- (order: Order) => {
- const transformedOrder: FiatOrder = {
- ...aggregatorOrderToFiatOrder(order),
- account: selectedAddress,
- network: selectedChainId,
- };
-
- // add the order to the redux global store
- dispatch(addFiatOrder(transformedOrder));
-
- // prompt user to protect his/her wallet
- dispatch(protectWalletModalVisible());
- // close the checkout webview
- // @ts-expect-error navigation prop mismatch
- navigation.dangerouslyGetParent()?.pop();
- NotificationManager.showSimpleNotification(
- getNotificationDetails(transformedOrder),
- );
- trackEvent('ONRAMP_PURCHASE_SUBMITTED', {
- provider_onramp: ((transformedOrder as FiatOrder)?.data as Order)
- ?.provider?.name,
- payment_method_id: ((transformedOrder as FiatOrder)?.data as Order)
- ?.paymentMethod?.id,
- currency_source: ((transformedOrder as FiatOrder)?.data as Order)
- ?.fiatCurrency.symbol,
- currency_destination: ((transformedOrder as FiatOrder)?.data as Order)
- ?.cryptoCurrency.symbol,
- chain_id_destination: selectedChainId,
- is_apple_pay: false,
- order_type: (transformedOrder as FiatOrder)?.orderType,
- has_zero_native_balance: accounts[selectedAddress]?.balance
- ? (hexToBN(accounts[selectedAddress].balance) as any)?.isZero?.()
- : undefined,
- });
- },
- [
- accounts,
- dispatch,
- navigation,
- selectedAddress,
- selectedChainId,
- trackEvent,
- ],
- );
+ const handleSuccessfulOrder = useHandleSuccessfulOrder();
const renderInAppBrowser = useCallback(
async (
@@ -154,7 +100,13 @@ export default function useInAppBrowser() {
return;
}
- handleSuccessfulOrder(order);
+ const transformedOrder: FiatOrder = {
+ ...aggregatorOrderToFiatOrder(order),
+ account: selectedAddress,
+ network: selectedChainId,
+ };
+
+ handleSuccessfulOrder(transformedOrder);
} catch (error) {
Logger.error(error as Error, {
message:
diff --git a/app/components/UI/FiatOnRampAggregator/index.tsx b/app/components/UI/FiatOnRampAggregator/index.tsx
index c71c2943da3..55a9198ca07 100644
--- a/app/components/UI/FiatOnRampAggregator/index.tsx
+++ b/app/components/UI/FiatOnRampAggregator/index.tsx
@@ -2,6 +2,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { InteractionManager, StyleSheet, View } from 'react-native';
import React, { useCallback } from 'react';
import WebView from 'react-native-webview';
+import { Order } from '@consensys/on-ramp-sdk';
import AppConstants from '../../../core/AppConstants';
import { MetaMetricsEvents } from '../../../core/Analytics';
@@ -21,15 +22,16 @@ import {
removeAuthenticationUrl,
} from '../../../reducers/fiatOrders';
import useInterval from '../../hooks/useInterval';
+import useThunkDispatch, { ThunkAction } from '../../hooks/useThunkDispatch';
import processOrder from './orderProcessor';
import processCustomOrderIdData from './orderProcessor/customOrderId';
import { aggregatorOrderToFiatOrder } from './orderProcessor/aggregator';
import { trackEvent } from './hooks/useAnalytics';
-import { Order } from '@consensys/on-ramp-sdk';
import { AnalyticsEvents } from './types';
import { CustomIdData } from '../../../reducers/fiatOrders/types';
import { callbackBaseUrl } from '../FiatOnRampAggregator/sdk';
import useFetchOnRampNetworks from './hooks/useFetchOnRampNetworks';
+import { stateHasOrder } from './utils';
const POLLING_FREQUENCY = AppConstants.FIAT_ORDERS.POLLING_FREQUENCY;
const NOTIFICATION_DURATION = 5000;
@@ -215,10 +217,12 @@ async function processCustomOrderId(
dispatchUpdateFiatCustomIdData,
dispatchRemoveFiatCustomIdData,
dispatchAddFiatOrder,
+ dispatchThunk,
}: {
dispatchUpdateFiatCustomIdData: (updatedCustomIdData: CustomIdData) => void;
dispatchRemoveFiatCustomIdData: (customOrderIdData: CustomIdData) => void;
dispatchAddFiatOrder: (fiatOrder: FiatOrder) => void;
+ dispatchThunk: (thunk: ThunkAction) => void;
},
) {
const [customOrderId, fiatOrderResponse] = await processCustomOrderIdData(
@@ -227,11 +231,17 @@ async function processCustomOrderId(
if (fiatOrderResponse) {
const fiatOrder = aggregatorOrderToFiatOrder(fiatOrderResponse);
- dispatchAddFiatOrder(fiatOrder);
- InteractionManager.runAfterInteractions(() => {
- NotificationManager.showSimpleNotification(
- getNotificationDetails(fiatOrder),
- );
+ dispatchThunk((_, getState) => {
+ const state = getState();
+ if (stateHasOrder(state, fiatOrder)) {
+ return;
+ }
+ dispatchAddFiatOrder(fiatOrder);
+ InteractionManager.runAfterInteractions(() => {
+ NotificationManager.showSimpleNotification(
+ getNotificationDetails(fiatOrder),
+ );
+ });
});
dispatchRemoveFiatCustomIdData(customOrderIdData);
} else if (customOrderId.expired) {
@@ -251,6 +261,7 @@ const styles = StyleSheet.create({
function FiatOrders() {
useFetchOnRampNetworks();
const dispatch = useDispatch();
+ const dispatchThunk = useThunkDispatch();
const pendingOrders = useSelector(getPendingOrders);
const customOrderIds = useSelector(getCustomOrderIds);
const authenticationUrls = useSelector(getAuthenticationUrls);
@@ -293,6 +304,7 @@ function FiatOrders() {
dispatchUpdateFiatCustomIdData,
dispatchRemoveFiatCustomIdData,
dispatchAddFiatOrder,
+ dispatchThunk,
}),
),
);
diff --git a/app/components/UI/FiatOnRampAggregator/sdk/index.tsx b/app/components/UI/FiatOnRampAggregator/sdk/index.tsx
index 95cd27bfa87..57eac67260f 100644
--- a/app/components/UI/FiatOnRampAggregator/sdk/index.tsx
+++ b/app/components/UI/FiatOnRampAggregator/sdk/index.tsx
@@ -33,6 +33,33 @@ import I18n, { I18nEvents } from '../../../../../locales/i18n';
import Device from '../../../../util/device';
import useActivationKeys from '../hooks/useActivationKeys';
+const isDevelopment = process.env.NODE_ENV !== 'production';
+const isInternalBuild = process.env.ONRAMP_INTERNAL_BUILD === 'true';
+const isDevelopmentOrInternalBuild = isDevelopment || isInternalBuild;
+
+let environment = Environment.Production;
+if (isInternalBuild) {
+ environment = Environment.Staging;
+} else if (isDevelopment) {
+ environment = Environment.Development;
+}
+
+let context = Context.Mobile;
+if (Device.isAndroid()) {
+ context = Context.MobileAndroid;
+} else if (Device.isIos()) {
+ context = Context.MobileIOS;
+}
+
+export const SDK = OnRampSdk.create(environment, context, {
+ verbose: isDevelopment,
+ locale: I18n.locale,
+});
+
+I18nEvents.addListener('localeChanged', (locale) => {
+ SDK.setLocale(locale);
+});
+
interface OnRampSDKConfig {
POLLING_INTERVAL: number;
POLLING_INTERVAL_HIGHLIGHT: number;
@@ -74,30 +101,6 @@ interface IProviderProps {
children?: React.ReactNode | undefined;
}
-const isDevelopment = process.env.NODE_ENV !== 'production';
-const isInternalBuild = process.env.ONRAMP_INTERNAL_BUILD === 'true';
-const isDevelopmentOrInternalBuild = isDevelopment || isInternalBuild;
-
-const CONTEXT = Device.isAndroid()
- ? Context.MobileAndroid
- : Device.isIos()
- ? Context.MobileIOS
- : Context.Mobile;
-const VERBOSE_SDK = isDevelopment;
-
-export const SDK = OnRampSdk.create(
- isDevelopmentOrInternalBuild ? Environment.Staging : Environment.Production,
- CONTEXT,
- {
- verbose: VERBOSE_SDK,
- locale: I18n.locale,
- },
-);
-
-I18nEvents.addListener('localeChanged', (locale) => {
- SDK.setLocale(locale);
-});
-
export const callbackBaseUrl = isDevelopment
? 'https://on-ramp.metaswap-dev.codefi.network/regions/fake-callback'
: 'https://on-ramp-content.metaswap.codefi.network/regions/fake-callback';
diff --git a/app/components/UI/FiatOnRampAggregator/utils/index.ts b/app/components/UI/FiatOnRampAggregator/utils/index.ts
index 84cf8808d7d..917d504bc83 100644
--- a/app/components/UI/FiatOnRampAggregator/utils/index.ts
+++ b/app/components/UI/FiatOnRampAggregator/utils/index.ts
@@ -1,11 +1,12 @@
import { AggregatorNetwork } from '@consensys/on-ramp-sdk/dist/API';
import { Order } from '@consensys/on-ramp-sdk';
-import { FiatOrder } from '../../../../reducers/fiatOrders';
import {
renderFromTokenMinimalUnit,
renderNumber,
toTokenMinimalUnit,
} from '../../../../util/number';
+import { getOrders, FiatOrder } from '../../../../reducers/fiatOrders';
+import { RootState } from '../../../../reducers/fiatOrders/types';
const isOverAnHour = (minutes: number) => minutes > 59;
@@ -148,3 +149,8 @@ export function getOrderAmount(order: FiatOrder) {
}
return amount;
}
+
+export function stateHasOrder(state: RootState, order: FiatOrder) {
+ const orders = getOrders(state);
+ return orders.some((o) => o.id === order.id);
+}
diff --git a/app/components/UI/Navbar/index.js b/app/components/UI/Navbar/index.js
index 0bae2beda5c..8c688d83dbe 100644
--- a/app/components/UI/Navbar/index.js
+++ b/app/components/UI/Navbar/index.js
@@ -5,15 +5,15 @@ import ModalNavbarTitle from '../ModalNavbarTitle';
import AccountRightButton from '../AccountRightButton';
import {
Alert,
- Text,
- TouchableOpacity,
- View,
- StyleSheet,
Image,
InteractionManager,
Platform,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
} from 'react-native';
-import { fontStyles, colors as importedColors } from '../../../styles/common';
+import { colors as importedColors, fontStyles } from '../../../styles/common';
import IonicIcon from 'react-native-vector-icons/Ionicons';
import EvilIcons from 'react-native-vector-icons/EvilIcons';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
@@ -29,9 +29,9 @@ import PickerNetwork from '../../../component-library/components/Pickers/PickerN
import BrowserUrlBar from '../BrowserUrlBar';
import generateTestId from '../../../../wdio/utils/generateTestId';
import {
- WALLET_VIEW_BURGER_ICON_ID,
HAMBURGER_MENU_BUTTON,
NAVBAR_NETWORK_BUTTON,
+ WALLET_VIEW_BURGER_ICON_ID,
} from '../../../../wdio/screen-objects/testIDs/Screens/WalletView.testIds';
import {
NAV_ANDROID_BACK_BUTTON,
@@ -39,7 +39,6 @@ import {
NETWORK_SCREEN_CLOSE_ICON,
} from '../../../../wdio/screen-objects/testIDs/Screens/NetworksScreen.testids';
import { SEND_CANCEL_BUTTON } from '../../../../wdio/screen-objects/testIDs/Screens/SendScreen.testIds';
-import { CONTACT_EDIT_BUTTON } from '../../../../wdio/screen-objects/testIDs/Screens/Contacts.testids';
import { ASSET_BACK_BUTTON } from '../../../../wdio/screen-objects/testIDs/Screens/TokenOverviewScreen.testIds';
import {
PAYMENT_REQUEST_CLOSE_BUTTON,
@@ -47,12 +46,14 @@ import {
} from '../../../../wdio/screen-objects/testIDs/Screens/RequestToken.testIds';
import { BACK_BUTTON_SIMPLE_WEBVIEW } from '../../../../wdio/screen-objects/testIDs/Components/SimpleWebView.testIds';
import ButtonIcon, {
+ ButtonIconSizes,
ButtonIconVariants,
} from '../../../component-library/components/Buttons/ButtonIcon';
import {
IconName,
IconSize,
} from '../../../component-library/components/Icons/Icon';
+import { EDIT_BUTTON } from '../../../../wdio/screen-objects/testIDs/Common.testIds';
const trackEvent = (event) => {
InteractionManager.runAfterInteractions(() => {
@@ -212,10 +213,12 @@ export function getNavigationOptionsTitle(
elevation: 0,
},
});
+
function navigationPop() {
if (navigationPopEvent) trackEvent(navigationPopEvent);
navigation.pop();
}
+
return {
title,
headerTitleStyle: innerStyles.headerTitleStyle,
@@ -278,9 +281,11 @@ export function getEditableOptions(title, navigation, route, themeColors) {
elevation: 0,
},
});
+
function navigationPop() {
navigation.pop();
}
+
const rightAction = route.params?.dispatch;
const editMode = route.params?.editMode === 'edit';
const addMode = route.params?.mode === 'add';
@@ -305,7 +310,7 @@ export function getEditableOptions(title, navigation, route, themeColors) {
{editMode
@@ -807,6 +812,7 @@ export function getOptinMetricsNavbarOptions(themeColors) {
headerTintColor: themeColors.primary.default,
};
}
+
/**
* Function that returns the navigation options
* for our closable screens,
@@ -839,9 +845,11 @@ export function getClosableNavigationOptions(
color: themeColors.text.default,
},
});
+
function navigationPop() {
navigation.pop();
}
+
return {
title,
headerTitleStyle: innerStyles.headerTitleStyle,
@@ -1365,6 +1373,7 @@ export function getSwapsAmountNavbar(navigation, route, themeColors) {
headerStyle: innerStyles.headerStyle,
};
}
+
export function getSwapsQuotesNavbar(navigation, route, themeColors) {
const innerStyles = StyleSheet.create({
headerButtonText: {
@@ -1529,3 +1538,32 @@ export function getFiatOnRampAggNavbar(
headerTitleStyle: innerStyles.headerTitleStyle,
};
}
+
+export const getEditAccountNameNavBarOptions = (goBack, themeColors) => {
+ const innerStyles = StyleSheet.create({
+ headerStyle: {
+ backgroundColor: themeColors.background.default,
+ shadowColor: importedColors.transparent,
+ elevation: 0,
+ },
+ headerTitleStyle: {
+ fontSize: 18,
+ ...fontStyles.normal,
+ color: themeColors.text.default,
+ },
+ });
+
+ return {
+ headerTitle: {strings('account_actions.edit_name')},
+ headerLeft: null,
+ headerRight: () => (
+
+ ),
+ ...innerStyles,
+ };
+};
diff --git a/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap b/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap
index 669a0db79a9..d7be9f83a92 100644
--- a/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap
+++ b/app/components/UI/Tokens/__snapshots__/index.test.tsx.snap
@@ -560,7 +560,57 @@ exports[`Tokens should hide zero balance tokens when setting is on 1`] = `
],
}
}
- />
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
= ({ tokens }) => {
const { colors, themeAppearance } = useTheme();
@@ -276,11 +277,13 @@ const Tokens: React.FC = ({ tokens }) => {
balance={secondaryBalance}
>
+ }
>
{asset.isETH ? (
diff --git a/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.tsx.snap b/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index 0bd021f7f0f..00000000000
--- a/app/components/UI/TransactionReview/TransactionReviewFeeCard/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,10 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`TransactionReviewFeeCard should render correctly 1`] = `
-
-`;
diff --git a/app/components/UI/TransactionReview/TransactionReviewFeeCard/index.js b/app/components/UI/TransactionReview/TransactionReviewFeeCard/index.js
deleted file mode 100644
index 47e286fd91b..00000000000
--- a/app/components/UI/TransactionReview/TransactionReviewFeeCard/index.js
+++ /dev/null
@@ -1,398 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import {
- StyleSheet,
- View,
- TouchableOpacity,
- ActivityIndicator,
- Linking,
-} from 'react-native';
-import { strings } from '../../../../../locales/i18n';
-import Summary from '../../../Base/Summary';
-import Text from '../../../Base/Text';
-import InfoModal from '../../../UI/Swaps/components/InfoModal';
-import { isMainnetByChainId } from '../../../../util/networks';
-import { connect } from 'react-redux';
-import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
-import FadeAnimationView from '../../FadeAnimationView';
-import { ThemeContext, mockTheme } from '../../../../util/theme';
-import { selectChainId } from '../../../../selectors/networkController';
-
-const createStyles = (colors) =>
- StyleSheet.create({
- overview: {
- marginHorizontal: 24,
- },
- loader: {
- backgroundColor: colors.background.default,
- height: 10,
- flex: 1,
- alignItems: 'flex-end',
- },
- over: {
- color: colors.error.default,
- },
- valuesContainer: {
- flex: 1,
- flexDirection: 'row',
- },
- gasInfoContainer: {
- paddingHorizontal: 2,
- },
- gasInfoIcon: {
- color: colors.primary.default,
- },
- amountContainer: {
- flex: 1,
- },
- gasFeeTitleContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- primaryContainer: (flex) => {
- if (flex) return { flex: 1 };
- return { width: 86, marginLeft: 2 };
- },
- hitSlop: {
- top: 10,
- left: 10,
- bottom: 10,
- right: 10,
- },
- });
-
-/**
- * PureComponent that displays a transaction's fee and total details inside a card
- */
-class TransactionReviewFeeCard extends PureComponent {
- static propTypes = {
- /**
- * True if gas estimation for a transaction is complete
- */
- gasEstimationReady: PropTypes.bool,
- /**
- * Total gas fee in fiat
- */
- totalGasFiat: PropTypes.string,
- /**
- * Total gas fee in ETH
- */
- totalGasEth: PropTypes.string,
- /**
- * Total transaction value in fiat
- */
- totalFiat: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.node),
- PropTypes.node,
- PropTypes.string,
- ]),
- /**
- * Transaction value in fiat before gas fee
- */
- fiat: PropTypes.string,
- /**
- * Total transaction value in ETH
- */
- totalValue: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
- /**
- * Transaction value in ETH before gas fee
- */
- transactionValue: PropTypes.string,
- /**
- * ETH or fiat, dependent on user setting
- */
- primaryCurrency: PropTypes.string,
- /**
- * Changes mode to edit
- */
- edit: PropTypes.func,
- /**
- * True if transaction is over the available funds
- */
- over: PropTypes.bool,
- /**
- * True if transaction is gas price is higher than the "FAST" value
- */
- warningGasPriceHigh: PropTypes.string,
- /**
- * A string representing the network chainId
- */
- chainId: PropTypes.string,
- /**
- * Function to call when update animation starts
- */
- onUpdatingValuesStart: PropTypes.func,
- /**
- * Function to call when update animation ends
- */
- onUpdatingValuesEnd: PropTypes.func,
- /**
- * If the values should animate upon update or not
- */
- animateOnChange: PropTypes.bool,
- /**
- * Boolean to determine if the animation is happening
- */
- isAnimating: PropTypes.bool,
- };
-
- state = {
- showGasTooltip: false,
- };
-
- getStyles = () => {
- const colors = this.context.colors || mockTheme.colors;
- return createStyles(colors);
- };
-
- renderIfGasEstimationReady = (children) => {
- const { gasEstimationReady } = this.props;
- const styles = this.getStyles();
-
- return !gasEstimationReady ? (
-
-
-
- ) : (
- children
- );
- };
-
- openLinkAboutGas = () =>
- Linking.openURL(
- 'https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172',
- );
-
- toggleGasTooltip = () =>
- this.setState((state) => ({ showGasTooltip: !state.showGasTooltip }));
-
- renderGasTooltip = () => {
- const isMainnet = isMainnetByChainId(this.props.chainId);
- return (
-
-
- {strings('transaction.gas_education_1')}
- {strings(
- `transaction.gas_education_2${isMainnet ? '_ethereum' : ''}`,
- )}{' '}
- {strings('transaction.gas_education_3')}
-
-
- {strings('transaction.gas_education_4')}
-
-
-
- {strings('transaction.gas_education_learn_more')}
-
-
-
- }
- />
- );
- };
-
- render() {
- const {
- totalGasFiat,
- totalGasEth,
- totalFiat,
- fiat,
- totalValue,
- transactionValue,
- primaryCurrency,
- edit,
- over,
- warningGasPriceHigh,
- chainId,
- onUpdatingValuesStart,
- onUpdatingValuesEnd,
- animateOnChange,
- isAnimating,
- } = this.props;
- const styles = this.getStyles();
-
- const isMainnet = isMainnetByChainId(chainId);
-
- let amount;
- let networkFee;
- let totalAmount;
- let primaryAmount;
- let primaryNetworkFee;
- let primaryTotalAmount;
- const showNativeCurrency = primaryCurrency === 'ETH' || !isMainnet;
- if (showNativeCurrency) {
- amount = fiat;
- networkFee = totalGasFiat;
- totalAmount = totalFiat;
-
- primaryAmount = transactionValue;
- primaryNetworkFee = totalGasEth;
- primaryTotalAmount = totalValue;
- } else {
- amount = transactionValue;
- networkFee = totalGasEth;
- totalAmount = totalValue;
-
- primaryAmount = fiat;
- primaryNetworkFee = totalGasFiat;
- primaryTotalAmount = totalFiat;
- }
-
- const valueToWatchAnimation = totalGasEth !== '0 ETH' ? totalGasEth : null;
-
- return (
-
-
-
-
- {strings('transaction.amount')}
-
-
- {isMainnet && (
-
- {amount}
-
- )}
-
- {primaryAmount}
-
-
-
-
-
-
-
- {strings('transaction.gas_fee')}
-
-
-
-
-
-
- {this.renderIfGasEstimationReady(
-
- {isMainnet && (
-
-
-
- {networkFee}
-
-
-
- )}
-
-
-
- {primaryNetworkFee}
-
-
-
- ,
- )}
-
-
-
-
- {strings('transaction.total')}
-
-
- {!!totalFiat &&
- this.renderIfGasEstimationReady(
-
- {isMainnet && (
-
- {totalAmount}
-
- )}
-
-
- {primaryTotalAmount}
-
- ,
- )}
-
-
- {this.renderGasTooltip()}
-
- );
- }
-}
-
-const mapStateToProps = (state) => ({
- conversionRate:
- state.engine.backgroundState.CurrencyRateController.conversionRate,
- currentCurrency:
- state.engine.backgroundState.CurrencyRateController.currentCurrency,
- chainId: selectChainId(state),
-});
-
-TransactionReviewFeeCard.contextType = ThemeContext;
-
-export default connect(mapStateToProps)(TransactionReviewFeeCard);
diff --git a/app/components/UI/TransactionReview/TransactionReviewFeeCard/index.test.tsx b/app/components/UI/TransactionReview/TransactionReviewFeeCard/index.test.tsx
deleted file mode 100644
index e6bd54328dc..00000000000
--- a/app/components/UI/TransactionReview/TransactionReviewFeeCard/index.test.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import TransactionReviewFeeCard from './';
-import { shallow } from 'enzyme';
-import configureMockStore from 'redux-mock-store';
-import { Provider } from 'react-redux';
-
-const mockStore = configureMockStore();
-const initialState = {
- engine: {
- backgroundState: {
- CurrencyRateController: {
- currentCurrency: 'usd',
- conversionRate: 0.1,
- },
- NetworkController: {
- providerConfig: {
- ticker: 'ETH',
- chainId: '1',
- },
- },
- },
- },
-};
-const store = mockStore(initialState);
-
-describe('TransactionReviewFeeCard', () => {
- it('should render correctly', () => {
- const wrapper = shallow(
-
-
- ,
- );
- expect(wrapper.dive()).toMatchSnapshot();
- });
-});
diff --git a/app/components/UI/WebsiteIcon/index.js b/app/components/UI/WebsiteIcon/index.js
index cfde080c528..23b594926f3 100644
--- a/app/components/UI/WebsiteIcon/index.js
+++ b/app/components/UI/WebsiteIcon/index.js
@@ -86,10 +86,14 @@ export default class WebsiteIcon extends PureComponent {
const colors = this.context.colors || mockTheme.colors;
const styles = createStyles(colors);
const apiLogoUrl = { uri: icon || this.getIconUrl(url) };
- const title =
- typeof this.props.title === 'string'
- ? this.props.title.substr(0, 1)
- : getHost(url).substr(0, 1);
+ let title = this.props.title;
+
+ if (title !== undefined) {
+ title =
+ typeof this.props.title === 'string'
+ ? this.props.title.substr(0, 1)
+ : getHost(url).substr(0, 1);
+ }
if (renderIconUrlError && title) {
return (
diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx
index 70c7268933a..04a70b1bf5c 100644
--- a/app/components/Views/AccountActions/AccountActions.tsx
+++ b/app/components/Views/AccountActions/AccountActions.tsx
@@ -121,6 +121,10 @@ const AccountActions = () => {
});
};
+ const goToEditAccountName = () => {
+ navigate('EditAccountName');
+ };
+
return (
@@ -128,7 +132,7 @@ const AccountActions = () => {
actionTitle={strings('account_actions.edit_name')}
iconName={IconName.Edit}
// This action will be address on other PR
- onPress={() => null}
+ onPress={goToEditAccountName}
{...generateTestId(Platform, EDIT_ACCOUNT)}
/>
{
+ const { theme } = params;
+ const { colors } = theme;
+ return StyleSheet.create({
+ screen: {
+ flex: 1,
+ paddingHorizontal: 16,
+ backgroundColor: colors.background.default,
+ },
+ inputsContainer: { flex: 1, marginHorizontal: 16 },
+ inputContainer: { marginTop: 24 },
+ buttonsContainer: {
+ flexDirection: 'row',
+ marginHorizontal: 16,
+ },
+ cancelButton: { flex: 1, marginRight: 8 },
+ saveButton: { flex: 1, marginLeft: 8 },
+ saveButtonDisabled: { opacity: 0.5 },
+ });
+};
+export default styleSheet;
diff --git a/app/components/Views/EditAccountName/EditAccountName.test.tsx b/app/components/Views/EditAccountName/EditAccountName.test.tsx
new file mode 100644
index 00000000000..edbb79778cb
--- /dev/null
+++ b/app/components/Views/EditAccountName/EditAccountName.test.tsx
@@ -0,0 +1,109 @@
+// Third party dependencies
+import React from 'react';
+import { fireEvent } from '@testing-library/react-native';
+
+// Internal dependencies
+import EditAccountName from './EditAccountName';
+import renderWithProvider from '../../../util/test/renderWithProvider';
+
+jest.unmock('react-redux');
+const mockSetAccountLabel = jest.fn();
+
+jest.mock('../../../core/Engine', () => ({
+ context: {
+ PreferencesController: {
+ setAccountLabel: () => mockSetAccountLabel,
+ },
+ },
+}));
+
+const initialState = {
+ swaps: { '1': { isLive: true }, hasOnboarded: false, isLive: true },
+ wizard: {
+ step: 0,
+ },
+ settings: {
+ primaryCurrency: 'usd',
+ },
+ engine: {
+ backgroundState: {
+ PreferencesController: {
+ selectedAddress: '0x',
+ identities: {
+ '0x': { name: 'Account 1', address: '0x' },
+ },
+ },
+ },
+ },
+};
+
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useSelector: jest
+ .fn()
+ .mockImplementation((callback) => callback(initialState)),
+}));
+
+const mockNavigate = jest.fn();
+const mockSetOptions = jest.fn();
+const mockGoBack = jest.fn();
+
+jest.mock('@react-navigation/native', () => {
+ const actualReactNavigation = jest.requireActual('@react-navigation/native');
+ return {
+ ...actualReactNavigation,
+ useNavigation: () => ({
+ navigate: mockNavigate,
+ setOptions: mockSetOptions,
+ goBack: mockGoBack,
+ }),
+ };
+});
+
+const renderComponent = (state: any) =>
+ renderWithProvider(, { state });
+
+describe('EditAccountName', () => {
+ afterEach(() => {
+ mockNavigate.mockClear();
+ mockGoBack.mockClear();
+ mockSetOptions.mockClear();
+ });
+ it('should render correctly', () => {
+ const { getByText, toJSON } = renderComponent(initialState);
+ expect(getByText('Cancel')).toBeDefined();
+ expect(getByText('Save')).toBeDefined();
+ expect(getByText('Name')).toBeDefined();
+ expect(getByText('Address')).toBeDefined();
+ expect(toJSON()).toMatchSnapshot();
+ });
+
+ it('should enable the save button when text input changes', () => {
+ const { getByTestId } = renderComponent(initialState);
+ const input = getByTestId('account-name-input');
+ const saveButton = getByTestId('save-button');
+
+ fireEvent.changeText(input, '');
+
+ expect(saveButton.props.disabled).toBe(true);
+ fireEvent.changeText(input, 'Account');
+
+ expect(saveButton.props.disabled).toBe(false);
+ });
+
+ it('should call goBack when cancel button is pressed', () => {
+ const { getByText } = renderComponent(initialState);
+ const cancelButton = getByText('Cancel');
+ fireEvent.press(cancelButton);
+ expect(mockGoBack).toHaveBeenCalled();
+ });
+
+ it('should call navigate when save button is pressed', () => {
+ const { getByTestId } = renderComponent(initialState);
+ const input = getByTestId('account-name-input');
+ const saveButton = getByTestId('save-button');
+ fireEvent.changeText(input, 'New Name');
+ fireEvent.press(saveButton);
+ expect(mockNavigate).toHaveBeenCalled();
+ });
+});
diff --git a/app/components/Views/EditAccountName/EditAccountName.tsx b/app/components/Views/EditAccountName/EditAccountName.tsx
new file mode 100644
index 00000000000..9ba58e21962
--- /dev/null
+++ b/app/components/Views/EditAccountName/EditAccountName.tsx
@@ -0,0 +1,154 @@
+// Third party dependencies
+import React, { useCallback, useEffect, useState } from 'react';
+import { useNavigation } from '@react-navigation/native';
+import { useSelector } from 'react-redux';
+import { InteractionManager, Platform, SafeAreaView } from 'react-native';
+
+// External dependencies
+import Text from '../../../component-library/components/Texts/Text/Text';
+import { View } from 'react-native-animatable';
+import { TextVariant } from '../../../component-library/components/Texts/Text';
+import { strings } from '../../../../locales/i18n';
+import TextField from '../../../component-library/components/Form/TextField/TextField';
+import { formatAddress, getAddressAccountType } from '../../../util/address';
+
+import Button from '../../../component-library/components/Buttons/Button/Button';
+import {
+ ButtonSize,
+ ButtonVariants,
+ ButtonWidthTypes,
+} from '../../../component-library/components/Buttons/Button';
+import { useStyles } from '../../../component-library/hooks';
+import { getEditAccountNameNavBarOptions } from '../../../components/UI/Navbar';
+import Engine from '../../../core/Engine';
+import generateTestId from '../../../../wdio/utils/generateTestId';
+import Analytics from '../../../core/Analytics/Analytics';
+import { MetaMetricsEvents } from '../../../core/Analytics';
+import { selectChainId } from '../../../selectors/networkController';
+import {
+ doENSReverseLookup,
+ isDefaultAccountName,
+} from '../../../util/ENSUtils';
+import { useTheme } from '../../../util/theme';
+
+// Internal dependencies
+import styleSheet from './EditAccountName.styles';
+
+const EditAccountName = () => {
+ const { colors } = useTheme();
+ const { styles } = useStyles(styleSheet, {});
+ const { setOptions, goBack, navigate } = useNavigation();
+ const [accountName, setAccountName] = useState();
+ const [ens, setEns] = useState();
+
+ const selectedAddress = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.PreferencesController.selectedAddress,
+ );
+ const identities = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.PreferencesController.identities,
+ );
+
+ const chainId = useSelector(selectChainId);
+
+ const lookupEns = useCallback(async () => {
+ try {
+ const accountEns = await doENSReverseLookup(selectedAddress, chainId);
+
+ setEns(accountEns);
+ // eslint-disable-next-line no-empty
+ } catch {}
+ }, [selectedAddress, chainId]);
+
+ useEffect(() => {
+ lookupEns();
+ }, [lookupEns]);
+
+ const updateNavBar = useCallback(() => {
+ setOptions(getEditAccountNameNavBarOptions(goBack, colors));
+ }, [setOptions, goBack, colors]);
+
+ useEffect(() => {
+ updateNavBar();
+ }, [updateNavBar]);
+
+ useEffect(() => {
+ const name = identities[selectedAddress].name;
+ setAccountName(isDefaultAccountName(name) && ens ? ens : name);
+ }, [selectedAddress, identities, ens]);
+
+ const onChangeName = (name: string) => {
+ setAccountName(name);
+ };
+
+ const saveAccountName = () => {
+ const { PreferencesController } = Engine.context;
+ PreferencesController.setAccountLabel(selectedAddress, accountName);
+ navigate('WalletView');
+
+ InteractionManager.runAfterInteractions(() => {
+ const analyticsProperties = async () => {
+ const accountType = getAddressAccountType(selectedAddress);
+ const account_type = accountType === 'QR' ? 'hardware' : accountType;
+ return { account_type, chain_id: chainId };
+ };
+ Analytics.trackEventWithParameters(
+ MetaMetricsEvents.ACCOUNT_RENAMED,
+ analyticsProperties(),
+ );
+ });
+ };
+
+ return (
+
+
+
+
+ {strings('address_book.name')}
+
+
+
+
+
+ {strings('address_book.address')}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default EditAccountName;
diff --git a/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap b/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap
new file mode 100644
index 00000000000..3a9babb8db7
--- /dev/null
+++ b/app/components/Views/EditAccountName/__snapshots__/EditAccountName.test.tsx.snap
@@ -0,0 +1,252 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EditAccountName should render correctly 1`] = `
+
+
+
+
+ Name
+
+
+
+
+
+
+
+
+
+ Address
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
+ Save
+
+
+
+
+`;
diff --git a/app/components/Views/EditAccountName/index.tsx b/app/components/Views/EditAccountName/index.tsx
new file mode 100644
index 00000000000..fadacca550b
--- /dev/null
+++ b/app/components/Views/EditAccountName/index.tsx
@@ -0,0 +1 @@
+export { default } from './EditAccountName';
diff --git a/app/components/Views/SendFlow/AddressFrom/AddressFrom.test.tsx b/app/components/Views/SendFlow/AddressFrom/AddressFrom.test.tsx
new file mode 100644
index 00000000000..ff73aac0ef1
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressFrom/AddressFrom.test.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import configureMockStore from 'redux-mock-store';
+
+import { render } from '@testing-library/react-native';
+
+import Engine from '../../../../core/Engine';
+import SendFlowAddressFrom from './';
+
+Engine.init({});
+jest.mock('@react-navigation/native', () => ({
+ useNavigation: () => ({
+ navigation: {},
+ }),
+ createNavigatorFactory: () => ({}),
+}));
+
+const initialState = {
+ settings: {},
+ engine: {
+ backgroundState: {
+ AccountTrackerController: {
+ accounts: {
+ '0x0': {
+ balance: 200,
+ },
+ },
+ },
+ PreferencesController: {
+ selectedAddress: '0x0',
+ identities: {
+ '0x0': {
+ address: '0x0',
+ name: 'Account 1',
+ },
+ },
+ },
+ },
+ },
+};
+
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useSelector: jest
+ .fn()
+ .mockImplementation((callback) => callback(initialState)),
+}));
+
+const mockStore = configureMockStore();
+const store = mockStore(initialState);
+
+describe('SendFlowAddressFrom', () => {
+ it('should render correctly', () => {
+ const wrapper = render(
+
+ undefined} />
+ ,
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/app/components/Views/SendFlow/AddressFrom/AddressFrom.tsx b/app/components/Views/SendFlow/AddressFrom/AddressFrom.tsx
new file mode 100644
index 00000000000..ca526d1fcc1
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressFrom/AddressFrom.tsx
@@ -0,0 +1,108 @@
+import React, { useEffect, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+
+import { hexToBN } from '@metamask/controller-utils';
+import { useNavigation } from '@react-navigation/native';
+
+import { setSelectedAsset } from '../../../../actions/transaction';
+import Routes from '../../../../constants/navigation/Routes';
+import {
+ selectNetwork,
+ selectTicker,
+} from '../../../../selectors/networkController';
+import { doENSReverseLookup } from '../../../../util/ENSUtils';
+import { renderFromWei } from '../../../../util/number';
+import { getEther, getTicker } from '../../../../util/transactions';
+import { AddressFrom } from '../../../UI/AddressInputs';
+import { SFAddressFromProps } from './AddressFrom.types';
+
+const SendFlowAddressFrom = ({
+ fromAccountBalanceState,
+}: SFAddressFromProps) => {
+ const navigation = useNavigation();
+ const identities = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.PreferencesController.identities,
+ );
+
+ const accounts = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.AccountTrackerController.accounts,
+ );
+
+ const network = useSelector((state: any) => selectNetwork(state));
+ const ticker = useSelector(selectTicker);
+
+ const selectedAddress = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.PreferencesController.selectedAddress,
+ );
+
+ const [accountAddress, setAccountAddress] = useState(selectedAddress);
+ const [accountName, setAccountName] = useState(
+ identities[selectedAddress].name,
+ );
+ const [accountBalance, setAccountBalance] = useState('');
+
+ useEffect(() => {
+ async function getAccount() {
+ const ens = await doENSReverseLookup(selectedAddress, network);
+ const balance = `${renderFromWei(
+ accounts[selectedAddress].balance,
+ )} ${getTicker(ticker)}`;
+ const balanceIsZero = hexToBN(accounts[selectedAddress].balance).isZero();
+ setAccountName(ens || identities[selectedAddress].name);
+ setAccountBalance(balance);
+ fromAccountBalanceState(balanceIsZero);
+ }
+ getAccount();
+ }, [
+ accounts,
+ selectedAddress,
+ ticker,
+ network,
+ identities,
+ fromAccountBalanceState,
+ ]);
+
+ const dispatch = useDispatch();
+
+ const selectedAssetAction = (selectedAsset: any) =>
+ dispatch(setSelectedAsset(selectedAsset));
+
+ const onSelectAccount = async (address: string) => {
+ const { name } = identities[address];
+ const balance = `${renderFromWei(accounts[address].balance)} ${getTicker(
+ ticker,
+ )}`;
+ const ens = await doENSReverseLookup(address);
+ const accName = ens || name;
+ const balanceIsZero = hexToBN(accounts[address].balance).isZero();
+ selectedAssetAction(getEther(ticker));
+ setAccountAddress(address);
+ setAccountName(accName);
+ setAccountBalance(balance);
+ fromAccountBalanceState(balanceIsZero);
+ };
+
+ const openAccountSelector = () => {
+ navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
+ screen: Routes.SHEET.ACCOUNT_SELECTOR,
+ params: {
+ isSelectOnly: true,
+ onSelectAccount,
+ },
+ });
+ };
+
+ return (
+
+ );
+};
+
+export default SendFlowAddressFrom;
diff --git a/app/components/Views/SendFlow/AddressFrom/AddressFrom.types.ts b/app/components/Views/SendFlow/AddressFrom/AddressFrom.types.ts
new file mode 100644
index 00000000000..2f05a877ae2
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressFrom/AddressFrom.types.ts
@@ -0,0 +1,3 @@
+export interface SFAddressFromProps {
+ fromAccountBalanceState: (value: boolean) => void;
+}
diff --git a/app/components/Views/SendFlow/AddressFrom/__snapshots__/AddressFrom.test.tsx.snap b/app/components/Views/SendFlow/AddressFrom/__snapshots__/AddressFrom.test.tsx.snap
new file mode 100644
index 00000000000..39eaa4943df
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressFrom/__snapshots__/AddressFrom.test.tsx.snap
@@ -0,0 +1,316 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SendFlowAddressFrom should render correctly 1`] = `
+
+
+
+ From:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Account 1
+
+
+
+ Balance:
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/app/components/Views/SendFlow/AddressFrom/index.ts b/app/components/Views/SendFlow/AddressFrom/index.ts
new file mode 100644
index 00000000000..b81c7b9cd14
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressFrom/index.ts
@@ -0,0 +1 @@
+export { default } from './AddressFrom';
diff --git a/app/components/Views/SendFlow/AddressTo/AddressTo.test.tsx b/app/components/Views/SendFlow/AddressTo/AddressTo.test.tsx
new file mode 100644
index 00000000000..c50db44e732
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressTo/AddressTo.test.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import configureMockStore from 'redux-mock-store';
+
+import { render } from '@testing-library/react-native';
+
+import Engine from '../../../../core/Engine';
+import SendFlowAddressTo from './';
+
+Engine.init({});
+jest.mock('@react-navigation/native', () => ({
+ useNavigation: () => ({
+ navigation: {},
+ }),
+ createNavigatorFactory: () => ({}),
+}));
+
+const initialState = {
+ settings: {},
+ engine: {
+ backgroundState: {
+ AccountTrackerController: {
+ accounts: {
+ '0x0': {
+ balance: 200,
+ },
+ },
+ },
+ AddressBookController: {
+ addressBook: {},
+ },
+ PreferencesController: {
+ selectedAddress: '0x0',
+ identities: {
+ '0x0': {
+ address: '0x0',
+ name: 'Account 1',
+ },
+ },
+ },
+ },
+ },
+};
+
+const mockStore = configureMockStore();
+const store = mockStore(initialState);
+
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useSelector: jest
+ .fn()
+ .mockImplementation((callback) => callback(initialState)),
+}));
+
+describe('SendFlowAddressTo', () => {
+ it('should render correctly', () => {
+ const wrapper = render(
+
+ undefined}
+ inputWidth={undefined}
+ confusableCollectionArray={undefined}
+ isFromAddressBook={undefined}
+ updateParentState={undefined}
+ onToSelectedAddressChange={undefined}
+ />
+ ,
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/app/components/Views/SendFlow/AddressTo/AddressTo.tsx b/app/components/Views/SendFlow/AddressTo/AddressTo.tsx
new file mode 100644
index 00000000000..af72641ba35
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressTo/AddressTo.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+import { Alert } from 'react-native';
+import { useDispatch, useSelector } from 'react-redux';
+
+import { useNavigation } from '@react-navigation/native';
+
+import { strings } from '../../../../../locales/i18n';
+import { showAlert } from '../../../../actions/alert';
+import { NetworkSwitchErrorType } from '../../../../constants/error';
+import Routes from '../../../../constants/navigation/Routes';
+import Engine from '../../../../core/Engine';
+import { selectNetwork } from '../../../../selectors/networkController';
+import { handleNetworkSwitch } from '../../../../util/networks';
+import { AddressTo } from '../../../UI/AddressInputs';
+import { createQRScannerNavDetails } from '../../QRScanner';
+import { SFAddressToProps } from './AddressTo.types';
+
+const SendFlowAddressTo = ({
+ addressToReady,
+ confusableCollectionArray,
+ highlighted,
+ inputRef,
+ inputWidth,
+ isFromAddressBook,
+ onSubmit,
+ onToSelectedAddressChange,
+ toSelectedAddress,
+ toSelectedAddressName,
+ updateParentState,
+}: SFAddressToProps) => {
+ const navigation = useNavigation();
+ const dispatch = useDispatch();
+
+ const network = useSelector(selectNetwork);
+
+ const frequentRpcList = useSelector(
+ (state: any) =>
+ state.engine.backgroundState.PreferencesController.frequentRpcList,
+ );
+
+ const showAlertAction = (config: any) => dispatch(showAlert(config));
+
+ const onHandleNetworkSwitch = (chain_id: string) => {
+ try {
+ const { NetworkController, CurrencyRateController } = Engine.context;
+ const networkSwitch = handleNetworkSwitch(chain_id, frequentRpcList, {
+ networkController: NetworkController,
+ currencyRateController: CurrencyRateController,
+ });
+
+ if (!networkSwitch) return;
+
+ showAlertAction({
+ isVisible: true,
+ autodismiss: 5000,
+ content: 'clipboard-alert',
+ data: { msg: strings('send.warn_network_change') + network },
+ });
+ } catch (e: any) {
+ let alertMessage;
+ switch (e.message) {
+ case NetworkSwitchErrorType.missingNetworkId:
+ alertMessage = strings('send.network_missing_id');
+ break;
+ default:
+ alertMessage = strings('send.network_not_found_description', {
+ chain_id,
+ });
+ }
+ Alert.alert(strings('send.network_not_found_title'), alertMessage);
+ }
+ };
+
+ const onScan = () => {
+ navigation.navigate(
+ ...createQRScannerNavDetails({
+ onScanSuccess: (meta) => {
+ if (meta.chain_id) {
+ onHandleNetworkSwitch(meta.chain_id);
+ }
+ if (meta.target_address) {
+ onToSelectedAddressChange(meta.target_address);
+ }
+ },
+ origin: Routes.SEND_FLOW.SEND_TO,
+ }),
+ );
+ };
+
+ const onToInputFocus = () => {
+ updateParentState({ highlighted: !highlighted });
+ };
+ const onClear = () => onToSelectedAddressChange();
+
+ return (
+
+ );
+};
+
+export default SendFlowAddressTo;
diff --git a/app/components/Views/SendFlow/AddressTo/AddressTo.types.ts b/app/components/Views/SendFlow/AddressTo/AddressTo.types.ts
new file mode 100644
index 00000000000..aab56bd1fbe
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressTo/AddressTo.types.ts
@@ -0,0 +1,13 @@
+export interface SFAddressToProps {
+ addressToReady: boolean;
+ confusableCollectionArray: any;
+ highlighted: boolean;
+ inputRef: any;
+ inputWidth: any;
+ isFromAddressBook: any;
+ onSubmit: (address: string) => void;
+ onToSelectedAddressChange: any;
+ toSelectedAddress: any;
+ toSelectedAddressName: any;
+ updateParentState: any;
+}
diff --git a/app/components/Views/SendFlow/AddressTo/__snapshots__/AddressTo.test.tsx.snap b/app/components/Views/SendFlow/AddressTo/__snapshots__/AddressTo.test.tsx.snap
new file mode 100644
index 00000000000..2af301d8711
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressTo/__snapshots__/AddressTo.test.tsx.snap
@@ -0,0 +1,163 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SendFlowAddressTo should render correctly 1`] = `
+
+
+
+ To:
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/app/components/Views/SendFlow/AddressTo/index.ts b/app/components/Views/SendFlow/AddressTo/index.ts
new file mode 100644
index 00000000000..73015feda7b
--- /dev/null
+++ b/app/components/Views/SendFlow/AddressTo/index.ts
@@ -0,0 +1 @@
+export { default } from './AddressTo';
diff --git a/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.tsx.snap b/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.tsx.snap
index 2b2d3babd36..023ac17daf1 100644
--- a/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/SendFlow/Confirm/__snapshots__/index.test.tsx.snap
@@ -1,49 +1,2743 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Confirm should render correctly 1`] = `
-
+
+ >
+
+
+
+ From:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Balance: undefined
+
+
+
+
+
+
+
+ To:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0x2...0x2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Check the recipient address
+
+
+
+
+
+
+
+
+
+ We have detected a confusable character in the ENS name. Check the ENS name to avoid a potential scam.
+
+
+
+
+
+
+
+
+
+ Amount
+
+
+
+
+
+
+
+
+
+
+ Estimated gas fee
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Total
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This gas fee suggestion is using legacy gas estimation which may be inaccurate.
+
+
+
+
+
+
+
+
+
+
+
+ What are gas fees?
+
+
+
+
+
+
+
+
+
+
+ Gas fees are paid to crypto miners who process transactions on the
+ network.
+
+
+ MetaMask does not profit from gas fees.
+
+
+
+ Gas fees are set by the network and fluctuate based on network traffic and transaction complexity.
+
+
+
+ Learn more about gas fees
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hex Data
+
+
+
+
+
+
+
+
+ Send
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hex Data
+
+
+
+ 0x
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
diff --git a/app/components/Views/SendFlow/Confirm/index.js b/app/components/Views/SendFlow/Confirm/index.js
index 1ff412a779e..13767c3e3e4 100644
--- a/app/components/Views/SendFlow/Confirm/index.js
+++ b/app/components/Views/SendFlow/Confirm/index.js
@@ -471,7 +471,7 @@ class Confirm extends PureComponent {
prepareTransaction,
transactionState: { transaction },
} = this.props;
- const estimation = await getGasLimit(transaction);
+ const estimation = await getGasLimit(transaction, true);
prepareTransaction({ ...transaction, ...estimation });
};
diff --git a/app/components/Views/SendFlow/Confirm/index.test.tsx b/app/components/Views/SendFlow/Confirm/index.test.tsx
index 40436017a24..b5fde06dc6a 100644
--- a/app/components/Views/SendFlow/Confirm/index.test.tsx
+++ b/app/components/Views/SendFlow/Confirm/index.test.tsx
@@ -1,10 +1,12 @@
import React from 'react';
-import { shallow } from 'enzyme';
-import Confirm from './';
-import configureMockStore from 'redux-mock-store';
-import { Provider } from 'react-redux';
+import Confirm from '.';
+
+import renderWithProvider from '../../../../util/test/renderWithProvider';
+
+import Engine from '../../../../core/Engine';
+
+Engine.init();
-const mockStore = configureMockStore();
const initialState = {
engine: {
backgroundState: {
@@ -15,6 +17,9 @@ const initialState = {
type: 'mainnet',
},
},
+ AddressBookController: {
+ addressBook: {},
+ },
AccountTrackerController: {
accounts: { '0x2': { balance: '0' } },
},
@@ -38,7 +43,7 @@ const initialState = {
keyrings: [{ accounts: ['0x'], type: 'HD Key Tree' }],
},
GasFeeController: {
- gasEstimates: {},
+ gasFeeEstimates: {},
},
},
},
@@ -63,15 +68,17 @@ const initialState = {
],
},
};
-const store = mockStore(initialState);
+
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useSelector: jest
+ .fn()
+ .mockImplementation((callback) => callback(initialState)),
+}));
describe('Confirm', () => {
it('should render correctly', () => {
- const wrapper = shallow(
-
-
- ,
- );
- expect(wrapper.dive()).toMatchSnapshot();
+ const wrapper = renderWithProvider(, { state: initialState });
+ expect(wrapper).toMatchSnapshot();
});
});
diff --git a/app/components/Views/SendFlow/SendTo/index.test.tsx b/app/components/Views/SendFlow/SendTo/SendTo.test.tsx
similarity index 98%
rename from app/components/Views/SendFlow/SendTo/index.test.tsx
rename to app/components/Views/SendFlow/SendTo/SendTo.test.tsx
index b303f77636f..d4a384c52b4 100644
--- a/app/components/Views/SendFlow/SendTo/index.test.tsx
+++ b/app/components/Views/SendFlow/SendTo/SendTo.test.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { shallow } from 'enzyme';
-import SendTo from './';
+import SendTo from '.';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
diff --git a/app/components/Views/SendFlow/SendTo/__snapshots__/index.test.tsx.snap b/app/components/Views/SendFlow/SendTo/__snapshots__/SendTo.test.tsx.snap
similarity index 90%
rename from app/components/Views/SendFlow/SendTo/__snapshots__/index.test.tsx.snap
rename to app/components/Views/SendFlow/SendTo/__snapshots__/SendTo.test.tsx.snap
index 05a7be90971..45835b957a5 100644
--- a/app/components/Views/SendFlow/SendTo/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/SendFlow/SendTo/__snapshots__/SendTo.test.tsx.snap
@@ -2,13 +2,6 @@
exports[`SendTo should render correctly 1`] = `
true;
@@ -81,10 +83,6 @@ const dummy = () => true;
*/
class SendFlow extends PureComponent {
static propTypes = {
- /**
- * Map of accounts to information objects including balances
- */
- accounts: PropTypes.object,
/**
* Map representing the address book
*/
@@ -153,6 +151,7 @@ class SendFlow extends PureComponent {
* Boolean that indicates if the network supports buy
*/
isNativeTokenBuySupported: PropTypes.bool,
+ updateParentState: PropTypes.func,
};
addressToInputRef = React.createRef();
@@ -162,8 +161,6 @@ class SendFlow extends PureComponent {
balanceIsZero: false,
addToAddressBookModalVisible: false,
fromSelectedAddress: this.props.selectedAddress,
- fromAccountName: this.props.identities[this.props.selectedAddress].name,
- fromAccountBalance: undefined,
toAccount: undefined,
toSelectedAddressName: undefined,
toSelectedAddressReady: false,
@@ -187,8 +184,6 @@ class SendFlow extends PureComponent {
componentDidMount = async () => {
const {
addressBook,
- selectedAddress,
- accounts,
ticker,
network,
navigation,
@@ -196,24 +191,10 @@ class SendFlow extends PureComponent {
route,
isPaymentRequest,
} = this.props;
- const { fromAccountName } = this.state;
this.updateNavBar();
// For analytics
navigation.setParams({ providerType, isPaymentRequest });
const networkAddressBook = addressBook[network] || {};
- const ens = await doENSReverseLookup(selectedAddress, network);
- const fromAccountBalance = `${renderFromWei(
- accounts[selectedAddress].balance,
- )} ${getTicker(ticker)}`;
-
- setTimeout(() => {
- this.setState({
- fromAccountName: ens || fromAccountName,
- fromAccountBalance,
- balanceIsZero: hexToBN(accounts[selectedAddress].balance).isZero(),
- inputWidth: { width: '100%' },
- });
- }, 100);
if (!Object.keys(networkAddressBook).length) {
setTimeout(() => {
this.addressToInputRef &&
@@ -240,55 +221,6 @@ class SendFlow extends PureComponent {
});
};
- onSelectAccount = async (accountAddress) => {
- const { ticker, accounts, identities } = this.props;
- const { name } = identities[accountAddress];
- const fromAccountBalance = `${renderFromWei(
- accounts[accountAddress].balance,
- )} ${getTicker(ticker)}`;
- const ens = await doENSReverseLookup(accountAddress);
- const fromAccountName = ens || name;
- // If new account doesn't have the asset
- this.props.setSelectedAsset(getEther(ticker));
- this.setState({
- fromAccountName,
- fromAccountBalance,
- fromSelectedAddress: accountAddress,
- balanceIsZero: hexToBN(accounts[accountAddress].balance).isZero(),
- });
- };
-
- openAccountSelector = () => {
- const { navigation } = this.props;
- navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
- screen: Routes.SHEET.ACCOUNT_SELECTOR,
- params: {
- isSelectOnly: true,
- onSelectAccount: this.onSelectAccount,
- },
- });
- };
-
- /**
- * This returns the address name from the address book or user accounts if the selectedAddress exist there
- * @param {String} toAccount - Address input
- * @returns {String | null} - Address or null if toAccount is not in the addressBook or identities array
- */
- getAddressNameFromBookOrIdentities = (toAccount) => {
- if (!toAccount) return;
-
- const { addressBook, network, identities } = this.props;
- const networkAddressBook = addressBook[network] || {};
-
- const checksummedAddress = toChecksumAddress(toAccount);
-
- return networkAddressBook[checksummedAddress]
- ? networkAddressBook[checksummedAddress].name
- : identities[checksummedAddress]
- ? identities[checksummedAddress].name
- : null;
- };
-
isAddressSaved = () => {
const { toAccount } = this.state;
const { addressBook, network, identities } = this.props;
@@ -299,71 +231,6 @@ class SendFlow extends PureComponent {
);
};
- /**
- * This set to the state all the information
- * that come from validating an ENS or address
- * @param {*} toSelectedAddress - The address or the ens writted on the destination input
- */
- validateAddressOrENSFromInput = async (toAccount) => {
- const { network, addressBook, identities, chainId } = this.props;
- const {
- addressError,
- toEnsName,
- addressReady,
- toEnsAddress,
- addToAddressToAddressBook,
- toAddressName,
- errorContinue,
- isOnlyWarning,
- confusableCollection,
- } = await validateAddressOrENS({
- toAccount,
- network,
- addressBook,
- identities,
- chainId,
- });
-
- this.setState({
- addressError,
- toEnsName,
- toSelectedAddressReady: addressReady,
- toEnsAddressResolved: toEnsAddress,
- addToAddressToAddressBook,
- toSelectedAddressName: toAddressName,
- errorContinue,
- isOnlyWarning,
- confusableCollection,
- });
- };
-
- onToSelectedAddressChange = (toAccount) => {
- const addressName = this.getAddressNameFromBookOrIdentities(toAccount);
-
- /**
- * If the address is from addressBook or identities
- * then validation is not necessary since it was already validated
- */
- if (addressName) {
- this.setState({
- toAccount,
- toSelectedAddressReady: true,
- isFromAddressBook: true,
- toSelectedAddressName: addressName,
- });
- } else {
- this.validateAddressOrENSFromInput(toAccount);
- /**
- * Because validateAddressOrENSFromInput is an asynchronous function
- * we are setting the state here synchronously, so it does not block the UI
- * */
- this.setState({
- toAccount,
- isFromAddressBook: false,
- });
- }
- };
-
validateToAddress = () => {
const { toAccount, toEnsAddressResolved } = this.state;
let addressError;
@@ -378,10 +245,6 @@ class SendFlow extends PureComponent {
return addressError;
};
- onToClear = () => {
- this.onToSelectedAddressChange();
- };
-
onChangeAlias = (alias) => {
this.setState({ alias });
};
@@ -435,22 +298,6 @@ class SendFlow extends PureComponent {
}
};
- onScan = () => {
- this.props.navigation.navigate(
- ...createQRScannerNavDetails({
- onScanSuccess: (meta) => {
- if (meta.chain_id) {
- this.handleNetworkSwitch(meta.chain_id);
- }
- if (meta.target_address) {
- this.onToSelectedAddressChange(meta.target_address);
- }
- },
- origin: Routes.SEND_FLOW.SEND_TO,
- }),
- );
- };
-
onTransactionDirectionSet = async () => {
const { setRecipient, navigation, providerType, addRecent } = this.props;
const {
@@ -458,13 +305,13 @@ class SendFlow extends PureComponent {
toAccount,
toEnsName,
toSelectedAddressName,
- fromAccountName,
toEnsAddressResolved,
} = this.state;
if (!this.isAddressSaved()) {
const addressError = this.validateToAddress();
if (addressError) return;
}
+
const toAddress = toEnsAddressResolved || toAccount;
addRecent(toAddress);
setRecipient(
@@ -472,7 +319,6 @@ class SendFlow extends PureComponent {
toAddress,
toEnsName,
toSelectedAddressName,
- fromAccountName,
);
InteractionManager.runAfterInteractions(() => {
Analytics.trackEventWithParameters(
@@ -506,7 +352,7 @@ class SendFlow extends PureComponent {
@@ -591,19 +437,98 @@ class SendFlow extends PureComponent {
addressError
);
+ updateParentState = (state) => {
+ this.setState({ ...state });
+ };
+
+ fromAccountBalanceState = (value) => {
+ this.setState({ balanceIsZero: value });
+ };
+
+ getAddressNameFromBookOrIdentities = (toAccount) => {
+ const { addressBook, identities, network } = this.props;
+ if (!toAccount) return;
+
+ const networkAddressBook = addressBook[network] || {};
+
+ const checksummedAddress = toChecksumAddress(toAccount);
+
+ return networkAddressBook[checksummedAddress]
+ ? networkAddressBook[checksummedAddress].name
+ : identities[checksummedAddress]
+ ? identities[checksummedAddress].name
+ : null;
+ };
+
+ validateAddressOrENSFromInput = async (toAccount) => {
+ const { addressBook, identities, chainId, network } = this.props;
+ const {
+ addressError,
+ toEnsName,
+ addressReady,
+ toEnsAddress,
+ addToAddressToAddressBook,
+ toAddressName,
+ errorContinue,
+ isOnlyWarning,
+ confusableCollection,
+ } = await validateAddressOrENS({
+ toAccount,
+ network,
+ addressBook,
+ identities,
+ chainId,
+ });
+
+ this.setState({
+ addressError,
+ toEnsName,
+ toSelectedAddressReady: addressReady,
+ toEnsAddressResolved: toEnsAddress,
+ addToAddressToAddressBook,
+ toSelectedAddressName: toAddressName,
+ errorContinue,
+ isOnlyWarning,
+ confusableCollection,
+ });
+ };
+
+ onToSelectedAddressChange = (toAccount) => {
+ const addressName = this.getAddressNameFromBookOrIdentities(toAccount);
+
+ /**
+ * If the address is from addressBook or identities
+ * then validation is not necessary since it was already validated
+ */
+ if (addressName) {
+ this.setState({
+ toAccount,
+ toSelectedAddressReady: true,
+ isFromAddressBook: true,
+ toSelectedAddressName: addressName,
+ });
+ } else {
+ this.validateAddressOrENSFromInput(toAccount);
+ /**
+ * Because validateAddressOrENSFromInput is an asynchronous function
+ * we are setting the state here synchronously, so it does not block the UI
+ * */
+ this.setState({
+ toAccount,
+ isFromAddressBook: false,
+ });
+ }
+ };
+
render = () => {
const { ticker, addressBook, network } = this.props;
const {
- fromSelectedAddress,
- fromAccountName,
- fromAccountBalance,
toAccount,
toSelectedAddressReady,
toSelectedAddressName,
addToAddressToAddressBook,
addressError,
balanceIsZero,
- toInputHighlighted,
inputWidth,
errorContinue,
isOnlyWarning,
@@ -611,6 +536,7 @@ class SendFlow extends PureComponent {
isFromAddressBook,
toEnsAddressResolved,
} = this.state;
+
const colors = this.context.colors || mockTheme.colors;
const styles = createStyles(colors);
@@ -633,32 +559,26 @@ class SendFlow extends PureComponent {
-
-
@@ -684,10 +604,7 @@ class SendFlow extends PureComponent {
{addressError && addressError !== CONTACT_ALREADY_SAVED && (
-
+
+
{!errorContinue && (
({
- accounts: state.engine.backgroundState.AccountTrackerController.accounts,
addressBook: state.engine.backgroundState.AddressBookController.addressBook,
chainId: selectChainId(state),
selectedAddress:
diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js
index 9561add4d1e..3b29a7f706a 100644
--- a/app/components/Views/Settings/Contacts/ContactForm/index.js
+++ b/app/components/Views/Settings/Contacts/ContactForm/index.js
@@ -3,10 +3,10 @@ import {
Platform,
SafeAreaView,
StyleSheet,
- TextInput,
- View,
Text,
+ TextInput,
TouchableOpacity,
+ View,
} from 'react-native';
import { fontStyles } from '../../../../../styles/common';
import PropTypes from 'prop-types';
@@ -24,7 +24,7 @@ import {
import ErrorMessage from '../../../SendFlow/ErrorMessage';
import AntIcon from 'react-native-vector-icons/AntDesign';
import ActionSheet from 'react-native-actionsheet';
-import { ThemeContext, mockTheme } from '../../../../../util/theme';
+import { mockTheme, ThemeContext } from '../../../../../util/theme';
import {
CONTACT_ALREADY_SAVED,
SYMBOL_ERROR,
@@ -32,15 +32,18 @@ import {
import Routes from '../../../../../constants/navigation/Routes';
import { createQRScannerNavDetails } from '../../../QRScanner';
import generateTestId from '../../../../../../wdio/utils/generateTestId';
-import {
- CONTACT_NAME_INPUT,
- CONTACT_ADD_BUTTON,
- CONTACT_ADDRESS_INPUT,
-} from '../../../../../../wdio/screen-objects/testIDs/Screens/Contacts.testids';
import {
selectChainId,
selectNetwork,
} from '../../../../../selectors/networkController';
+import {
+ ADD_CONTACT_ADD_BUTTON,
+ ADD_CONTACT_ADDRESS_INPUT,
+ ADD_CONTACT_DELETE_BUTTON,
+ ADD_CONTACT_MEMO_INPUT,
+ ADD_CONTACT_NAME_INPUT,
+ ADD_CONTACTS_CONTAINER_ID,
+} from '../../../../../../wdio/screen-objects/testIDs/Screens/AddContact.testIds';
const createStyles = (colors) =>
StyleSheet.create({
@@ -350,7 +353,10 @@ class ContactForm extends PureComponent {
const styles = createStyles(colors);
return (
-
+
{strings('address_book.name')}
@@ -370,7 +376,7 @@ class ContactForm extends PureComponent {
]}
value={name}
onSubmitEditing={this.jumpToAddressInput}
- {...generateTestId(Platform, CONTACT_NAME_INPUT)}
+ {...generateTestId(Platform, ADD_CONTACT_NAME_INPUT)}
keyboardAppearance={themeAppearance}
/>
@@ -395,7 +401,7 @@ class ContactForm extends PureComponent {
value={toEnsName || address}
ref={this.addressInput}
onSubmitEditing={this.jumpToMemoInput}
- {...generateTestId(Platform, CONTACT_ADDRESS_INPUT)}
+ {...generateTestId(Platform, ADD_CONTACT_ADDRESS_INPUT)}
keyboardAppearance={themeAppearance}
/>
{toEnsName && toEnsAddress && (
@@ -441,7 +447,7 @@ class ContactForm extends PureComponent {
]}
value={memo}
ref={this.memoInput}
- testID={'contact-memo-input'}
+ {...generateTestId(Platform, ADD_CONTACT_MEMO_INPUT)}
keyboardAppearance={themeAppearance}
/>
@@ -464,7 +470,7 @@ class ContactForm extends PureComponent {
type={'confirm'}
disabled={!addressReady || !name || !!addressError}
onPress={this.saveContact}
- testID={CONTACT_ADD_BUTTON}
+ testID={ADD_CONTACT_ADD_BUTTON}
>
{strings(`address_book.${mode}_contact`)}
@@ -476,6 +482,7 @@ class ContactForm extends PureComponent {
type={'warning-empty'}
disabled={!addressReady || !name || !!addressError}
onPress={this.onDelete}
+ testID={ADD_CONTACT_DELETE_BUTTON}
>
{strings(`address_book.delete`)}
diff --git a/app/components/Views/Settings/Contacts/index.js b/app/components/Views/Settings/Contacts/index.js
index fd716a1fb7a..7c8dae72c84 100644
--- a/app/components/Views/Settings/Contacts/index.js
+++ b/app/components/Views/Settings/Contacts/index.js
@@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
-import { SafeAreaView, StyleSheet, Platform } from 'react-native';
+import { Platform, SafeAreaView, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { strings } from '../../../../../locales/i18n';
import { getNavigationOptionsTitle } from '../../../UI/Navbar';
@@ -8,11 +8,15 @@ import AddressList from '../../SendFlow/AddressList';
import StyledButton from '../../../UI/StyledButton';
import Engine from '../../../../core/Engine';
import ActionSheet from 'react-native-actionsheet';
-import { ThemeContext, mockTheme } from '../../../../util/theme';
+import { mockTheme, ThemeContext } from '../../../../util/theme';
import { selectNetwork } from '../../../../selectors/networkController';
import generateTestId from '../../../../../wdio/utils/generateTestId';
-import { CONTACTS_CONTAINER_ID } from '../../../../../wdio/screen-objects/testIDs/Screens/Contacts.testids';
+import {
+ CONTACT_ADD_BUTTON,
+ CONTACTS_CONTAINER_ID,
+} from '../../../../../wdio/screen-objects/testIDs/Screens/Contacts.testids';
+
const createStyles = (colors) =>
StyleSheet.create({
wrapper: {
@@ -38,8 +42,8 @@ class Contacts extends PureComponent {
*/
addressBook: PropTypes.object,
/**
- /* navigation object required to push new views
- */
+ /* navigation object required to push new views
+ */
navigation: PropTypes.object,
/**
* Network id
@@ -140,7 +144,7 @@ class Contacts extends PureComponent {
type={'confirm'}
containerStyle={styles.addContact}
onPress={this.goToAddContact}
- testID={'add-contact-button'}
+ testID={CONTACT_ADD_BUTTON}
>
{strings('address_book.add_contact')}
diff --git a/app/components/hooks/useThunkDispatch.ts b/app/components/hooks/useThunkDispatch.ts
new file mode 100644
index 00000000000..fec5952ee0d
--- /dev/null
+++ b/app/components/hooks/useThunkDispatch.ts
@@ -0,0 +1,11 @@
+import { useDispatch } from 'react-redux';
+import { store } from '../../store';
+type Dispatch = typeof store.dispatch;
+type GetState = typeof store.getState;
+
+export type ThunkAction = (dispatch: Dispatch, getState: GetState) => void;
+
+function useThunkDispatch() {
+ return useDispatch<(thunkAction: ThunkAction) => void>();
+}
+export default useThunkDispatch;
diff --git a/app/constants/error.ts b/app/constants/error.ts
index 13ecdc02902..f31c53001b5 100644
--- a/app/constants/error.ts
+++ b/app/constants/error.ts
@@ -48,3 +48,7 @@ export const VAULT_BACKUP_FAILED_UNDEFINED =
'Unable to backup vault as it is undefined';
export const VAULT_FAILED_TO_GET_VAULT_FROM_BACKUP =
'getVaultFromBackup failed to retrieve vault';
+
+// RPCMethodMiddleware
+export const TOKEN_NOT_SUPPORTED_FOR_NETWORK =
+ 'This token is not supported on this network';
diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts
index 0914aa9a15e..044d1b0d388 100644
--- a/app/constants/navigation/Routes.ts
+++ b/app/constants/navigation/Routes.ts
@@ -45,6 +45,7 @@ const Routes = {
},
SEND_FLOW: {
SEND_TO: 'SendTo',
+ AMOUNT: 'Amount',
},
ACCOUNT_BACKUP: {
STEP_1_B: 'AccountBackupStep1B',
diff --git a/app/constants/test-ids.js b/app/constants/test-ids.js
index 572b8e1f63d..421fcf81980 100644
--- a/app/constants/test-ids.js
+++ b/app/constants/test-ids.js
@@ -58,6 +58,10 @@ export const WHATS_NEW_MODAL_GOT_IT_BUTTON_ID = 'whats-new-modal-got-it-button';
export const INPUT_NETWORK_NAME = 'input-network-name';
export const ADDRESS_BOOK_NEXT_BUTTON = 'address-book-next-button';
+export const NO_ETH_MESSAGE = 'no-eth-message';
+export const SEND_SCREEN = 'send-screen';
+export const ADDRESS_ERROR = 'address-error';
+export const ADD_ADDRESS_BUTTON_ID = 'add-address-button';
// Design System test ids
export const CELL_DISPLAY_TEST_ID = 'cell-display';
diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts
index dbe9dfdbf3d..ede0ca4f592 100644
--- a/app/core/Analytics/MetaMetrics.events.ts
+++ b/app/core/Analytics/MetaMetrics.events.ts
@@ -298,6 +298,9 @@ enum EVENT_NAME {
RECEIVE_BUTTON_CLICKED = 'Receive Button Clicked',
SWAP_BUTTON_CLICKED = 'Swaps Button Clicked',
SEND_BUTTON_CLICKED = 'Send Button Clicked',
+
+ // Edit account name
+ ACCOUNT_RENAMED = 'Account Renamed',
}
enum ACTIONS {
@@ -662,6 +665,9 @@ const events = {
RECEIVE_BUTTON_CLICKED: generateOpt(EVENT_NAME.RECEIVE_BUTTON_CLICKED),
SWAP_BUTTON_CLICKED: generateOpt(EVENT_NAME.SWAP_BUTTON_CLICKED),
SEND_BUTTON_CLICKED: generateOpt(EVENT_NAME.SEND_BUTTON_CLICKED),
+
+ // Edit account name
+ ACCOUNT_RENAMED: generateOpt(EVENT_NAME.ACCOUNT_RENAMED),
};
/**
diff --git a/app/core/DeeplinkManager.js b/app/core/DeeplinkManager.js
index ce72cb177ea..5d786c21b06 100644
--- a/app/core/DeeplinkManager.js
+++ b/app/core/DeeplinkManager.js
@@ -24,7 +24,8 @@ import SDKConnect from '../core/SDKConnect/SDKConnect';
import Routes from '../constants/navigation/Routes';
import Minimizer from 'react-native-minimizer';
import { getAddress } from '../util/address';
-import { allowedToBuy } from '../components/UI/FiatOnRampAggregator';
+import { chainIdSelector, getRampNetworks } from '../reducers/fiatOrders';
+import { isNetworkBuySupported } from '../components/UI/FiatOnRampAggregator/utils';
class DeeplinkManager {
constructor({ navigation, frequentRpcList, dispatch, network }) {
@@ -188,10 +189,15 @@ class DeeplinkManager {
}
_handleBuyCrypto() {
- // Do nothing for now if use is not in a supported network
- if (allowedToBuy(this.network)) {
- this.navigation.navigate(Routes.FIAT_ON_RAMP_AGGREGATOR.ID);
- }
+ this.dispatch((_, getState) => {
+ const state = getState();
+ // Do nothing for now if use is not in a supported network
+ if (
+ isNetworkBuySupported(chainIdSelector(state), getRampNetworks(state))
+ ) {
+ this.navigation.navigate(Routes.FIAT_ON_RAMP_AGGREGATOR.ID);
+ }
+ });
}
parse(url, { browserCallBack, origin, onHandled }) {
diff --git a/app/core/RPCMethods/RPCMethodMiddleware.ts b/app/core/RPCMethods/RPCMethodMiddleware.ts
index 99aded917e6..f953a124dbc 100644
--- a/app/core/RPCMethods/RPCMethodMiddleware.ts
+++ b/app/core/RPCMethods/RPCMethodMiddleware.ts
@@ -20,6 +20,8 @@ import setOnboardingWizardStep from '../../actions/wizard';
import { v1 as random } from 'uuid';
import { getPermittedAccounts } from '../Permissions';
import AppConstants from '../AppConstants.js';
+import { isSmartContractAddress } from '../../util/transactions';
+import { TOKEN_NOT_SUPPORTED_FOR_NETWORK } from '../../constants/error';
const Engine = ImportedEngine as any;
let appVersion = '';
@@ -617,10 +619,19 @@ export const getRpcMethodMiddleware = ({
type,
},
} = req;
- const { TokensController } = Engine.context;
+ const { TokensController, NetworkController } = Engine.context;
+ const { chainId } = NetworkController.state?.providerConfig || {};
checkTabActive();
try {
+ // Check if token exists on wallet's active network.
+ const isTokenOnNetwork = await isSmartContractAddress(
+ address,
+ chainId,
+ );
+ if (!isTokenOnNetwork) {
+ throw new Error(TOKEN_NOT_SUPPORTED_FOR_NETWORK);
+ }
const permittedAccounts = await getPermittedAccounts(hostname);
// This should return the current active account on the Dapp.
const selectedAddress =
diff --git a/app/store/index.js b/app/store/index.js
index 9ea596c37b1..91a2e2a433f 100644
--- a/app/store/index.js
+++ b/app/store/index.js
@@ -1,10 +1,11 @@
-import { createStore } from 'redux';
+import { applyMiddleware, createStore } from 'redux';
import {
persistStore,
persistReducer,
createMigrate,
createTransform,
} from 'redux-persist';
+import thunk from 'redux-thunk';
import AsyncStorage from '@react-native-async-storage/async-storage';
import FilesystemStorage from 'redux-persist-filesystem-storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
@@ -123,7 +124,7 @@ const persistConfig = {
const pReducer = persistReducer(persistConfig, rootReducer);
-export const store = createStore(pReducer);
+export const store = createStore(pReducer, undefined, applyMiddleware(thunk));
/**
* Initialize services after persist is completed
diff --git a/app/store/migrations.js b/app/store/migrations.js
index c7dafc31db0..3c9ff690cb4 100644
--- a/app/store/migrations.js
+++ b/app/store/migrations.js
@@ -11,6 +11,7 @@ import {
DENIED,
EXPLORED,
} from '../constants/storage';
+import { GOERLI } from '../../app/constants/network';
export const migrations = {
// Needed after https://github.com/MetaMask/controllers/pull/152
@@ -63,7 +64,7 @@ export const migrations = {
// If the current network does not have a chainId, switch to testnet.
state.engine.backgroundState.NetworkController.provider = {
ticker: 'ETH',
- type: 'goerli',
+ type: GOERLI,
};
}
return state;
@@ -91,7 +92,7 @@ export const migrations = {
// If the current network does not have a chainId, switch to testnet.
state.engine.backgroundState.NetworkController.provider = {
ticker: 'ETH',
- type: 'goerli',
+ type: GOERLI,
chainId: NetworksChainId.goerli,
};
}
@@ -390,6 +391,19 @@ export const migrations = {
return state;
},
+ 15: (state) => {
+ const chainId =
+ state.engine.backgroundState.NetworkController.providerConfig.chainId;
+ // Deprecate rinkeby, ropsten and Kovan, any user that is on those we fallback to goerli
+ if (chainId === '4' || chainId === '3' || chainId === '42') {
+ state.engine.backgroundState.NetworkController.providerConfig = {
+ chainId: NetworksChainId.goerli,
+ ticker: 'GoerliETH',
+ type: GOERLI,
+ };
+ }
+ return state;
+ },
};
-export const version = 14;
+export const version = 15;
diff --git a/app/util/custom-gas/index.js b/app/util/custom-gas/index.js
index 62c779a3192..b675408e8bb 100644
--- a/app/util/custom-gas/index.js
+++ b/app/util/custom-gas/index.js
@@ -107,12 +107,16 @@ export function parseWaitTime(min) {
return parsed.trim();
}
-export async function getGasLimit(transaction) {
+export async function getGasLimit(transaction, resetGas = false) {
const { TransactionController } = Engine.context;
let estimation;
try {
- estimation = await TransactionController.estimateGas(transaction);
+ const newTransactionObj = resetGas
+ ? { ...transaction, gas: undefined }
+ : transaction;
+
+ estimation = await TransactionController.estimateGas(newTransactionObj);
} catch (error) {
estimation = {
gas: TransactionTypes.CUSTOM_GAS.DEFAULT_GAS_LIMIT,
diff --git a/app/util/custom-gas/index.test.ts b/app/util/custom-gas/index.test.ts
index ca6aa4f8a14..90f09de934f 100644
--- a/app/util/custom-gas/index.test.ts
+++ b/app/util/custom-gas/index.test.ts
@@ -1,4 +1,18 @@
-import { parseWaitTime } from '.';
+import { parseWaitTime, getGasLimit } from '.';
+import Engine from '../../core/Engine';
+
+jest.mock('../../core/Engine');
+
+const ENGINE_MOCK = Engine as jest.MockedClass;
+
+ENGINE_MOCK.context = {
+ TransactionController: {
+ estimateGas: jest.fn().mockImplementation(({ gas }) => {
+ if (gas === undefined) return Promise.resolve({ gas: '0x5208' });
+ return Promise.resolve({ gas });
+ }),
+ },
+};
describe('CustomGas utils :: parseWaitTime', () => {
it('parseWaitTime', () => {
@@ -26,3 +40,15 @@ describe('CustomGas utils :: parseWaitTime', () => {
expect(parseWaitTime(3360, 'hr', 'min', 'sec')).toEqual('2day 8hr');
});
});
+
+describe('CustomGas Util:: GetGasLimit', () => {
+ it('should return passed gas value', async () => {
+ const estimate = await getGasLimit({ gas: '0x9fd2', gasPrice: '12' });
+ expect(estimate.gas.toNumber()).toEqual(40914);
+ });
+
+ it('should fetch new estimated gas value', async () => {
+ const estimate = await getGasLimit({ gas: '0x9fd2', gasPrice: '12' }, true);
+ expect(estimate.gas.toNumber()).toEqual(21000);
+ });
+});
diff --git a/app/util/linkCheck.test.ts b/app/util/linkCheck.test.ts
new file mode 100644
index 00000000000..ffd18494b6d
--- /dev/null
+++ b/app/util/linkCheck.test.ts
@@ -0,0 +1,47 @@
+/* eslint-disable no-script-url */
+import isLinkSafe from './linkCheck';
+
+jest.mock('../core/Engine', () => ({
+ context: {
+ PhishingController: {
+ maybeUpdateState: jest.fn(),
+ test: jest.fn((url: string) => {
+ if (url === 'phishing.com') return { result: true };
+ return { result: false };
+ }),
+ },
+ },
+}));
+
+describe('linkCheck', () => {
+ it('should correctly check links for safety', () => {
+ expect(isLinkSafe('htps://ww.example.com/')).toEqual(false);
+ expect(isLinkSafe('https://ww.example.com/')).toEqual(true);
+ expect(isLinkSafe('http://example com/page?id=123')).toEqual(false);
+ expect(isLinkSafe('https://www.example.com/')).toEqual(true);
+ expect(isLinkSafe('http://phishing.com')).toEqual(false);
+ expect(
+ isLinkSafe(
+ 'https://metamask.app.link/send/pay-Contract-Address@chain-id/transfer?address=Receiver-Address&uint256=1e21',
+ ),
+ ).toEqual(false);
+
+ expect(isLinkSafe('javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('j	avascript:alert(1);')).toEqual(false);
+ expect(isLinkSafe('	javascript:alert(1);&tab;')).toEqual(false);
+ expect(isLinkSafe('javas\x00cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x07cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x0Dcript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x0Acript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x08cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x02cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x03cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x04cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x01cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x05cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x0Bcript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x09cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x06cript:javascript:alert(1)')).toEqual(false);
+ expect(isLinkSafe('javas\x0Ccript:javascript:alert(1)')).toEqual(false);
+ });
+});
diff --git a/app/util/linkCheck.ts b/app/util/linkCheck.ts
new file mode 100644
index 00000000000..caaf4afc701
--- /dev/null
+++ b/app/util/linkCheck.ts
@@ -0,0 +1,35 @@
+import Url from 'url-parse';
+import isUrl from 'is-url';
+import { PhishingController as PhishingControllerClass } from '@metamask/phishing-controller';
+import Engine from '../core/Engine';
+const ALLOWED_PROTOCOLS = ['http:', 'https:'];
+const DENYLISTED_DOMAINS = ['metamask.app.link'];
+
+const isAllowedProtocol = (protocol: string): boolean =>
+ ALLOWED_PROTOCOLS.includes(protocol);
+
+const isAllowedHostname = (hostname: string): boolean => {
+ const { PhishingController } = Engine.context as {
+ PhishingController: PhishingControllerClass;
+ };
+ PhishingController.maybeUpdateState();
+ const phishingControllerTestResult = PhishingController.test(hostname);
+
+ return !(
+ phishingControllerTestResult.result || DENYLISTED_DOMAINS.includes(hostname)
+ );
+};
+
+export const isLinkSafe = (link: string): boolean => {
+ try {
+ const url = new Url(link);
+ const { protocol, hostname, href } = url;
+ return (
+ isUrl(href) && isAllowedProtocol(protocol) && isAllowedHostname(hostname)
+ );
+ } catch (err) {
+ return false;
+ }
+};
+
+export default isLinkSafe;
diff --git a/app/util/number/index.test.ts b/app/util/number/index.test.ts
index cae5f5bc2c6..361e1569fc0 100644
--- a/app/util/number/index.test.ts
+++ b/app/util/number/index.test.ts
@@ -781,10 +781,10 @@ describe('Number utils :: isZeroValue', () => {
it('returns true for 0', () => {
expect(isZeroValue(0)).toBe(true);
});
- it('returns true for 0x0', () => {
+ it('returns true for hexadecimal string 0x0', () => {
expect(isZeroValue('0x0')).toBe(true);
});
- it('returns true for 0x0', () => {
+ it('returns true for hexadecimal integer literal 0x0', () => {
expect(isZeroValue(0x0)).toBe(true);
});
it('returns true for BN zero value', () => {
diff --git a/bitrise.yml b/bitrise.yml
index f0cce505828..84b03332494 100644
--- a/bitrise.yml
+++ b/bitrise.yml
@@ -173,9 +173,9 @@ workflows:
after_run:
- _notify_failure_on_slack
steps:
- - install-missing-android-tools@2:
+ - install-missing-android-tools@3:
inputs:
- - ndk_revision: $NDK_VERSION
+ - ndk_version: $NDK_VERSION
- gradlew_path: $PROJECT_LOCATION/gradlew
- avd-manager@1:
inputs:
@@ -220,7 +220,7 @@ workflows:
- content: |-
#!/usr/bin/env bash
node -v
- METAMASK_ENVIRONMENT='production' yarn test:e2e:ios
+ METAMASK_ENVIRONMENT='production' yarn test:e2e:ios --testNamePattern='Smoke'
title: Detox Build & Test
is_always_run: false
- custom-test-results-export@1:
@@ -328,9 +328,9 @@ workflows:
inputs:
- source: $BITRISEIO_ANDROID_KEYSTORE_URL
- destination: android/keystores/release.keystore
- - install-missing-android-tools@2:
+ - install-missing-android-tools@3:
inputs:
- - ndk_revision: $NDK_VERSION
+ - ndk_version: $NDK_VERSION
- gradlew_path: $PROJECT_LOCATION/gradlew
- script@1:
inputs:
@@ -386,9 +386,9 @@ workflows:
inputs:
- source: $BITRISEIO_ANDROID_QA_KEYSTORE_URL
- destination: android/keystores/internalRelease.keystore
- - install-missing-android-tools@2:
+ - install-missing-android-tools@3:
inputs:
- - ndk_revision: $NDK_VERSION
+ - ndk_version: $NDK_VERSION
- gradlew_path: $PROJECT_LOCATION/gradlew
- script@1:
inputs:
@@ -402,7 +402,7 @@ workflows:
is_always_run: false
is_skippable: true
inputs:
- - deploy_path: $PROJECT_LOCATION/app/build/outputs/apk/qa/release/app-qa-release.apk
+ - deploy_path: $PROJECT_LOCATION/app/build/outputs/apk/qa/release/$QA_APK_NAME.apk
title: Bitrise Deploy APK
- deploy-to-bitrise-io@2:
is_always_run: false
@@ -438,7 +438,7 @@ workflows:
set -e
set -x
set -o pipefail
- APK_PATH=$PROJECT_LOCATION/app/build/outputs/apk/qa/release/app-qa-release.apk
+ APK_PATH=$PROJECT_LOCATION/app/build/outputs/apk/qa/release/$QA_APK_NAME.apk
CUSTOM_ID="$BITRISE_GIT_BRANCH-$VERSION_NAME-$VERSION_NUMBER"
CUSTOM_ID=${CUSTOM_ID////-}
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@$APK_PATH" -F 'data={"custom_id": "'$CUSTOM_ID'"}' | jq -j '.app_url' | envman add --key BROWSERSTACK_APP_URL
@@ -596,7 +596,10 @@ app:
PROJECT_LOCATION: android
- opts:
is_expand: false
- NDK_VERSION: 21e
+ NDK_VERSION: 24.0.8215888
+ - opts:
+ is_expand: false
+ QA_APK_NAME: app-qa-release
- opts:
is_expand: false
MODULE: app
@@ -620,10 +623,10 @@ app:
PROJECT_LOCATION_IOS: ios
- opts:
is_expand: false
- VERSION_NAME: 6.4.0
+ VERSION_NAME: 6.5.0
- opts:
is_expand: false
- VERSION_NUMBER: 1100
+ VERSION_NUMBER: 1104
- opts:
is_expand: false
ANDROID_APK_LINK: ''
@@ -636,6 +639,7 @@ app:
meta:
bitrise.io:
stack: osx-xcode-14.2.x-ventura
+ machine_type_id: g2-m1-max.10core
trigger_map:
- push_branch: release/*
diff --git a/e2e/pages/Drawer/Settings/Contacts/AddContactView.js b/e2e/pages/Drawer/Settings/Contacts/AddContactView.js
index ea09d37ccfe..ed92ae1d784 100644
--- a/e2e/pages/Drawer/Settings/Contacts/AddContactView.js
+++ b/e2e/pages/Drawer/Settings/Contacts/AddContactView.js
@@ -1,27 +1,32 @@
import TestHelpers from '../../../../helpers';
import { BACK_ARROW_BUTTON_ID } from '../../../../../app/constants/test-ids';
+import { EDIT_BUTTON } from '../../../../../wdio/screen-objects/testIDs/Common.testIds';
+import {
+ ADD_CONTACT_ADD_BUTTON,
+ ADD_CONTACT_ADDRESS_INPUT,
+ ADD_CONTACT_DELETE_BUTTON,
+ ADD_CONTACT_MEMO_INPUT,
+ ADD_CONTACT_NAME_INPUT,
+ ADD_CONTACTS_CONTAINER_ID,
+} from '../../../../../wdio/screen-objects/testIDs/Screens/AddContact.testIds';
+import { DELETE_CONTACT_MODAL_DELETE_BUTTON } from '../../../../../wdio/screen-objects/testIDs/Components/DeleteContactModal.testIds';
-const ADD_CONTACT_BUTTON_ID = 'contact-add-contact-button';
-const ADD_CONTACT_CONTAINER_ID = 'add-contact-screen';
-const ADD_CONTACTS_VIEW_CONTACT_INPUT_BOX_ID = 'contact-name-input';
-const ADD_CONTACTS_VIEW_ADDRESS_INPUT_BOX_ID = 'contact-address-input';
const ERROR_MESSAGE_LABEL_ID = 'error-message-warning';
-const MEMO_INPUT_BOX_ID = 'contact-memo-input';
export default class AddContactView {
static async tapAddContactButton() {
if (device.getPlatform() === 'android') {
- await TestHelpers.tap(ADD_CONTACT_BUTTON_ID);
+ await TestHelpers.waitAndTap(ADD_CONTACT_ADD_BUTTON);
await TestHelpers.delay(700);
- await TestHelpers.tap(ADD_CONTACT_BUTTON_ID);
+ await TestHelpers.waitAndTap(ADD_CONTACT_ADD_BUTTON);
} else {
- await TestHelpers.tap(ADD_CONTACT_BUTTON_ID);
+ await TestHelpers.waitAndTap(ADD_CONTACT_ADD_BUTTON);
}
}
static async tapEditButton() {
- await TestHelpers.tapByText('Edit'); // edit button when you tap on the alias
+ await TestHelpers.waitAndTap(EDIT_BUTTON);
}
static async tapEditContactCTA() {
@@ -29,62 +34,53 @@ export default class AddContactView {
}
static async tapBackButton() {
- await TestHelpers.tap(BACK_ARROW_BUTTON_ID);
+ await TestHelpers.waitAndTap(BACK_ARROW_BUTTON_ID);
}
static async tapDeleteContactCTA() {
- await TestHelpers.tapByText('Delete');
+ await TestHelpers.waitAndTap(ADD_CONTACT_DELETE_BUTTON);
if (device.getPlatform() === 'ios') {
- await TestHelpers.tapByText('Delete', 1);
+ await TestHelpers.tapByText(DELETE_CONTACT_MODAL_DELETE_BUTTON, 1);
} else {
- await TestHelpers.tapByText('Delete');
+ await TestHelpers.tapByText(DELETE_CONTACT_MODAL_DELETE_BUTTON);
}
}
static async typeInName(name) {
if (device.getPlatform() === 'android') {
- await TestHelpers.replaceTextInField(
- ADD_CONTACTS_VIEW_CONTACT_INPUT_BOX_ID,
- name,
- );
- await element(
- by.id(ADD_CONTACTS_VIEW_CONTACT_INPUT_BOX_ID),
- ).tapReturnKey();
+ await TestHelpers.replaceTextInField(ADD_CONTACT_NAME_INPUT, name);
+ await element(by.id(ADD_CONTACT_NAME_INPUT)).tapReturnKey();
} else {
- await TestHelpers.replaceTextInField(
- ADD_CONTACTS_VIEW_CONTACT_INPUT_BOX_ID,
- name,
- );
+ await TestHelpers.replaceTextInField(ADD_CONTACT_NAME_INPUT, name);
}
}
+
static async typeInMemo(memo) {
- await TestHelpers.replaceTextInField(MEMO_INPUT_BOX_ID, memo);
+ await TestHelpers.replaceTextInField(ADD_CONTACT_MEMO_INPUT, memo);
}
static async typeInAddress(address) {
- await TestHelpers.replaceTextInField(
- ADD_CONTACTS_VIEW_ADDRESS_INPUT_BOX_ID,
- address,
- );
+ await TestHelpers.replaceTextInField(ADD_CONTACT_ADDRESS_INPUT, address);
}
static async clearAddressInputBox() {
- await TestHelpers.clearField(ADD_CONTACTS_VIEW_ADDRESS_INPUT_BOX_ID);
+ await TestHelpers.clearField(ADD_CONTACT_ADDRESS_INPUT);
}
// Assertions
static async isVisible() {
- await TestHelpers.checkIfVisible(ADD_CONTACT_CONTAINER_ID);
+ await TestHelpers.checkIfVisible(ADD_CONTACTS_CONTAINER_ID);
}
static async isNotVisible() {
- await TestHelpers.checkIfNotVisible(ADD_CONTACT_CONTAINER_ID);
+ await TestHelpers.checkIfNotVisible(ADD_CONTACTS_CONTAINER_ID);
}
static async isErrorMessageVisible() {
await TestHelpers.checkIfVisible(ERROR_MESSAGE_LABEL_ID);
}
+
static async isErrorMessageTextCorrect() {
await TestHelpers.checkIfElementHasString(
ERROR_MESSAGE_LABEL_ID,
diff --git a/e2e/pages/Drawer/Settings/Contacts/ContactsView.js b/e2e/pages/Drawer/Settings/Contacts/ContactsView.js
index 6c67ef37d7f..9662a7b66d0 100644
--- a/e2e/pages/Drawer/Settings/Contacts/ContactsView.js
+++ b/e2e/pages/Drawer/Settings/Contacts/ContactsView.js
@@ -1,18 +1,12 @@
import TestHelpers from '../../../../helpers';
import {
- CONTACT_ADDRESS_INPUT,
+ CONTACT_ADD_BUTTON,
CONTACTS_CONTAINER_ID,
} from '../../../../../wdio/screen-objects/testIDs/Screens/Contacts.testids';
-const ADD_CONTACT_BUTTON_ID = 'add-contact-button';
-
export default class ContactsView {
static async tapAddContactButton() {
- await TestHelpers.tap(ADD_CONTACT_BUTTON_ID);
- }
-
- static async clearAddressInputBox() {
- await TestHelpers.clearField(CONTACT_ADDRESS_INPUT);
+ await TestHelpers.waitAndTap(CONTACT_ADD_BUTTON);
}
static async tapOnAlias(alias) {
@@ -32,6 +26,7 @@ export default class ContactsView {
static async isContactAliasVisible(alias) {
await TestHelpers.checkIfElementWithTextIsVisible(alias);
}
+
static async isContactAliasNotVisible(alias) {
await TestHelpers.checkIfElementWithTextIsNotVisible(alias);
}
diff --git a/e2e/pages/TransactionConfirmView.js b/e2e/pages/TransactionConfirmView.js
index cc24e648d90..54c37973a17 100644
--- a/e2e/pages/TransactionConfirmView.js
+++ b/e2e/pages/TransactionConfirmView.js
@@ -1,18 +1,20 @@
import TestHelpers from '../helpers';
-import { COMFIRM_TXN_AMOUNT } from '../../wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds';
+import {
+ COMFIRM_TXN_AMOUNT,
+ CONFIRM_TRANSACTION_BUTTON_ID,
+ NAVBAR_TITLE_TEXT,
+ TRANSACTION_ACCOUNT_BALANCE,
+ TRANSACTION_VIEW_CONTAINER_ID,
+} from '../../wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds';
import { ESTIMATED_FEE_TEST_ID } from '../../wdio/screen-objects/testIDs/Screens/TransactionSummaryScreen.testIds.js';
import {
EDIT_PRIOTIRY_SCREEN_TEST_ID,
MAX_PRIORITY_FEE_INPUT_TEST_ID,
} from '../../wdio/screen-objects/testIDs/Screens/EditGasFeeScreen.testids.js';
-const TRANSACTION_VIEW_CONTAINER_ID = 'txn-confirm-screen';
-const CONFIRM_TRANSACTION_BUTTON_ID = 'txn-confirm-send-button';
-const NAVBAR_TITLE_TEXT = 'navbar-title-text';
-const TRANSACTION_ACCOUNT_BALANCE = 'account-balance';
export default class TransactionConfirmationView {
static async tapConfirmButton() {
- await TestHelpers.tap(CONFIRM_TRANSACTION_BUTTON_ID);
+ await TestHelpers.waitAndTap(CONFIRM_TRANSACTION_BUTTON_ID);
}
static async tapCancelButton() {
@@ -65,6 +67,7 @@ export default class TransactionConfirmationView {
static async isVisible() {
await TestHelpers.checkIfVisible(TRANSACTION_VIEW_CONTAINER_ID);
}
+
static async isNotVisible() {
await TestHelpers.checkIfNotVisible(TRANSACTION_VIEW_CONTAINER_ID);
}
diff --git a/e2e/resources/collectibles.json b/e2e/resources/collectibles.json
new file mode 100644
index 00000000000..cff686786cb
--- /dev/null
+++ b/e2e/resources/collectibles.json
@@ -0,0 +1,6 @@
+{
+ "erc1155tokenAddress": "0x306d717D109e0995e0f56027Eb93D9C1d5686dE1",
+ "erc1155tokenID": "179",
+ "erc1155collectionName": "Refinable721",
+ "erc1155tokenName": "Refinable721 #179"
+}
diff --git a/e2e/specs/add-custom-rpc.spec.js b/e2e/specs/add-custom-rpc.spec.js
index 624ff21dba6..0923d20479b 100644
--- a/e2e/specs/add-custom-rpc.spec.js
+++ b/e2e/specs/add-custom-rpc.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Regression } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
@@ -28,7 +29,7 @@ const XDAI_URL = 'https://rpc.gnosischain.com';
const MAINNET = 'Ethereum Main Network';
const PASSWORD = '12345678';
-describe('Custom RPC Tests', () => {
+describe(Regression('Custom RPC Tests'), () => {
beforeEach(() => {
jest.setTimeout(170000);
});
diff --git a/e2e/specs/addressbook-tests.spec.js b/e2e/specs/addressbook-tests.spec.js
index df3b8e4b812..904f0530977 100644
--- a/e2e/specs/addressbook-tests.spec.js
+++ b/e2e/specs/addressbook-tests.spec.js
@@ -1,4 +1,5 @@
'use strict';
+import { Smoke } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
import ProtectYourWalletView from '../pages/Onboarding/ProtectYourWalletView';
@@ -31,7 +32,7 @@ const MYTH_ADDRESS = '0x1FDb169Ef12954F20A15852980e1F0C122BfC1D6';
const MEMO = 'Test adding ENS';
const PASSWORD = '12345678';
-describe('Addressbook Tests', () => {
+describe(Smoke('Addressbook Tests'), () => {
beforeEach(() => {
jest.setTimeout(150000);
});
diff --git a/e2e/specs/browser-tests.spec.js b/e2e/specs/browser-tests.spec.js
index 8b9bb249b67..1cc2e49f42d 100644
--- a/e2e/specs/browser-tests.spec.js
+++ b/e2e/specs/browser-tests.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Smoke } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
@@ -26,7 +27,7 @@ const PASSWORD = '12345678';
const PHISHING_SITE = 'http://www.empowr.com/FanFeed/Home.aspx';
const INVALID_URL = 'https://quackquakc.easq';
-describe('Browser Tests', () => {
+describe(Smoke('Browser Tests'), () => {
beforeEach(() => {
jest.setTimeout(150000);
});
diff --git a/e2e/specs/confirmations/advanced-gas-fees.spec.js b/e2e/specs/confirmations/advanced-gas-fees.spec.js
index d1deb380d97..e92b6ee6b67 100644
--- a/e2e/specs/confirmations/advanced-gas-fees.spec.js
+++ b/e2e/specs/confirmations/advanced-gas-fees.spec.js
@@ -1,4 +1,6 @@
'use strict';
+import { Smoke } from '../../tags';
+
import WalletView from '../../pages/WalletView';
import SendView from '../../pages/SendView';
import AmountView from '../../pages/AmountView';
@@ -12,7 +14,7 @@ import WalletActionsModal from '../../pages/modals/WalletActionsModal';
const VALID_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb';
-describe('Advanced Gas Fees and Priority Tests', () => {
+describe(Smoke('Advanced Gas Fees and Priority Tests'), () => {
beforeEach(() => {
jest.setTimeout(170000);
});
diff --git a/e2e/specs/confirmations/send-eth-flow.spec.js b/e2e/specs/confirmations/send-eth-flow.spec.js
index 0aa1c43d8d4..b295ba75ea2 100644
--- a/e2e/specs/confirmations/send-eth-flow.spec.js
+++ b/e2e/specs/confirmations/send-eth-flow.spec.js
@@ -1,5 +1,6 @@
'use strict';
+import { Smoke } from '../../tags';
import AmountView from '../../pages/AmountView';
import SendView from '../../pages/SendView';
import TransactionConfirmationView from '../../pages/TransactionConfirmView';
@@ -16,7 +17,7 @@ import Ganache from '../../../app/util/test/ganache';
const validAccount = Accounts.getValidAccount();
const MYTH_ADDRESS = '0x1FDb169Ef12954F20A15852980e1F0C122BfC1D6';
-describe('Send ETH Tests', () => {
+describe(Smoke('Send ETH Tests'), () => {
let ganacheServer;
beforeAll(async () => {
jest.setTimeout(150000);
diff --git a/e2e/specs/confirmations/send-multisig-address-ios.spec.js b/e2e/specs/confirmations/send-multisig-address-ios.spec.js
index 8b7d2108985..4e1cd00145b 100644
--- a/e2e/specs/confirmations/send-multisig-address-ios.spec.js
+++ b/e2e/specs/confirmations/send-multisig-address-ios.spec.js
@@ -1,4 +1,6 @@
'use strict';
+import { Regression } from '../../tags';
+
import WalletView from '../../pages/WalletView';
import DrawerView from '../../pages/Drawer/DrawerView';
import SendView from '../../pages/SendView';
@@ -11,7 +13,7 @@ import {
const MULTISIG_ADDRESS = '0x0C1DD822d1Ddf78b0b702df7BF9fD0991D6255A1';
-describe('Send to multisig address on iOS', () => {
+describe(Regression('Send to multisig address on iOS'), () => {
beforeEach(() => {
jest.setTimeout(170000);
});
diff --git a/e2e/specs/deeplinks.spec.js b/e2e/specs/deeplinks.spec.js
index eae0faf7e93..74077743bba 100644
--- a/e2e/specs/deeplinks.spec.js
+++ b/e2e/specs/deeplinks.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Regression } from '../tags';
import ConnectModal from '../pages/modals/ConnectModal';
import NetworkApprovalModal from '../pages/modals/NetworkApprovalModal';
@@ -39,7 +40,7 @@ const networkErrorBodyMessage =
const validAccount = Accounts.getValidAccount();
-describe('Deep linking Tests', () => {
+describe(Regression('Deep linking Tests'), () => {
beforeEach(() => {
jest.setTimeout(150000);
});
diff --git a/e2e/specs/delete-wallet.spec.js b/e2e/specs/delete-wallet.spec.js
index b3bf99d1e0d..ffa6353451b 100644
--- a/e2e/specs/delete-wallet.spec.js
+++ b/e2e/specs/delete-wallet.spec.js
@@ -1,6 +1,7 @@
'use strict';
import TestHelpers from '../helpers';
+import { Smoke } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
@@ -24,123 +25,128 @@ import EnableAutomaticSecurityChecksView from '../pages/EnableAutomaticSecurityC
import { acceptTermOfUse } from '../viewHelper';
import Accounts from '../../wdio/helpers/Accounts';
-describe('Import wallet with 24 word SRP, change password then delete wallet flow', () => {
- let validAccount;
-
- beforeAll(() => {
- validAccount = Accounts.getValidAccount();
- });
-
- beforeEach(() => {
- jest.setTimeout(150000);
- });
-
- it('should go to import wallet view', async () => {
- await OnboardingCarouselView.isVisible();
- await OnboardingCarouselView.tapOnGetStartedButton();
-
- await OnboardingView.isVisible();
- await OnboardingView.tapImportWalletFromSeedPhrase();
-
- await MetaMetricsOptIn.isVisible();
- await MetaMetricsOptIn.tapAgreeButton();
- await acceptTermOfUse();
-
- await ImportWalletView.isVisible();
- });
-
- it('should import wallet with valid secret recovery phrase and password', async () => {
- await ImportWalletView.clearSecretRecoveryPhraseInputBox();
- await ImportWalletView.enterSecretRecoveryPhrase(validAccount.seedPhrase);
- await ImportWalletView.enterPassword(validAccount.password);
- await ImportWalletView.reEnterPassword(validAccount.password);
- });
- it('Should dismiss Automatic Security checks screen', async () => {
- await TestHelpers.delay(3500);
- await EnableAutomaticSecurityChecksView.isVisible();
- await EnableAutomaticSecurityChecksView.tapNoThanks();
- });
-
- it('should dismiss the onboarding wizard', async () => {
- // dealing with flakiness on bitrise.
- await TestHelpers.delay(1000);
- try {
- await OnboardingWizardModal.isVisible();
- await OnboardingWizardModal.tapNoThanksButton();
- await OnboardingWizardModal.isNotVisible();
- } catch {
- //
- }
- });
-
- it('should tap on "Got it" to dimiss the whats new modal', async () => {
- // dealing with flakiness on bitrise.
- await TestHelpers.delay(2500);
- try {
- await WhatsNewModal.isVisible();
- await WhatsNewModal.tapCloseButton();
- } catch {
- //
- }
- });
-
- it('should go to settings then security & privacy', async () => {
- // Open Drawer
- await WalletView.tapDrawerButton(); // tapping burger menu
-
- await DrawerView.isVisible();
- await DrawerView.tapSettings();
-
- await SettingsView.tapSecurityAndPrivacy();
- await SecurityAndPrivacyView.scrollToChangePasswordView();
-
- await SecurityAndPrivacyView.isChangePasswordSectionVisible();
- });
-
- it('should confirm password before changing it', async () => {
- await SecurityAndPrivacyView.tapChangePasswordButton();
-
- await ChangePasswordView.isVisible();
- await ChangePasswordView.typeInConfirmPasswordInputBox(
- validAccount.password,
- );
- //await ChangePasswordView.tapConfirmButton();
- });
-
- it('should change the password', async () => {
- const NEW_PASSWORD = '11111111';
- await ChangePasswordView.enterPassword(NEW_PASSWORD);
- await ChangePasswordView.reEnterPassword(NEW_PASSWORD);
- await ChangePasswordView.tapIUnderstandCheckBox();
-
- await ChangePasswordView.tapResetPasswordButton();
- });
-
- it('should open drawer and log out', async () => {
- await device.disableSynchronization(); // because the SRP tutorial video prevents the test from moving forward
- await SecurityAndPrivacyView.tapBackButton();
- await device.enableSynchronization();
-
- await SettingsView.tapCloseButton();
-
- await WalletView.tapDrawerButton();
-
- await DrawerView.isVisible();
- await DrawerView.tapLockAccount();
- await DrawerView.tapYesAlertButton();
- await LoginView.isVisible();
- });
-
- it('should tap reset wallet button', async () => {
- await LoginView.tapResetWalletButton();
-
- await DeleteWalletModal.isVisible();
- });
- it('should delete wallet', async () => {
- await DeleteWalletModal.tapIUnderstandButton();
- await DeleteWalletModal.typeDeleteInInputBox();
- await DeleteWalletModal.tapDeleteMyWalletButton();
- await TestHelpers.delay(2000);
- await OnboardingView.isVisible();
- });
-});
+describe(
+ Smoke(
+ 'Import wallet with 24 word SRP, change password then delete wallet flow',
+ ),
+ () => {
+ let validAccount;
+
+ beforeAll(() => {
+ validAccount = Accounts.getValidAccount();
+ });
+
+ beforeEach(() => {
+ jest.setTimeout(150000);
+ });
+
+ it('should go to import wallet view', async () => {
+ await OnboardingCarouselView.isVisible();
+ await OnboardingCarouselView.tapOnGetStartedButton();
+
+ await OnboardingView.isVisible();
+ await OnboardingView.tapImportWalletFromSeedPhrase();
+
+ await MetaMetricsOptIn.isVisible();
+ await MetaMetricsOptIn.tapAgreeButton();
+ await acceptTermOfUse();
+
+ await ImportWalletView.isVisible();
+ });
+
+ it('should import wallet with valid secret recovery phrase and password', async () => {
+ await ImportWalletView.clearSecretRecoveryPhraseInputBox();
+ await ImportWalletView.enterSecretRecoveryPhrase(validAccount.seedPhrase);
+ await ImportWalletView.enterPassword(validAccount.password);
+ await ImportWalletView.reEnterPassword(validAccount.password);
+ });
+ it('Should dismiss Automatic Security checks screen', async () => {
+ await TestHelpers.delay(3500);
+ await EnableAutomaticSecurityChecksView.isVisible();
+ await EnableAutomaticSecurityChecksView.tapNoThanks();
+ });
+
+ it('should dismiss the onboarding wizard', async () => {
+ // dealing with flakiness on bitrise.
+ await TestHelpers.delay(1000);
+ try {
+ await OnboardingWizardModal.isVisible();
+ await OnboardingWizardModal.tapNoThanksButton();
+ await OnboardingWizardModal.isNotVisible();
+ } catch {
+ //
+ }
+ });
+
+ it('should tap on "Got it" to dimiss the whats new modal', async () => {
+ // dealing with flakiness on bitrise.
+ await TestHelpers.delay(2500);
+ try {
+ await WhatsNewModal.isVisible();
+ await WhatsNewModal.tapCloseButton();
+ } catch {
+ //
+ }
+ });
+
+ it('should go to settings then security & privacy', async () => {
+ // Open Drawer
+ await WalletView.tapDrawerButton(); // tapping burger menu
+
+ await DrawerView.isVisible();
+ await DrawerView.tapSettings();
+
+ await SettingsView.tapSecurityAndPrivacy();
+ await SecurityAndPrivacyView.scrollToChangePasswordView();
+
+ await SecurityAndPrivacyView.isChangePasswordSectionVisible();
+ });
+
+ it('should confirm password before changing it', async () => {
+ await SecurityAndPrivacyView.tapChangePasswordButton();
+
+ await ChangePasswordView.isVisible();
+ await ChangePasswordView.typeInConfirmPasswordInputBox(
+ validAccount.password,
+ );
+ //await ChangePasswordView.tapConfirmButton();
+ });
+
+ it('should change the password', async () => {
+ const NEW_PASSWORD = '11111111';
+ await ChangePasswordView.enterPassword(NEW_PASSWORD);
+ await ChangePasswordView.reEnterPassword(NEW_PASSWORD);
+ await ChangePasswordView.tapIUnderstandCheckBox();
+
+ await ChangePasswordView.tapResetPasswordButton();
+ });
+
+ it('should open drawer and log out', async () => {
+ await device.disableSynchronization(); // because the SRP tutorial video prevents the test from moving forward
+ await SecurityAndPrivacyView.tapBackButton();
+ await device.enableSynchronization();
+
+ await SettingsView.tapCloseButton();
+
+ await WalletView.tapDrawerButton();
+
+ await DrawerView.isVisible();
+ await DrawerView.tapLockAccount();
+ await DrawerView.tapYesAlertButton();
+ await LoginView.isVisible();
+ });
+
+ it('should tap reset wallet button', async () => {
+ await LoginView.tapResetWalletButton();
+
+ await DeleteWalletModal.isVisible();
+ });
+ it('should delete wallet', async () => {
+ await DeleteWalletModal.tapIUnderstandButton();
+ await DeleteWalletModal.typeDeleteInInputBox();
+ await DeleteWalletModal.tapDeleteMyWalletButton();
+ await TestHelpers.delay(2000);
+ await OnboardingView.isVisible();
+ });
+ },
+);
diff --git a/e2e/specs/onboarding-wizard-opt-in.spec.js b/e2e/specs/onboarding-wizard-opt-in.spec.js
index 5cad9d44d0b..ff61f9d2820 100644
--- a/e2e/specs/onboarding-wizard-opt-in.spec.js
+++ b/e2e/specs/onboarding-wizard-opt-in.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Regression } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
@@ -25,133 +26,136 @@ import { acceptTermOfUse } from '../viewHelper';
const PASSWORD = '12345678';
-describe('Onboarding wizard opt-in, metametrics opt out from settings', () => {
- it('should be able to opt-in of the onboarding-wizard', async () => {
- await OnboardingCarouselView.isVisible();
- await OnboardingCarouselView.tapOnGetStartedButton();
-
- await OnboardingView.isVisible();
- await OnboardingView.tapCreateWallet();
-
- await MetaMetricsOptIn.isVisible();
- await MetaMetricsOptIn.tapAgreeButton();
- await acceptTermOfUse();
-
- await CreatePasswordView.isVisible();
- });
- it('should be able to create a new wallet', async () => {
- await CreatePasswordView.enterPassword(PASSWORD);
- await CreatePasswordView.reEnterPassword(PASSWORD);
- await CreatePasswordView.tapIUnderstandCheckBox();
- await CreatePasswordView.tapCreatePasswordButton();
- });
-
- it('Should skip backup check', async () => {
- // Check that we are on the Secure your wallet screen
- await ProtectYourWalletView.isVisible();
- await ProtectYourWalletView.tapOnRemindMeLaterButton();
-
- await SkipAccountSecurityModal.tapIUnderstandCheckBox();
- await SkipAccountSecurityModal.tapSkipButton();
- await WalletView.isVisible();
- });
-
- it('Should dismiss Automatic Security checks screen', async () => {
- await TestHelpers.delay(3500);
- await EnableAutomaticSecurityChecksView.isVisible();
- await EnableAutomaticSecurityChecksView.tapNoThanks();
- });
-
- it('should dismiss the onboarding wizard', async () => {
- // dealing with flakiness on bitrise.
- await TestHelpers.delay(1000);
- try {
- await OnboardingWizardModal.isVisible();
- await OnboardingWizardModal.tapNoThanksButton();
- await OnboardingWizardModal.isNotVisible();
- } catch {
- //
- }
- });
-
- it('should tap on "Got it" Button in the whats new modal', async () => {
- // dealing with flakiness on bitrise.
- await TestHelpers.delay(2500);
- try {
- await WhatsNewModal.isVisible();
- await WhatsNewModal.tapCloseButton();
- } catch {
- //
- }
- });
-
- it('should dismiss the protect your wallet modal', async () => {
- await ProtectYourWalletModal.isCollapsedBackUpYourWalletModalVisible();
- await TestHelpers.delay(1000);
-
- await ProtectYourWalletModal.tapRemindMeLaterButton();
-
- await SkipAccountSecurityModal.tapIUnderstandCheckBox();
- await SkipAccountSecurityModal.tapSkipButton();
-
- await WalletView.isVisible();
- });
-
- it('should check that metametrics is enabled in settings', async () => {
- await WalletView.tapDrawerButton(); // tapping burger menu
-
- await DrawerView.isVisible();
- await DrawerView.tapSettings();
-
- await SettingsView.tapSecurityAndPrivacy();
-
- await SecurityAndPrivacy.scrollToBottomOfView();
- TestHelpers.delay(2000);
- await SecurityAndPrivacy.isMetaMetricsToggleOn();
-
- TestHelpers.delay(1500);
- });
-
- it('should disable metametrics', async () => {
- await SecurityAndPrivacy.tapMetaMetricsToggle();
- await SecurityAndPrivacy.isMetaMetricsToggleOff();
-
- TestHelpers.delay(1500);
- await SecurityAndPrivacy.tapOKAlertButton();
- await SecurityAndPrivacy.isMetaMetricsToggleOff();
- });
-
- it('should relaunch the app and log in', async () => {
- // Relaunch app
- await TestHelpers.relaunchApp();
-
- await LoginView.isVisible();
- await LoginView.enterPassword(PASSWORD);
-
- await WalletView.isVisible();
- });
-
- it('should dismiss the onboarding wizard after logging in', async () => {
- // dealing with flakiness on bitrise.
- await TestHelpers.delay(1000);
- try {
- await OnboardingWizardModal.isVisible();
- await OnboardingWizardModal.tapNoThanksButton();
- await OnboardingWizardModal.isNotVisible();
- } catch {
- //
- }
- });
-
- it('should verify metametrics is turned off', async () => {
- await WalletView.tapDrawerButton(); // tapping burger menu
-
- await DrawerView.isVisible();
- await DrawerView.tapSettings();
-
- await SettingsView.tapSecurityAndPrivacy();
-
- await SecurityAndPrivacy.scrollToBottomOfView();
- await SecurityAndPrivacy.isMetaMetricsToggleOff();
- });
-});
+describe(
+ Regression('Onboarding wizard opt-in, metametrics opt out from settings'),
+ () => {
+ it('should be able to opt-in of the onboarding-wizard', async () => {
+ await OnboardingCarouselView.isVisible();
+ await OnboardingCarouselView.tapOnGetStartedButton();
+
+ await OnboardingView.isVisible();
+ await OnboardingView.tapCreateWallet();
+
+ await MetaMetricsOptIn.isVisible();
+ await MetaMetricsOptIn.tapAgreeButton();
+ await acceptTermOfUse();
+
+ await CreatePasswordView.isVisible();
+ });
+ it('should be able to create a new wallet', async () => {
+ await CreatePasswordView.enterPassword(PASSWORD);
+ await CreatePasswordView.reEnterPassword(PASSWORD);
+ await CreatePasswordView.tapIUnderstandCheckBox();
+ await CreatePasswordView.tapCreatePasswordButton();
+ });
+
+ it('Should skip backup check', async () => {
+ // Check that we are on the Secure your wallet screen
+ await ProtectYourWalletView.isVisible();
+ await ProtectYourWalletView.tapOnRemindMeLaterButton();
+
+ await SkipAccountSecurityModal.tapIUnderstandCheckBox();
+ await SkipAccountSecurityModal.tapSkipButton();
+ await WalletView.isVisible();
+ });
+
+ it('Should dismiss Automatic Security checks screen', async () => {
+ await TestHelpers.delay(3500);
+ await EnableAutomaticSecurityChecksView.isVisible();
+ await EnableAutomaticSecurityChecksView.tapNoThanks();
+ });
+
+ it('should dismiss the onboarding wizard', async () => {
+ // dealing with flakiness on bitrise.
+ await TestHelpers.delay(1000);
+ try {
+ await OnboardingWizardModal.isVisible();
+ await OnboardingWizardModal.tapNoThanksButton();
+ await OnboardingWizardModal.isNotVisible();
+ } catch {
+ //
+ }
+ });
+
+ it('should tap on "Got it" Button in the whats new modal', async () => {
+ // dealing with flakiness on bitrise.
+ await TestHelpers.delay(2500);
+ try {
+ await WhatsNewModal.isVisible();
+ await WhatsNewModal.tapCloseButton();
+ } catch {
+ //
+ }
+ });
+
+ it('should dismiss the protect your wallet modal', async () => {
+ await ProtectYourWalletModal.isCollapsedBackUpYourWalletModalVisible();
+ await TestHelpers.delay(1000);
+
+ await ProtectYourWalletModal.tapRemindMeLaterButton();
+
+ await SkipAccountSecurityModal.tapIUnderstandCheckBox();
+ await SkipAccountSecurityModal.tapSkipButton();
+
+ await WalletView.isVisible();
+ });
+
+ it('should check that metametrics is enabled in settings', async () => {
+ await WalletView.tapDrawerButton(); // tapping burger menu
+
+ await DrawerView.isVisible();
+ await DrawerView.tapSettings();
+
+ await SettingsView.tapSecurityAndPrivacy();
+
+ await SecurityAndPrivacy.scrollToBottomOfView();
+ TestHelpers.delay(2000);
+ await SecurityAndPrivacy.isMetaMetricsToggleOn();
+
+ TestHelpers.delay(1500);
+ });
+
+ it('should disable metametrics', async () => {
+ await SecurityAndPrivacy.tapMetaMetricsToggle();
+ await SecurityAndPrivacy.isMetaMetricsToggleOff();
+
+ TestHelpers.delay(1500);
+ await SecurityAndPrivacy.tapOKAlertButton();
+ await SecurityAndPrivacy.isMetaMetricsToggleOff();
+ });
+
+ it('should relaunch the app and log in', async () => {
+ // Relaunch app
+ await TestHelpers.relaunchApp();
+
+ await LoginView.isVisible();
+ await LoginView.enterPassword(PASSWORD);
+
+ await WalletView.isVisible();
+ });
+
+ it('should dismiss the onboarding wizard after logging in', async () => {
+ // dealing with flakiness on bitrise.
+ await TestHelpers.delay(1000);
+ try {
+ await OnboardingWizardModal.isVisible();
+ await OnboardingWizardModal.tapNoThanksButton();
+ await OnboardingWizardModal.isNotVisible();
+ } catch {
+ //
+ }
+ });
+
+ it('should verify metametrics is turned off', async () => {
+ await WalletView.tapDrawerButton(); // tapping burger menu
+
+ await DrawerView.isVisible();
+ await DrawerView.tapSettings();
+
+ await SettingsView.tapSecurityAndPrivacy();
+
+ await SecurityAndPrivacy.scrollToBottomOfView();
+ await SecurityAndPrivacy.isMetaMetricsToggleOff();
+ });
+ },
+);
diff --git a/e2e/specs/permission-system-delete-wallet.spec.js b/e2e/specs/permission-system-delete-wallet.spec.js
index 57ff9336034..b101a7330d0 100644
--- a/e2e/specs/permission-system-delete-wallet.spec.js
+++ b/e2e/specs/permission-system-delete-wallet.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Regression } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import ProtectYourWalletView from '../pages/Onboarding/ProtectYourWalletView';
@@ -27,91 +28,93 @@ import {
const TEST_DAPP = 'https://metamask.github.io/test-dapp/';
const PASSWORD = '12345678';
-
-describe('Permission System: Deleting wallet after connecting to a dapp', () => {
- beforeEach(() => {
- jest.setTimeout(150000);
- });
-
- it('should import wallet and go to the wallet view', async () => {
- await importWalletWithRecoveryPhrase();
- });
-
- it('should navigate to browser', async () => {
- await TabBarComponent.tapBrowser();
- await Browser.isVisible();
- });
-
- it('should connect to the test dapp', async () => {
- await TestHelpers.delay(3000);
- // Tap on search in bottom navbar
- await Browser.tapUrlInputBox();
- await Browser.navigateToURL(TEST_DAPP);
- await TestHelpers.delay(3000);
- await TestHelpers.tapAtPoint(
- BROWSER_SCREEN_ID,
- testDappConnectButtonCooridinates,
- );
- await ConnectModal.isVisible();
- await ConnectModal.tapConnectButton();
- });
-
- it('should navigate to wallet view', async () => {
- await TestHelpers.delay(3000);
- await TabBarComponent.tapWallet();
- // Check that we are on the browser screen
- await WalletView.isVisible();
- });
-
- it('should open drawer and log out', async () => {
- await WalletView.tapDrawerButton();
- await DrawerView.isVisible();
- await DrawerView.tapLockAccount();
- await DrawerView.tapYesAlertButton();
- await LoginView.isVisible();
- });
-
- it('should tap reset wallet button', async () => {
- await LoginView.tapResetWalletButton();
-
- await DeleteWalletModal.isVisible();
- });
- it('should delete wallet', async () => {
- await DeleteWalletModal.tapIUnderstandButton();
- await DeleteWalletModal.typeDeleteInInputBox();
- await DeleteWalletModal.tapDeleteMyWalletButton();
- await OnboardingView.isDeleteWalletToastVisible();
- });
-
- it('should create new wallet', async () => {
- await OnboardingView.deleteWalletToastisNotVisible();
- await OnboardingView.tapCreateWallet();
-
- await CreatePasswordView.isVisible();
- await CreatePasswordView.enterPassword(PASSWORD);
- await CreatePasswordView.reEnterPassword(PASSWORD);
- await CreatePasswordView.tapIUnderstandCheckBox();
- await CreatePasswordView.tapCreatePasswordButton();
- });
-
- it('Should skip backup check', async () => {
- await ProtectYourWalletView.isVisible();
- await ProtectYourWalletView.tapOnRemindMeLaterButton();
-
- await SkipAccountSecurityModal.tapIUnderstandCheckBox();
- await SkipAccountSecurityModal.tapSkipButton();
- await WalletView.isVisible();
- });
-
- it('should go to browser', async () => {
- await TabBarComponent.tapBrowser();
- await Browser.isVisible();
- });
-
- it('should no longer be connected to the dapp', async () => {
- await Browser.tapNetworkAvatarButtonOnBrowser();
- await ConnectedAccountsModal.isNotVisible();
- await NetworkListModal.isVisible();
- await NetworkListModal.tapNetworkListCloseIcon();
- });
-});
+describe(
+ Regression('Onboarding wizard opt-in, metametrics opt out from settings'),
+ () => {
+ beforeEach(() => {
+ jest.setTimeout(150000);
+ });
+
+ it('should import wallet and go to the wallet view', async () => {
+ await importWalletWithRecoveryPhrase();
+ });
+
+ it('should navigate to browser', async () => {
+ await TabBarComponent.tapBrowser();
+ await Browser.isVisible();
+ });
+
+ it('should connect to the test dapp', async () => {
+ await TestHelpers.delay(3000);
+ // Tap on search in bottom navbar
+ await Browser.tapUrlInputBox();
+ await Browser.navigateToURL(TEST_DAPP);
+ await TestHelpers.delay(3000);
+ await TestHelpers.tapAtPoint(
+ BROWSER_SCREEN_ID,
+ testDappConnectButtonCooridinates,
+ );
+ await ConnectModal.isVisible();
+ await ConnectModal.tapConnectButton();
+ });
+
+ it('should navigate to wallet view', async () => {
+ await TestHelpers.delay(3000);
+ await TabBarComponent.tapWallet();
+ // Check that we are on the browser screen
+ await WalletView.isVisible();
+ });
+
+ it('should open drawer and log out', async () => {
+ await WalletView.tapDrawerButton();
+ await DrawerView.isVisible();
+ await DrawerView.tapLockAccount();
+ await DrawerView.tapYesAlertButton();
+ await LoginView.isVisible();
+ });
+
+ it('should tap reset wallet button', async () => {
+ await LoginView.tapResetWalletButton();
+
+ await DeleteWalletModal.isVisible();
+ });
+ it('should delete wallet', async () => {
+ await DeleteWalletModal.tapIUnderstandButton();
+ await DeleteWalletModal.typeDeleteInInputBox();
+ await DeleteWalletModal.tapDeleteMyWalletButton();
+ await OnboardingView.isDeleteWalletToastVisible();
+ });
+
+ it('should create new wallet', async () => {
+ await OnboardingView.deleteWalletToastisNotVisible();
+ await OnboardingView.tapCreateWallet();
+
+ await CreatePasswordView.isVisible();
+ await CreatePasswordView.enterPassword(PASSWORD);
+ await CreatePasswordView.reEnterPassword(PASSWORD);
+ await CreatePasswordView.tapIUnderstandCheckBox();
+ await CreatePasswordView.tapCreatePasswordButton();
+ });
+
+ it('Should skip backup check', async () => {
+ await ProtectYourWalletView.isVisible();
+ await ProtectYourWalletView.tapOnRemindMeLaterButton();
+
+ await SkipAccountSecurityModal.tapIUnderstandCheckBox();
+ await SkipAccountSecurityModal.tapSkipButton();
+ await WalletView.isVisible();
+ });
+
+ it('should go to browser', async () => {
+ await TabBarComponent.tapBrowser();
+ await Browser.isVisible();
+ });
+
+ it('should no longer be connected to the dapp', async () => {
+ await Browser.tapNetworkAvatarButtonOnBrowser();
+ await ConnectedAccountsModal.isNotVisible();
+ await NetworkListModal.isVisible();
+ await NetworkListModal.tapNetworkListCloseIcon();
+ });
+ },
+);
diff --git a/e2e/specs/permission-system-removing-imported-account.spec.js b/e2e/specs/permission-system-removing-imported-account.spec.js
index 2b6a275cd64..acd4e5c1751 100644
--- a/e2e/specs/permission-system-removing-imported-account.spec.js
+++ b/e2e/specs/permission-system-removing-imported-account.spec.js
@@ -1,6 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
-
+import { Smoke } from '../tags';
import WalletView from '../pages/WalletView';
import ImportAccountView from '../pages/ImportAccountView';
import TabBarComponent from '../pages/TabBarComponent';
@@ -24,108 +24,110 @@ import {
} from '../viewHelper';
const TEST_DAPP = 'https://metamask.github.io/test-dapp/';
-const GOERLI = 'Goerli Test Network';
+const SEPOLIA = 'Sepolia Test Network';
const accountPrivateKey = Accounts.getAccountPrivateKey();
-
-describe('Permission System Test: Revoking accounts after connecting to a dapp', () => {
- beforeEach(() => {
- jest.setTimeout(150000);
- });
-
- it('should import wallet and go to the wallet view', async () => {
- await importWalletWithRecoveryPhrase();
- });
-
- it('should navigate to browser', async () => {
- await TestHelpers.delay(2000);
- await TabBarComponent.tapBrowser();
- await Browser.isVisible();
- });
-
- it('should connect to the test dapp', async () => {
- await TestHelpers.delay(3000);
- await Browser.tapUrlInputBox();
- await Browser.navigateToURL(TEST_DAPP);
- await TestHelpers.delay(3000);
- await TestHelpers.tapAtPoint(
- BROWSER_SCREEN_ID,
- testDappConnectButtonCooridinates,
- );
- await ConnectModal.isVisible();
- });
-
- it('should go to multiconnect in the connect account modal', async () => {
- await ConnectModal.tapConnectMultipleAccountsButton();
- });
-
- it('should import account', async () => {
- await ConnectModal.tapImportAccountButton();
- await ImportAccountView.isVisible();
- await ImportAccountView.enterPrivateKey(accountPrivateKey.keys);
- await ImportAccountView.isImportSuccessSreenVisible();
- await ImportAccountView.tapCloseButtonOnImportSuccess();
- });
-
- it('should connect multiple accounts to a dapp', async () => {
- await ConnectModal.tapSelectAllButton();
-
- await ConnectModal.tapAccountConnectMultiSelectButton();
- });
-
- it('should switch to Goreli', async () => {
- await Browser.tapNetworkAvatarButtonOnBrowser();
- await ConnectedAccountsModal.tapNetworksPicker();
- await NetworkListModal.changeNetwork(GOERLI);
- });
-
- it('should dismiss the network education modal', async () => {
- await NetworkEducationModal.isVisible();
- await NetworkEducationModal.tapGotItButton();
- await NetworkEducationModal.isNotVisible();
- });
-
- it('should set the imported account as primary account', async () => {
- await TestHelpers.delay(1500);
- await ConnectedAccountsModal.tapToSetAsPrimaryAccount();
- });
-
- it('should submit a EIP1559 transaction ', async () => {
- await TestHelpers.swipe(BROWSER_SCREEN_ID, 'up', 'slow', 0.1);
- await TestHelpers.tapAtPoint(
- BROWSER_SCREEN_ID,
- testDappSendEIP1559ButtonCoordinates,
- );
-
- await TransactionConfirmationView.isBalanceVisible();
- await TestHelpers.tapByText('Confirm', 1);
- await TransactionConfirmationView.isBalanceNotVisible();
- });
-
- it('should navigate to wallet view', async () => {
- await TabBarComponent.tapWallet();
- await WalletView.isVisible();
- });
-
- it('should remove imported account', async () => {
- // Wait for page to load
- await WalletView.tapIdenticon();
- await AccountListView.isVisible();
- await AccountListView.longPressImportedAccount();
- await AccountListView.tapYesToRemoveImportedAccountAlertButton();
- await AccountListView.accountNameNotVisible('Account 2');
- });
-
- it('should return to browser', async () => {
- await AccountListView.swipeToDimssAccountsModal();
- await TabBarComponent.tapBrowser();
- // Check that we are on the browser screen
- await Browser.isVisible();
- });
-
- it('imported account is not visible', async () => {
- await Browser.tapNetworkAvatarButtonOnBrowser();
- await ConnectedAccountsModal.isVisible();
- await AccountListView.accountNameNotVisible('Account 2');
- });
-});
+describe(
+ Smoke('Permission System Test: Revoking accounts after connecting to a dapp'),
+ () => {
+ beforeEach(() => {
+ jest.setTimeout(150000);
+ });
+
+ it('should import wallet and go to the wallet view', async () => {
+ await importWalletWithRecoveryPhrase();
+ });
+
+ it('should navigate to browser', async () => {
+ await TestHelpers.delay(2000);
+ await TabBarComponent.tapBrowser();
+ await Browser.isVisible();
+ });
+
+ it('should connect to the test dapp', async () => {
+ await TestHelpers.delay(3000);
+ await Browser.tapUrlInputBox();
+ await Browser.navigateToURL(TEST_DAPP);
+ await TestHelpers.delay(3000);
+ await TestHelpers.tapAtPoint(
+ BROWSER_SCREEN_ID,
+ testDappConnectButtonCooridinates,
+ );
+ await ConnectModal.isVisible();
+ });
+
+ it('should go to multiconnect in the connect account modal', async () => {
+ await ConnectModal.tapConnectMultipleAccountsButton();
+ });
+
+ it('should import account', async () => {
+ await ConnectModal.tapImportAccountButton();
+ await ImportAccountView.isVisible();
+ await ImportAccountView.enterPrivateKey(accountPrivateKey.keys);
+ await ImportAccountView.isImportSuccessSreenVisible();
+ await ImportAccountView.tapCloseButtonOnImportSuccess();
+ });
+
+ it('should connect multiple accounts to a dapp', async () => {
+ await ConnectModal.tapSelectAllButton();
+
+ await ConnectModal.tapAccountConnectMultiSelectButton();
+ });
+
+ it('should switch to Sepolia', async () => {
+ await Browser.tapNetworkAvatarButtonOnBrowser();
+ await ConnectedAccountsModal.tapNetworksPicker();
+ await NetworkListModal.changeNetwork(SEPOLIA);
+ });
+
+ it('should dismiss the network education modal', async () => {
+ await NetworkEducationModal.isVisible();
+ await NetworkEducationModal.tapGotItButton();
+ await NetworkEducationModal.isNotVisible();
+ });
+
+ it('should set the imported account as primary account', async () => {
+ await TestHelpers.delay(1500);
+ await ConnectedAccountsModal.tapToSetAsPrimaryAccount();
+ });
+
+ it('should submit a EIP1559 transaction ', async () => {
+ await TestHelpers.swipe(BROWSER_SCREEN_ID, 'up', 'slow', 0.1);
+ await TestHelpers.tapAtPoint(
+ BROWSER_SCREEN_ID,
+ testDappSendEIP1559ButtonCoordinates,
+ );
+
+ await TransactionConfirmationView.isBalanceVisible();
+ await TestHelpers.tapByText('Confirm', 1);
+ await TransactionConfirmationView.isBalanceNotVisible();
+ });
+
+ it('should navigate to wallet view', async () => {
+ await TabBarComponent.tapWallet();
+ await WalletView.isVisible();
+ });
+
+ it('should remove imported account', async () => {
+ // Wait for page to load
+ await WalletView.tapIdenticon();
+ await AccountListView.isVisible();
+ await AccountListView.longPressImportedAccount();
+ await AccountListView.tapYesToRemoveImportedAccountAlertButton();
+ await AccountListView.accountNameNotVisible('Account 2');
+ });
+
+ it('should return to browser', async () => {
+ await AccountListView.swipeToDimssAccountsModal();
+ await TabBarComponent.tapBrowser();
+ // Check that we are on the browser screen
+ await Browser.isVisible();
+ });
+
+ it('imported account is not visible', async () => {
+ await Browser.tapNetworkAvatarButtonOnBrowser();
+ await ConnectedAccountsModal.isVisible();
+ await AccountListView.accountNameNotVisible('Account 2');
+ });
+ },
+);
diff --git a/e2e/specs/permission-system-revoke-single-account.spec.js b/e2e/specs/permission-system-revoke-single-account.spec.js
index de02367ab41..7c6428fe043 100644
--- a/e2e/specs/permission-system-revoke-single-account.spec.js
+++ b/e2e/specs/permission-system-revoke-single-account.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Regression } from '../tags';
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
@@ -29,7 +30,7 @@ import {
const TEST_DAPP = 'https://metamask.github.io/test-dapp/';
const PASSWORD = '12345678';
-describe('Revoke Single Account after connecting to a dapp', () => {
+describe(Regression('Revoke Single Account after connecting to a dapp'), () => {
beforeEach(() => {
jest.setTimeout(150000);
});
diff --git a/e2e/specs/permission-system-revoking-multiple-accounts.spec.js b/e2e/specs/permission-system-revoking-multiple-accounts.spec.js
index 28e13fd4772..6cf94b06f9c 100644
--- a/e2e/specs/permission-system-revoking-multiple-accounts.spec.js
+++ b/e2e/specs/permission-system-revoking-multiple-accounts.spec.js
@@ -1,5 +1,6 @@
'use strict';
import TestHelpers from '../helpers';
+import { Regression } from '../tags';
import Browser from '../pages/Drawer/Browser';
import { BROWSER_SCREEN_ID } from '../../wdio/screen-objects/testIDs/BrowserScreen/BrowserScreen.testIds';
@@ -19,81 +20,86 @@ import {
const SUSHI_SWAP = 'https://app.sushi.com/swap';
const TEST_DAPP = 'https://metamask.github.io/test-dapp/';
-describe('Connecting to multiple dapps and revoking permission on one but staying connected to the other', () => {
- beforeEach(() => {
- jest.setTimeout(150000);
- });
-
- it('should import wallet and go to the wallet view', async () => {
- await importWalletWithRecoveryPhrase();
- });
-
- it('should navigate to browser', async () => {
- await TabBarComponent.tapBrowser();
- // Check that we are on the browser screen
- await Browser.isVisible();
- });
-
- it('should connect to sushi swap dapp', async () => {
- await TestHelpers.delay(3000);
- // Tap on search in bottom navbar
- await Browser.tapUrlInputBox();
- await Browser.navigateToURL(SUSHI_SWAP);
- await ConnectModal.isVisible();
- await ConnectModal.tapConnectButton();
- await Browser.isAccountToastVisible('Account 1');
- });
-
- it('should go to the test dapp', async () => {
- // Tap on search in bottom navbar
- await Browser.tapOpenAllTabsButton();
- await Browser.tapOpenNewTabButton();
- await Browser.tapUrlInputBox();
- await Browser.navigateToURL(TEST_DAPP);
- await Browser.waitForBrowserPageToLoad();
- await TestHelpers.tapAtPoint(
- BROWSER_SCREEN_ID,
- testDappConnectButtonCooridinates,
- );
- await ConnectModal.isVisible();
- });
-
- it('should go to multiconnect in the connect account modal', async () => {
- await ConnectModal.tapConnectMultipleAccountsButton();
- });
-
- it('should connect with multiple accounts', async () => {
- // Wait for page to load
- await ConnectModal.tapCreateAccountButton();
- await AccountListView.isNewAccountNameVisible();
- await AccountListView.tapAccountByName('Account 2');
-
- await ConnectModal.tapAccountConnectMultiSelectButton();
- });
-
- it('should revoke accounts', async () => {
- await Browser.tapNetworkAvatarButtonOnBrowser();
- await ConnectedAccountsModal.tapPermissionsButton();
- await TestHelpers.delay(1500);
- await ConnectedAccountsModal.tapRevokeAllButton();
- Browser.isRevokeAllAccountToastVisible();
- });
-
- it('should no longer be connected to the test dapp', async () => {
- await Browser.tapNetworkAvatarButtonOnBrowser();
- await ConnectedAccountsModal.isNotVisible();
- await NetworkListModal.tapNetworkListCloseIcon();
- });
-
- it('should open sushi swap dapp', async () => {
- // Wait for page to load
- await Browser.tapOpenAllTabsButton();
- await TestHelpers.tapByText('app.sushi.com');
- });
-
- it('should still be connected to sushi swap', async () => {
- // Wait for page to load
- await Browser.tapNetworkAvatarButtonOnBrowser();
- await ConnectedAccountsModal.isVisible();
- });
-});
+describe(
+ Regression(
+ 'Connecting to multiple dapps and revoking permission on one but staying connected to the other',
+ ),
+ () => {
+ beforeEach(() => {
+ jest.setTimeout(150000);
+ });
+
+ it('should import wallet and go to the wallet view', async () => {
+ await importWalletWithRecoveryPhrase();
+ });
+
+ it('should navigate to browser', async () => {
+ await TabBarComponent.tapBrowser();
+ // Check that we are on the browser screen
+ await Browser.isVisible();
+ });
+
+ it('should connect to sushi swap dapp', async () => {
+ await TestHelpers.delay(3000);
+ // Tap on search in bottom navbar
+ await Browser.tapUrlInputBox();
+ await Browser.navigateToURL(SUSHI_SWAP);
+ await ConnectModal.isVisible();
+ await ConnectModal.tapConnectButton();
+ await Browser.isAccountToastVisible('Account 1');
+ });
+
+ it('should go to the test dapp', async () => {
+ // Tap on search in bottom navbar
+ await Browser.tapOpenAllTabsButton();
+ await Browser.tapOpenNewTabButton();
+ await Browser.tapUrlInputBox();
+ await Browser.navigateToURL(TEST_DAPP);
+ await Browser.waitForBrowserPageToLoad();
+ await TestHelpers.tapAtPoint(
+ BROWSER_SCREEN_ID,
+ testDappConnectButtonCooridinates,
+ );
+ await ConnectModal.isVisible();
+ });
+
+ it('should go to multiconnect in the connect account modal', async () => {
+ await ConnectModal.tapConnectMultipleAccountsButton();
+ });
+
+ it('should connect with multiple accounts', async () => {
+ // Wait for page to load
+ await ConnectModal.tapCreateAccountButton();
+ await AccountListView.isNewAccountNameVisible();
+ await AccountListView.tapAccountByName('Account 2');
+
+ await ConnectModal.tapAccountConnectMultiSelectButton();
+ });
+
+ it('should revoke accounts', async () => {
+ await Browser.tapNetworkAvatarButtonOnBrowser();
+ await ConnectedAccountsModal.tapPermissionsButton();
+ await TestHelpers.delay(1500);
+ await ConnectedAccountsModal.tapRevokeAllButton();
+ Browser.isRevokeAllAccountToastVisible();
+ });
+
+ it('should no longer be connected to the test dapp', async () => {
+ await Browser.tapNetworkAvatarButtonOnBrowser();
+ await ConnectedAccountsModal.isNotVisible();
+ await NetworkListModal.tapNetworkListCloseIcon();
+ });
+
+ it('should open sushi swap dapp', async () => {
+ // Wait for page to load
+ await Browser.tapOpenAllTabsButton();
+ await TestHelpers.tapByText('app.sushi.com');
+ });
+
+ it('should still be connected to sushi swap', async () => {
+ // Wait for page to load
+ await Browser.tapNetworkAvatarButtonOnBrowser();
+ await ConnectedAccountsModal.isVisible();
+ });
+ },
+);
diff --git a/e2e/specs/request-token-flow.spec.js b/e2e/specs/request-token-flow.spec.js
index 1c1a9416a9b..8e3a1bb10de 100644
--- a/e2e/specs/request-token-flow.spec.js
+++ b/e2e/specs/request-token-flow.spec.js
@@ -1,4 +1,6 @@
'use strict';
+import { Smoke } from '../tags';
+
import OnboardingView from '../pages/Onboarding/OnboardingView';
import OnboardingCarouselView from '../pages/Onboarding/OnboardingCarouselView';
import ProtectYourWalletView from '../pages/Onboarding/ProtectYourWalletView';
@@ -24,7 +26,7 @@ import { acceptTermOfUse } from '../viewHelper';
const SAI_CONTRACT_ADDRESS = '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359';
const PASSWORD = '12345678';
-describe('Request Token Flow', () => {
+describe(Smoke('Request Token Flow'), () => {
beforeEach(() => {
jest.setTimeout(150000);
});
diff --git a/e2e/specs/send-ERC-token.spec.js b/e2e/specs/send-ERC-token.spec.js
index ca3f7138f80..255c6d35b83 100644
--- a/e2e/specs/send-ERC-token.spec.js
+++ b/e2e/specs/send-ERC-token.spec.js
@@ -1,4 +1,6 @@
'use strict';
+import { Smoke } from '../tags';
+
import TestHelpers from '../helpers';
import WalletView from '../pages/WalletView';
@@ -16,7 +18,7 @@ const AVAX_URL = 'https://api.avax-test.network/ext/C/rpc';
const TOKEN_ADDRESS = '0x5425890298aed601595a70AB815c96711a31Bc65';
const SEND_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb';
-describe('Send ERC Token', () => {
+describe(Smoke('Send ERC Token'), () => {
beforeAll(async () => {
await importWalletWithRecoveryPhrase();
});
diff --git a/e2e/specs/start-exploring.spec.js b/e2e/specs/start-exploring.spec.js
index c40541fbaa2..ac4dcfbb04a 100644
--- a/e2e/specs/start-exploring.spec.js
+++ b/e2e/specs/start-exploring.spec.js
@@ -1,4 +1,5 @@
'use strict';
+import { Smoke } from '../tags';
import TestHelpers from '../helpers';
@@ -21,7 +22,7 @@ import { acceptTermOfUse } from '../viewHelper';
const ACCOUNT = 'Test Account One';
const PASSWORD = '12345678';
-describe('Start Exploring', () => {
+describe(Smoke('Start Exploring'), () => {
beforeEach(() => {
jest.setTimeout(150000);
});
diff --git a/e2e/specs/wallet-tests.spec.js b/e2e/specs/wallet-tests.spec.js
index 720a488461b..b65f3c51d59 100644
--- a/e2e/specs/wallet-tests.spec.js
+++ b/e2e/specs/wallet-tests.spec.js
@@ -1,22 +1,21 @@
'use strict';
-import TestHelpers from '../helpers';
+import { Smoke } from '../tags';
+import TestHelpers from '../helpers';
import WalletView from '../pages/WalletView';
import AccountListView from '../pages/AccountListView';
import ImportAccountView from '../pages/ImportAccountView';
-
import DrawerView from '../pages/Drawer/DrawerView';
-
import AddCustomTokenView from '../pages/AddCustomTokenView';
import ImportTokensView from '../pages/ImportTokensView';
-
import NetworkListModal from '../pages/modals/NetworkListModal';
import RequestPaymentModal from '../pages/modals/RequestPaymentModal';
import NetworkEducationModal from '../pages/modals/NetworkEducationModal';
import { importWalletWithRecoveryPhrase } from '../viewHelper';
import Accounts from '../../wdio/helpers/Accounts';
+import Collectibles from '../resources/collectibles.json';
-describe('Wallet Tests', () => {
+describe(Smoke('Wallet Tests'), () => {
const GOERLI = 'Goerli Test Network';
const ETHEREUM = 'Ethereum Main Network';
@@ -24,9 +23,7 @@ describe('Wallet Tests', () => {
// I should NEVER hold any eth or token
const TEST_PRIVATE_KEY =
'cbfd798afcfd1fd8ecc48cbecb6dc7e876543395640b758a90e11d986e758ad1';
- const COLLECTIBLE_CONTRACT_ADDRESS =
- '0x306d717D109e0995e0f56027Eb93D9C1d5686dE1';
- const COLLECTIBLE_IDENTIFIER = '179';
+
const BLT_TOKEN_ADDRESS = '0x107c4504cd79c5d2696ea0030a8dd4e92601b82e';
const validAccount = Accounts.getValidAccount();
@@ -86,6 +83,7 @@ describe('Wallet Tests', () => {
await RequestPaymentModal.isPublicAddressCorrect(validAccount.address);
await RequestPaymentModal.closeRequestModal();
+
await WalletView.isVisible();
});
@@ -124,18 +122,18 @@ describe('Wallet Tests', () => {
await WalletView.tapImportNFTButton();
await AddCustomTokenView.isVisible();
- await AddCustomTokenView.typeInNFTAddress(COLLECTIBLE_CONTRACT_ADDRESS);
- await AddCustomTokenView.typeInNFTIdentifier(COLLECTIBLE_IDENTIFIER);
+ await AddCustomTokenView.typeInNFTAddress(Collectibles.erc1155tokenAddress);
+ await AddCustomTokenView.typeInNFTIdentifier(Collectibles.erc1155tokenID);
await WalletView.isVisible();
// Wait for asset to load
await TestHelpers.delay(3000);
- await WalletView.isNFTVisibleInWallet('Refinable721');
- // Tap on Crypto Kitty
- await WalletView.tapOnNFTInWallet('Refinable721');
+ await WalletView.isNFTVisibleInWallet(Collectibles.erc1155collectionName);
+ // Tap on Collectible
+ await WalletView.tapOnNFTInWallet(Collectibles.erc1155collectionName);
- await WalletView.isNFTNameVisible('Refinable721 #179');
+ await WalletView.isNFTNameVisible(Collectibles.erc1155tokenName);
await WalletView.scrollUpOnNFTsTab();
});
diff --git a/e2e/tags.js b/e2e/tags.js
new file mode 100644
index 00000000000..7e95c4c8713
--- /dev/null
+++ b/e2e/tags.js
@@ -0,0 +1,8 @@
+const tags = {
+ regression: 'Regression',
+ smoke: 'Smoke',
+};
+const Smoke = (testName) => `${tags.smoke} ${testName}`;
+const Regression = (testName) => `${tags.regression} ${testName}`;
+
+export { Smoke, Regression };
diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj
index dd29a2b6146..46a18c93bbd 100644
--- a/ios/MetaMask.xcodeproj/project.pbxproj
+++ b/ios/MetaMask.xcodeproj/project.pbxproj
@@ -1023,7 +1023,7 @@
CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 1100;
+ CURRENT_PROJECT_VERSION = 1104;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 48XVW22RCG;
@@ -1056,7 +1056,7 @@
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
LLVM_LTO = YES;
- MARKETING_VERSION = 6.4.0;
+ MARKETING_VERSION = 6.5.0;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = (
"$(inherited)",
@@ -1087,7 +1087,7 @@
CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 1100;
+ CURRENT_PROJECT_VERSION = 1104;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 48XVW22RCG;
ENABLE_BITCODE = NO;
@@ -1120,7 +1120,7 @@
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
LLVM_LTO = YES;
- MARKETING_VERSION = 6.4.0;
+ MARKETING_VERSION = 6.5.0;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = (
"$(inherited)",
@@ -1230,7 +1230,7 @@
CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 1100;
+ CURRENT_PROJECT_VERSION = 1104;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 48XVW22RCG;
@@ -1266,7 +1266,7 @@
"\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"",
);
LLVM_LTO = YES;
- MARKETING_VERSION = 6.4.0;
+ MARKETING_VERSION = 6.5.0;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = (
"$(inherited)",
@@ -1297,7 +1297,7 @@
CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 1100;
+ CURRENT_PROJECT_VERSION = 1104;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 48XVW22RCG;
ENABLE_BITCODE = NO;
@@ -1333,7 +1333,7 @@
"\"$(SRCROOT)/MetaMask/System/Library/Frameworks\"",
);
LLVM_LTO = YES;
- MARKETING_VERSION = 6.4.0;
+ MARKETING_VERSION = 6.5.0;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = (
"$(inherited)",
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 67ddcc8b9d3..a10ea2c56c9 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -820,11 +820,11 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
- boost: 57d2868c099736d80fcd648bf211b4431e51a558
+ boost: a7c83b31436843459a1961bfd74b96033dc77234
Branch: 4ac024cb3c29b0ef628048694db3c4cfa679beb0
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
- DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
+ DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: a83ceaa8a8581003a623facdb3c44f6d4f342ac5
FBReactNativeSpec: 85eee79837cb797ab6176f0243a2b40511c09158
Flipper: b1fddf9a17c32097b2b4c806ad158b2f36bb2692
@@ -837,7 +837,7 @@ SPEC CHECKSUMS:
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
FlipperKit: aec2d931adeee48a07bab1ea8bcc8a6bb87dfce4
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
- glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
+ glog: 5337263514dd6f09803962437687240c5dc39aa4
JitsiWebRTC: f441eb0e2d67f0588bf24e21c5162e97342714fb
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
lottie-ios: 016449b5d8be0c3dcbcfa0a9988469999cd04c5d
diff --git a/locales/languages/de.json b/locales/languages/de.json
index 3976bcec3e4..a63a3f79fa8 100644
--- a/locales/languages/de.json
+++ b/locales/languages/de.json
@@ -108,6 +108,37 @@
"content": "Suchen Sie nach Seiten oder geben Sie eine URL ein, wenn Sie wissen, wohin sie wollen."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Erstellen Ihres Wallets ...",
"subtitle": "Das sollte nicht lang dauern"
@@ -800,7 +831,30 @@
"buy_description": "Krypto mit Bargeld kaufen",
"swap_description": "Zwischen Tokens tauschen",
"send_description": "Krypto an ein beliebiges Konto senden",
- "receive_description": "Krypto erhalten"
+ "receive_description": "Krypto erhalten",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Kontodetails",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "hier entdecken.",
"you_can": "Sie können aber auch",
"add_network": "weitere Netzwerke manuell hinzufügen.",
- "deprecated_network_msg": "Aufgrund der Protokolländerungen von Ethereum: Rinkeby, Ropsten, Kovan funktionieren die Testnetzwerke möglicherweise nicht mehr so zuverlässig und werden bald veraltet sein.",
"select_network": "Netzwerk wählen"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView erhielt Fehler-Statuscode: {{code}}",
"no_tokens_available_title": "Keine Token verfügbar",
"no_tokens_available": "Derzeit gibt es keine Kryptowährungen, die in {{network}} in der {{region}} gekauft werden können.",
+ "this_network": "this network",
"change_payment_method": "Zahlungsmethode ändern",
"try_different_region": "Andere Region ausprobieren",
"return_home": "Zur Startseite zurückkehren",
diff --git a/locales/languages/el.json b/locales/languages/el.json
index 653d45f41e5..e39461b364c 100644
--- a/locales/languages/el.json
+++ b/locales/languages/el.json
@@ -108,6 +108,37 @@
"content": "Ψάξτε για ιστοσελίδες ή πληκτρολογήστε ένα URL εάν γνωρίζετε τον προορισμό σας."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Δημιουργούμε το πορτοφόλι σας...",
"subtitle": "Δεν θα πάρει πολύ"
@@ -800,7 +831,30 @@
"buy_description": "Αγοράστε κρυπτονομίσματα με μετρητά",
"swap_description": "Ανταλλαγή μεταξύ tokens",
"send_description": "Στείλτε κρυπτονομίσματα σε οποιονδήποτε λογαριασμό",
- "receive_description": "Λάβετε κρυπτονομίσματα"
+ "receive_description": "Λάβετε κρυπτονομίσματα",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Λεπτομέρειες Λογαριασμού",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "εδώ.",
"you_can": "Ή μπορείτε",
"add_network": "να προσθέσετε περισσότερα δίκτυα χειροκίνητα.",
- "deprecated_network_msg": "Λόγω των αλλαγών στο πρωτόκολλο του Ethereum: τα δοκιμαστικά δίκτυα Rinkeby, Ropsten, Kovan μπορεί να μη λειτουργούν και τόσο αξιόπιστα και θα καταργηθούν σύντομα.",
"select_network": "Επιλέξτε ένα δίκτυο"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "Το WebView έλαβε έναν κωδικό κατάστασης σφάλματος: {{code}}",
"no_tokens_available_title": "Κανένα διαθέσιμο Token",
"no_tokens_available": "Δεν υπάρχουν προς το παρόν διαθέσιμα token για αγορά στο {{network}} με την επιλεγμένη μέθοδο πληρωμής.",
+ "this_network": "this network",
"change_payment_method": "Αλλάξτε μέθοδο πληρωμής",
"try_different_region": "Δοκιμάστε μια άλλη περιοχή",
"return_home": "Επιστροφή στην Αρχική Σελίδα",
diff --git a/locales/languages/en.json b/locales/languages/en.json
index 539f2f92833..c257e403365 100644
--- a/locales/languages/en.json
+++ b/locales/languages/en.json
@@ -831,7 +831,30 @@
"buy_description": "Buy crypto with cash",
"swap_description": "Exchange between tokens",
"send_description": "Send crypto to any account",
- "receive_description": "Receive crypto"
+ "receive_description": "Receive crypto",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Account Details",
@@ -1818,6 +1841,7 @@
"webview_received_error": "WebView received error status code: {{code}}",
"no_tokens_available_title": "No Tokens Available",
"no_tokens_available": "There are currently no tokens available to purchase on {{network}} with the selected payment method.",
+ "this_network": "this network",
"change_payment_method": "Change payment method",
"try_different_region": "Try a different region",
"return_home": "Return to Home Screen",
diff --git a/locales/languages/es.json b/locales/languages/es.json
index ba643aac79b..e9b10bd9378 100644
--- a/locales/languages/es.json
+++ b/locales/languages/es.json
@@ -108,6 +108,37 @@
"content": "Busque los sitios o escriba una dirección URL si sabe adónde va."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Creando su cartera…",
"subtitle": "Este proceso no debería tardar mucho"
@@ -800,7 +831,30 @@
"buy_description": "Comprar criptomonedas con efectivo",
"swap_description": "Intercambiar entre tokens",
"send_description": "Enviar criptomonedas a cualquier cuenta",
- "receive_description": "Recibir criptomonedas"
+ "receive_description": "Recibir criptomonedas",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Detalles de la cuenta",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "aquí.",
"you_can": "O puede",
"add_network": "agregar más redes manualmente.",
- "deprecated_network_msg": "Debido a los cambios de protocolo de Ethereum: es posible que las redes de prueba de Rinkeby, Ropsten y Kovan no funcionen de manera tan confiable y queden obsoletas pronto.",
"select_network": "Seleccionar una red"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView recibió un código de estado de error: {{code}}",
"no_tokens_available_title": "No hay tokens disponibles",
"no_tokens_available": "En este momento no hay tokens disponibles para comprar en {{network}} con el método de pago seleccionado.",
+ "this_network": "this network",
"change_payment_method": "Cambiar el método de pago",
"try_different_region": "Pruebe una región distinta",
"return_home": "Volver a la pantalla de inicio",
diff --git a/locales/languages/fr.json b/locales/languages/fr.json
index b38fad43b03..9015a658a83 100644
--- a/locales/languages/fr.json
+++ b/locales/languages/fr.json
@@ -108,6 +108,37 @@
"content": "Recherchez des sites, ou saisissez une URL si vous connaissez l’adresse."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Création de votre portefeuille…",
"subtitle": "Cela ne devrait prendre que quelques instants."
@@ -800,7 +831,30 @@
"buy_description": "Acheter des crypto-actifs en payant en espèces",
"swap_description": "Échange de jetons",
"send_description": "Envoyer des crypto-actifs vers n’importe quel compte",
- "receive_description": "Recevoir des crypto-actifs"
+ "receive_description": "Recevoir des crypto-actifs",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Détails du compte",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "ici.",
"you_can": "Ou vous pouvez",
"add_network": "ajouter manuellement d’autres réseaux.",
- "deprecated_network_msg": "En raison des changements apportés au protocole d’Ethereum, les réseaux testnet Rinkeby, Ropsten et Kovan peuvent ne pas fonctionner d’une manière aussi fiable qu’auparavant et deviendront bientôt obsolètes.",
"select_network": "Sélectionner un réseau"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView a reçu le code d’erreur : {{code}}",
"no_tokens_available_title": "Aucun jeton disponible",
"no_tokens_available": "Il n'y a actuellement aucun jeton que vous pouvez acheter sur {{network}} en utilisant le moyen de paiement sélectionné.",
+ "this_network": "this network",
"change_payment_method": "Changer de mode de paiement",
"try_different_region": "Essayez une autre région",
"return_home": "Retour à l’écran d’accueil",
diff --git a/locales/languages/hi.json b/locales/languages/hi.json
index 4a8f1d9eb93..9b287b2b77f 100644
--- a/locales/languages/hi.json
+++ b/locales/languages/hi.json
@@ -108,6 +108,37 @@
"content": "अगर आप जानते हैं कि आप कहां जा रहे हैं, साइट को खोजें, या कोई URL टाइप करें।"
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "आपका वॉलेट बनाया जा रहा है...",
"subtitle": "इसमें ज्यादा समय नहीं लगना चाहिए"
@@ -800,7 +831,30 @@
"buy_description": "नकद के साथ क्रिप्टो खरीदें",
"swap_description": "टोकन के बीच विनिमय",
"send_description": "किसी भी खाते में क्रिप्टो भेजें",
- "receive_description": "क्रिप्टो प्राप्त करें"
+ "receive_description": "क्रिप्टो प्राप्त करें",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "अकाउंट का विवरण",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "यहां।",
"you_can": "या आप",
"add_network": "मैन्युअल रूप से अधिक नेटवर्क जोड़ें।",
- "deprecated_network_msg": "Ethereum प्रोटोकॉल परिवर्तनों के कारण: Rinkeby, Ropsten, Kovan टेस्ट नेटवर्क मज़बूती से काम करने में असमर्थ हो सकते हैं और जल्द ही अप्रचलित हो जाएंगे।",
"select_network": "एक नेटवर्क चुनें"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView को त्रुटि का स्टेटस कोड प्राप्त हुआ: {{code}}",
"no_tokens_available_title": "कोई टोकन उपलब्ध नहीं है",
"no_tokens_available": "चयनित भुगतान विधि के साथ {{network}} पर खरीदने के लिए फिलहाल कोई टोकन उपलब्ध नहीं हैं।",
+ "this_network": "this network",
"change_payment_method": "भुगतान का तरीका बदलें",
"try_different_region": "कोई दूसरा क्षेत्र आज़माएं",
"return_home": "होम स्क्रीन पर लौटें",
diff --git a/locales/languages/id.json b/locales/languages/id.json
index 5d4ec70976b..e8e6605af7d 100644
--- a/locales/languages/id.json
+++ b/locales/languages/id.json
@@ -108,6 +108,37 @@
"content": "Telusuri situs, atau ketikkan URL jika Anda mengetahui tujuan Anda."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Membuat dompet Anda...",
"subtitle": "Tidak perlu menunggu lama"
@@ -800,7 +831,30 @@
"buy_description": "Beli kripto dengan uang tunai",
"swap_description": "Pertukaran antar token",
"send_description": "Kirim kripto ke akun mana pun",
- "receive_description": "Terima kripto"
+ "receive_description": "Terima kripto",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Detail Akun",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "di sini.",
"you_can": "Atau Anda dapat",
"add_network": "menambahkan lebih banyak jaringan secara manual.",
- "deprecated_network_msg": "Sehubungan dengan perubahan protokol Ethereum: jaringan uji Rinkeby, Ropsten, Kovan mungkin tidak dapat beroperasi dengan baik dan akan segera dihentikan.",
"select_network": "Pilih jaringan"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView menerima kode status galat: {{code}}",
"no_tokens_available_title": "Token Tidak Tersedia",
"no_tokens_available": "Saat ini tidak ada token yang tersedia untuk dibeli di {{network}} dengan metode pembayaran yang dipilih.",
+ "this_network": "this network",
"change_payment_method": "Ubah metode pembayaran",
"try_different_region": "Coba wilayah lain",
"return_home": "Kembali ke Layar Beranda",
diff --git a/locales/languages/ja.json b/locales/languages/ja.json
index d4c4842e59e..a23127bbf42 100644
--- a/locales/languages/ja.json
+++ b/locales/languages/ja.json
@@ -108,6 +108,37 @@
"content": "サイトを検索するか、アクセス先がわかっている場合はURLを入力します。"
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "ウォレットを作成中...",
"subtitle": "少しだけお待ちください"
@@ -800,7 +831,30 @@
"buy_description": "現金で仮想通貨を購入します",
"swap_description": "トークン同士を交換します",
"send_description": "仮想通貨を任意のアカウントに送金します",
- "receive_description": "仮想通貨を受け取ります"
+ "receive_description": "仮想通貨を受け取ります",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "アカウント詳細",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "こちらから検出できます。",
"you_can": "または",
"add_network": "他のネットワークを手動で追加できます。",
- "deprecated_network_msg": "Ethereum のプロトコル変更のため: Rinkeby、Ropsten、Kovan テストネットワークは動作が安定しない可能性があり、近いうちに非推奨になります。",
"select_network": "ネットワークを選択"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView がエラーステータスコード {{code}} を受信しました",
"no_tokens_available_title": "利用可能なトークンがありません",
"no_tokens_available": "現在{{network}}に選択した支払方法で購入可能なトークンがありません。",
+ "this_network": "this network",
"change_payment_method": "支払方法を変更",
"try_different_region": "他の地域を試す",
"return_home": "ホーム画面に戻る",
diff --git a/locales/languages/ko.json b/locales/languages/ko.json
index d2bf1697365..44b689372f2 100644
--- a/locales/languages/ko.json
+++ b/locales/languages/ko.json
@@ -108,6 +108,37 @@
"content": "사이트를 검색하거나 URL을 입력하세요."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "지갑 생성 중...",
"subtitle": "오래 걸리지 않을 것입니다"
@@ -800,7 +831,30 @@
"buy_description": "현금으로 암호화폐 구입",
"swap_description": "토큰 간 교환",
"send_description": "원하는 계정에 암호화폐 보내기",
- "receive_description": "암호화폐 수령"
+ "receive_description": "암호화폐 수령",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "계정 상세 내역",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "여기에서 확인하세요.",
"you_can": "또는 다음을 확인하세요:",
"add_network": "네트워크를 직접 추가할 수도 있습니다.",
- "deprecated_network_msg": "이더리움 프로토콜 변경으로 인해 Rinkeby, Ropsten, Kovan 테스트 네트워크가 안정적으로 작동하지 않을 수 있습니다. 해당 네트워크는 곧 사용이 중단될 예정입니다.",
"select_network": "네트워크 선택"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView 수신 오류 코드: {{code}}",
"no_tokens_available_title": "사용 가능한 토큰 없음",
"no_tokens_available": "현재 {{network}}에서 선택한 결제 방식으로 구매할 수 있는 토큰이 없습니다.",
+ "this_network": "this network",
"change_payment_method": "결제 방법 변경",
"try_different_region": "다른 지역을 시도하세요",
"return_home": "홈 화면으로 돌아가기",
diff --git a/locales/languages/pt.json b/locales/languages/pt.json
index c21db3fb14c..362ba92dc75 100644
--- a/locales/languages/pt.json
+++ b/locales/languages/pt.json
@@ -108,6 +108,37 @@
"content": "Pesquise por sites ou digite um URL caso saiba para onde quer ir."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Criando sua carteira...",
"subtitle": "Não deve demorar muito"
@@ -800,7 +831,30 @@
"buy_description": "Compre criptomoedas com dinheiro",
"swap_description": "Faça câmbio entre tokens",
"send_description": "Envie criptomoedas para qualquer conta",
- "receive_description": "Receba criptomoedas"
+ "receive_description": "Receba criptomoedas",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Detalhes da conta",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "aqui.",
"you_can": "Ou você pode",
"add_network": "adicionar mais redes manualmente.",
- "deprecated_network_msg": "Devido às mudanças de protocolo do Ethereum, as redes de teste Rinkeby, Ropsten e Kovan podem não funcionar com a mesma confiança e serão descontinuadas em breve.",
"select_network": "Selecione uma rede"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "O WebView recebeu o código de status de erro: {{code}}",
"no_tokens_available_title": "Nenhum token disponível",
"no_tokens_available": "No momento, não há tokens disponíveis para compra em {{network}} com a forma de pagamento selecionada.",
+ "this_network": "this network",
"change_payment_method": "Alterar método de pagamento",
"try_different_region": "Tente uma região diferente",
"return_home": "Voltar à tela inicial",
diff --git a/locales/languages/ru.json b/locales/languages/ru.json
index 68b01963476..4a239f941ef 100644
--- a/locales/languages/ru.json
+++ b/locales/languages/ru.json
@@ -108,6 +108,37 @@
"content": "Найдите сайты или введите URL-адрес, если знаете, куда направляетесь."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Создание кошелька...",
"subtitle": "Это не займет много времени"
@@ -800,7 +831,30 @@
"buy_description": "Покупайте криптовалюту за наличные",
"swap_description": "Обменивайте одни токены на другие",
"send_description": "Отправляйте криптовалюту на любой счет",
- "receive_description": "Получайте криптовалюту"
+ "receive_description": "Получайте криптовалюту",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Реквизиты счета",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "здесь.",
"you_can": "Или вы можете",
"add_network": "добавить другие сети вручную.",
- "deprecated_network_msg": "Из-за изменений протокола Ethereum: тестовые сети Rinkeby, Ropsten, Kovan могут работать не так надежно и скоро будут признаны устаревшими.",
"select_network": "Выбрать сеть"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView получил код состояния ошибки: {{code}}",
"no_tokens_available_title": "Нет доступных токенов",
"no_tokens_available": "В настоящее время нет токенов, доступных для покупки в {{network}} в {{region}}",
+ "this_network": "this network",
"change_payment_method": "Изменить способ оплаты",
"try_different_region": "Попробуйте другой регион",
"return_home": "Назад на главный экран",
diff --git a/locales/languages/tl.json b/locales/languages/tl.json
index 5b26324e588..bc8baba6d73 100644
--- a/locales/languages/tl.json
+++ b/locales/languages/tl.json
@@ -108,6 +108,37 @@
"content": "Maghanap ng site o mag-type ng URL kung alam mo kung saan ka pupunta."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Ginagawa ang iyong wallet...",
"subtitle": "Hindi ito aabutin nang matagal"
@@ -800,7 +831,30 @@
"buy_description": "Bumili ng crypto gamit ang cash",
"swap_description": "Palitan sa pagitan ng mga token",
"send_description": "Magpadala ng crypto sa anumang account",
- "receive_description": "Tumanggap ng crypto"
+ "receive_description": "Tumanggap ng crypto",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Mga Detalye ng Account",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "dito.",
"you_can": "O maaari kang",
"add_network": "magdagdag pa ng mga network nang manu-mano.",
- "deprecated_network_msg": "Dahil sa mga pagbabago ng protokol ng Ethereum: ang mga test network na Rinkeby, Ropsten, at Kovan ay maaaring hindi gumana tulad ng maaasahan at malapit nang itigil ang paggamit.",
"select_network": "Pumili ng network"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "Ang WebView ay nakatanggap ng error status code: {{code}}",
"no_tokens_available_title": "Walang Available na Token",
"no_tokens_available": "Kasalukuyang walang mga token na available upang bilhin sa {{network}} gamit ang napiling paraan ng pagbabayad.",
+ "this_network": "this network",
"change_payment_method": "Baguhin ang paraan ng pagbabayad",
"try_different_region": "Subukan ang ibang rehiyon",
"return_home": "Bumalik sa Home Screen",
diff --git a/locales/languages/tr.json b/locales/languages/tr.json
index 4e41217bfe3..346e9764bfd 100644
--- a/locales/languages/tr.json
+++ b/locales/languages/tr.json
@@ -108,6 +108,37 @@
"content": "Siteleri arayabilir veya nereye yönlendirildiğinizi biliyorsanız bir URL adresi girebilirsiniz."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Cüzdanınız oluşturuluyor...",
"subtitle": "Bu işlem uzun sürmeyecektir"
@@ -800,7 +831,30 @@
"buy_description": "Nakit ile kripto satın alın",
"swap_description": "Tokenler arasında takas yapın",
"send_description": "Dilediğin hesaba kripto gönderin",
- "receive_description": "Kripto alın"
+ "receive_description": "Kripto alın",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Hesap Bilgileri",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "Burada daha fazla ağ keşfedebilirsiniz.",
"you_can": "Veya",
"add_network": "manuel olarak daha fazla ekleyebilirsiniz.",
- "deprecated_network_msg": "Ethereum'da yaşanan protokol değişikliklerinden dolayı: Rinkeby, Ropsten, Kovan test ağları güvenilir bir şekilde çalışmayabilir ve yakında kullanım dışı olacak.",
"select_network": "Bir ağ seçin"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView şu hata durum kodunu aldı: {{code}}",
"no_tokens_available_title": "Token Yok",
"no_tokens_available": "Şu anda {{network}} üzerinde seçili ödeme yöntemi ile mevcut token bulunmamaktadır.",
+ "this_network": "this network",
"change_payment_method": "Ödeme yöntemini değiştir",
"try_different_region": "Farklı bir bölge deneyin",
"return_home": "Ana Ekrana Geri Dön",
diff --git a/locales/languages/vi.json b/locales/languages/vi.json
index 3031b060875..4423a62163a 100644
--- a/locales/languages/vi.json
+++ b/locales/languages/vi.json
@@ -108,6 +108,37 @@
"content": "Tìm kiếm trang web hoặc gõ địa chỉ URL nếu bạn biết trang web bạn muốn đến."
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "Đang tạo ví của bạn...",
"subtitle": "Sẽ không tốn nhiều thời gian"
@@ -800,7 +831,30 @@
"buy_description": "Mua tiền mã hóa bằng tiền",
"swap_description": "Chuyển đổi giữa các token",
"send_description": "Gửi tiền mã hóa đến bất kỳ tài khoản nào",
- "receive_description": "Nhận tiền mã hóa"
+ "receive_description": "Nhận tiền mã hóa",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "Chi tiết tài khoản",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "tại đây.",
"you_can": "Hoặc bạn có thể",
"add_network": "thêm thủ công các mạng khác.",
- "deprecated_network_msg": "Do những thay đổi trong giao thức của Ethereum: các mạng thử nghiệm Rinkeby, Ropsten, Kovan có thể không hoạt động đáng tin cậy và sẽ sớm bị ngừng sử dụng.",
"select_network": "Chọn mạng"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView đã nhận được mã trạng thái lỗi: {{code}}",
"no_tokens_available_title": "Hiện không có token nào",
"no_tokens_available": "Hiện không có token nào để mua trên {{network}} bằng phương thức thanh toán đã chọn.",
+ "this_network": "this network",
"change_payment_method": "Thay đổi phương thức thanh toán",
"try_different_region": "Hãy thử một khu vực khác",
"return_home": "Quay lại Màn hình chính",
diff --git a/locales/languages/zh.json b/locales/languages/zh.json
index 443d5f392cd..4fc905998a4 100644
--- a/locales/languages/zh.json
+++ b/locales/languages/zh.json
@@ -108,6 +108,37 @@
"content": "搜索站点;如果您知道要前往的目的地,键入 URL。"
}
},
+ "onboarding_wizard_new": {
+ "coachmark": {
+ "action_back": "No thanks",
+ "action_next": "Take the tour",
+ "progress_next": "Got it"
+ },
+ "step1": {
+ "title": "Welcome to your wallet!",
+ "content1": "On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet."
+ },
+ "step2": {
+ "title": "Your accounts",
+ "content1": "This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon."
+ },
+ "step3": {
+ "title": "Managing your account",
+ "content1": "Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon"
+ },
+ "step4": {
+ "title": "Using your wallet",
+ "content1": "Buy, send, swap, and receive assets by tapping this icon"
+ },
+ "step5": {
+ "title": "Exploring web3",
+ "content1": "Open the browser by tapping this icon"
+ },
+ "step6": {
+ "title": "Using the browser",
+ "content1": "Search for sites by keyword or enter a URL. Have fun out there! "
+ }
+ },
"create_wallet": {
"title": "正在创建您的钱包...",
"subtitle": "这应该不需要很长时间"
@@ -800,7 +831,30 @@
"buy_description": "用现金购买加密货币",
"swap_description": "代币之间的兑换",
"send_description": "向任何账户发送加密货币",
- "receive_description": "接收加密货币"
+ "receive_description": "接收加密货币",
+ "chart_time_period": {
+ "1d": "Today",
+ "7d": "Past 7 days",
+ "1w": "Past week",
+ "1m": "Past month",
+ "3m": "Past 3 months",
+ "1y": "Past year",
+ "3y": "Past 3 years"
+ },
+ "chart_time_period_navigation": {
+ "1d": "1D",
+ "7d": "7D",
+ "1w": "1W",
+ "1m": "1M",
+ "3m": "3M",
+ "1y": "1Y",
+ "3y": "3Y"
+ },
+ "your_balance": "Your balance",
+ "unable_to_load_balance": "Unable to load your balance",
+ "about": "About",
+ "activity": "{{symbol}} activity",
+ "disclaimer": "Market data is provided by one or more third-party data sources, including CoinGecko. Such third-party content is provided solely for informational purposes and should not be treated as advice to buy, sell, or use any particular asset. MetaMask does not suggest the use of this content for any particular purpose and is not responsible for its accuracy."
},
"account_details": {
"title": "账户详细信息",
@@ -1222,7 +1276,6 @@
"add_other_network_here": "这里。",
"you_can": "或者您可以",
"add_network": "手动添加更多网络。",
- "deprecated_network_msg": "由于以太坊的协议有变化:Rinkeby、Ropsten、Kovan测试网络可能无法可靠地工作,很快就会被弃用。",
"select_network": "选择网络"
},
"select": {
@@ -1788,6 +1841,7 @@
"webview_received_error": "WebView收到错误状态代码:{{code}}",
"no_tokens_available_title": "无可用代币",
"no_tokens_available": "当前{{network}}上没有可用的代币,无法使用所选支付方式购买。",
+ "this_network": "this network",
"change_payment_method": "更改付款方式",
"try_different_region": "请尝试其他区域",
"return_home": "返回主屏幕",
diff --git a/package.json b/package.json
index 5d821ddb180..02d348650c5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "metamask",
- "version": "6.4.0",
+ "version": "6.5.0",
"private": true,
"scripts": {
"audit:ci": "./scripts/yarn-audit.sh",
@@ -108,6 +108,7 @@
"**/y18n": "^3.2.2",
"pubnub/**/netmask": "^2.0.1",
"**/vm2": ">=3.9.17",
+ "**/xml2js": ">=0.5.0",
"**/optimist/minimist": "^1.2.5",
"react-native-level-fs/**/bl": "^1.2.3",
"react-native-level-fs/**/semver": "^4.3.2",
@@ -138,6 +139,7 @@
"@ethereumjs/common": "^2.3.1",
"@ethereumjs/tx": "^3.2.1",
"@ethereumjs/util": "^8.0.5",
+ "@ethersproject/abi": "^5.7.0",
"@exodus/react-native-payments": "git+https://github.com/MetaMask/react-native-payments.git#dbc8cbbed570892d2fea5e3d183bf243e062c1e5",
"@keystonehq/bc-ur-registry-eth": "^0.7.7",
"@keystonehq/metamask-airgapped-keyring": "^0.3.0",
@@ -307,6 +309,7 @@
"redux-mock-store": "1.5.4",
"redux-persist": "6.0.0",
"redux-persist-filesystem-storage": "^3.0.0",
+ "redux-thunk": "^2.4.2",
"reselect": "^4.0.0",
"rn-fetch-blob": "^0.12.0",
"socket.io-client": "^4.5.3",
@@ -339,6 +342,7 @@
"@storybook/react-native": "^5.3.25",
"@testing-library/react-hooks": "^8.0.1",
"@types/enzyme": "^3.10.9",
+ "@types/is-url": "^1.2.30",
"@types/jest": "^27.0.1",
"@types/react": "^17.0.11",
"@types/react-native": "^0.64.10",
@@ -347,6 +351,7 @@
"@types/react-native-vector-icons": "^6.4.8",
"@types/react-native-video": "^5.0.13",
"@types/redux-mock-store": "^1.0.3",
+ "@types/url-parse": "^1.4.8",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0",
"@wdio/appium-service": "^7.19.1",
@@ -364,7 +369,6 @@
"babel-loader": "^8.2.3",
"browserstack-local": "^1.5.1",
"chromedriver": "^99.0.0",
- "concat-cli": "4.0.0",
"detox": "^19.11.0",
"dotenv": "^16.0.3",
"enzyme": "3.9.0",
diff --git a/patches/@metamask+assets-controllers+4.0.0.patch b/patches/@metamask+assets-controllers+4.0.0.patch
index d9917a56783..c0dbfc04147 100644
--- a/patches/@metamask+assets-controllers+4.0.0.patch
+++ b/patches/@metamask+assets-controllers+4.0.0.patch
@@ -24,10 +24,25 @@ index 332c87d..c110f41 100644
* Enumerate assets assigned to an owner.
*
diff --git a/node_modules/@metamask/assets-controllers/dist/Standards/ERC20Standard.js b/node_modules/@metamask/assets-controllers/dist/Standards/ERC20Standard.js
-index 9ddbc28..acdc28a 100644
+index 9ddbc28..2d12e33 100644
--- a/node_modules/@metamask/assets-controllers/dist/Standards/ERC20Standard.js
+++ b/node_modules/@metamask/assets-controllers/dist/Standards/ERC20Standard.js
-@@ -57,6 +57,27 @@ class ERC20Standard {
+@@ -13,7 +13,13 @@ exports.ERC20Standard = void 0;
+ const contracts_1 = require("@ethersproject/contracts");
+ const metamask_eth_abis_1 = require("@metamask/metamask-eth-abis");
+ const ethereumjs_util_1 = require("ethereumjs-util");
+-const abi_utils_1 = require("@metamask/abi-utils");
++
++// Revert to use @ethersproject/abi to fix BigInt compatibility issue
++// Issue reported here - https://github.com/MetaMask/metamask-mobile/issues/6307
++// TODO - Remove patch once app is upgraded to React Native 0.71.6
++// const abi_utils_1 = require("@metamask/abi-utils");
++const ethers_project_abi = require('@ethersproject/abi');
++
+ const controller_utils_1 = require("@metamask/controller-utils");
+ const utils_1 = require("@metamask/utils");
+ const assetsUtil_1 = require("../assetsUtil");
+@@ -57,6 +63,27 @@ class ERC20Standard {
}
});
}
@@ -55,6 +70,25 @@ index 9ddbc28..acdc28a 100644
/**
* Query for symbol for a given ERC20 asset.
*
+@@ -68,10 +95,16 @@ class ERC20Standard {
+ // Signature for calling `symbol()`
+ const payload = { to: address, data: '0x95d89b41' };
+ const result = yield this.provider.call(payload);
+- (0, utils_1.assertIsStrictHexString)(result);
++
++ // TODO - Remove patch once app is upgraded to RN 0.71.6
++ // (0, utils_1.assertIsStrictHexString)(result);
++
+ // Parse as string - treat empty string as failure
+ try {
+- const decoded = (0, abi_utils_1.decodeSingle)('string', result);
++ // TODO - Remove patch once app is upgraded to React Native 0.71.6
++ const decoded = ethers_project_abi.abiCoder.decode(['string'], result)[0];
++ // const decoded = (0, abi_utils_1.decodeSingle)('string', result);
++
+ if ((decoded === null || decoded === void 0 ? void 0 : decoded.length) > 0) {
+ return decoded;
+ }
diff --git a/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.js b/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.js
index 4ed4990..da18116 100644
--- a/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.js
diff --git a/patches/react-native+0.71.6.patch b/patches/react-native+0.71.6.patch
index 99a3be92922..478a09c8b66 100644
--- a/patches/react-native+0.71.6.patch
+++ b/patches/react-native+0.71.6.patch
@@ -1,11 +1,31 @@
+diff --git a/node_modules/react-native/ReactAndroid/build.gradle b/node_modules/react-native/ReactAndroid/build.gradle
+index 155cb59..053550c 100644
+--- a/node_modules/react-native/ReactAndroid/build.gradle
++++ b/node_modules/react-native/ReactAndroid/build.gradle
+@@ -420,12 +420,9 @@ android {
+
+ // Used to override the NDK path/version on internal CI or by allowing
+ // users to customize the NDK path/version from their root project (e.g. for M1 support)
+- if (rootProject.hasProperty("ndkPath")) {
+- ndkPath rootProject.ext.ndkPath
+- }
+- if (rootProject.hasProperty("ndkVersion")) {
+- ndkVersion rootProject.ext.ndkVersion
+- }
++
++ // Added patch to apply ndk Path from MetaMask app gradle.build this is required for M1 Bitrise builds to work
++ ndkPath = project.getProperties().get("ndkPath")
+
+ defaultConfig {
+ minSdkVersion(21)
diff --git a/node_modules/react-native/ReactAndroid/hermes-engine/build.gradle b/node_modules/react-native/ReactAndroid/hermes-engine/build.gradle
-index c714f87..b7f4c69 100644
+index c714f87..dca2750 100644
--- a/node_modules/react-native/ReactAndroid/hermes-engine/build.gradle
+++ b/node_modules/react-native/ReactAndroid/hermes-engine/build.gradle
@@ -43,11 +43,11 @@ def overrideHermesDir = System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") != nul
def hermesDir = System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") ?: new File(reactNativeRootDir, "sdks/hermes")
def hermesBuildDir = new File("$buildDir/hermes")
-
+
-def hermesVersion = "main"
-def hermesVersionFile = new File(reactNativeRootDir, "sdks/.hermesversion")
-if (hermesVersionFile.exists()) {
@@ -18,16 +38,32 @@ index c714f87..b7f4c69 100644
+// }
def ndkBuildJobs = Runtime.runtime.availableProcessors().toString()
def prefabHeadersDir = new File("$buildDir/prefab-headers")
-
+
@@ -59,7 +59,7 @@ def skipPrefabPublishing = System.getenv("REACT_NATIVE_HERMES_SKIP_PREFAB") != n
def jsiDir = new File(reactNativeRootDir, "ReactCommon/jsi")
-
+
task downloadHermes(type: Download) {
- src("https://github.com/facebook/hermes/tarball/${hermesVersion}")
+ src("https://github.com/MetaMask/hermes/tarball/${hermesVersion}")
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "hermes.tar.gz"))
+@@ -120,12 +120,9 @@ android {
+
+ // Used to override the NDK path/version on internal CI or by allowing
+ // users to customize the NDK path/version from their root project (e.g. for M1 support)
+- if (rootProject.hasProperty("ndkPath")) {
+- ndkPath rootProject.ext.ndkPath
+- }
+- if (rootProject.hasProperty("ndkVersion")) {
+- ndkVersion rootProject.ext.ndkVersion
+- }
++
++ // Added patch to apply ndk Path from MetaMask app gradle.build this is required for M1 Bitrise builds to work
++ ndkPath = project.getProperties().get("ndkPath")
+
+ defaultConfig {
+ minSdkVersion 21
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
index c150abc..a446111 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
@@ -43,7 +79,7 @@ index c150abc..a446111 100644
@@ -89,6 +90,16 @@ public class ReactEditText extends AppCompatEditText
/** A count of events sent to JS or C++. */
protected int mNativeEventCount;
-
+
+ /**
+ * Taken from EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING We can't use that
+ * value directly as it was only added on Oreo, but we can apply the value
@@ -55,7 +91,7 @@ index c150abc..a446111 100644
+ /** Samsung Device Check */
+ private static final Boolean IS_SAMSUNG_DEVICE = Build.MANUFACTURER.equals(SAMSUNG_MANUFACTURER_NAME);
private static final int UNSET = -1;
-
+
private @Nullable ArrayList mListeners;
@@ -248,16 +259,24 @@ public class ReactEditText extends AppCompatEditText
@Override
diff --git a/scripts/build.sh b/scripts/build.sh
index 66f6ad8efde..7324cf97ac3 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -396,7 +396,7 @@ startWatcher() {
yarn --ignore-engines build:static-logos
if [ "$MODE" == "clean" ]; then
watchman watch-del-all
- rm -rf $TMPDIR/react-*
+ rm -rf $TMPDIR/metro-cache
react-native start -- --reset-cache
else
react-native start
diff --git a/wdio.conf.js b/wdio.conf.js
index c248aaacefe..7e8a4b93dca 100644
--- a/wdio.conf.js
+++ b/wdio.conf.js
@@ -31,6 +31,10 @@ exports.config = {
//
specs: ['./wdio/features/**/*.feature'],
+ suites: {
+ confirmations: ['./wdio/features/Confirmations/*.feature']
+ },
+
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
diff --git a/wdio/features/Accounts/CreatingWalletAccount.feature b/wdio/features/Accounts/CreatingWalletAccount.feature
index fbf7ac543f6..bd4f5d2d4be 100644
--- a/wdio/features/Accounts/CreatingWalletAccount.feature
+++ b/wdio/features/Accounts/CreatingWalletAccount.feature
@@ -1,6 +1,6 @@
@androidApp
@smoke
-Feature: Creating account in wallet
+Feature: Create Account
Scenario: Import wallet
Given the app displayed the splash animation
@@ -14,5 +14,4 @@ Feature: Creating account in wallet
When I tap on the Identicon
Then the account list should be visible
When I tap on Create a new account
- Then A new account is created
- And I am on the new account
+ Then I am on the new account
diff --git a/wdio/features/Accounts/ImportingAccount.feature b/wdio/features/Accounts/ImportingAccount.feature
index d4e0c7335fc..6799eaafb1d 100644
--- a/wdio/features/Accounts/ImportingAccount.feature
+++ b/wdio/features/Accounts/ImportingAccount.feature
@@ -1,8 +1,8 @@
@androidApp
-@smoke
-Feature: Importing account in wallet
+@regression
+Feature: Import Aaccount
- Scenario: Import wallet
+ Scenario: Import Wallet
Given the app displayed the splash animation
And I have imported my wallet
And I tap No Thanks on the Enable security check screen
@@ -34,6 +34,7 @@ Feature: Importing account in wallet
When I type into the private key input field
And I tap on the private key import button
Then The account is imported
+ And I am on the wallet view
Examples:
| PRIVATEKEY |
| cbfd798afcfd1fd8ecc48cbecb6dc7e876543395640b758a90e11d986e758ad1 |
diff --git a/wdio/features/BrowserFlow/AddFavorite.feature b/wdio/features/BrowserFlow/AddFavorite.feature
index da44bdc2247..448cff33c93 100644
--- a/wdio/features/BrowserFlow/AddFavorite.feature
+++ b/wdio/features/BrowserFlow/AddFavorite.feature
@@ -1,6 +1,6 @@
@androidApp
@smoke
-Feature: This feature file covers adding favorite websites in browser.
+Feature: Browser Add Favorite
Scenario: Adding browser Favorites
Add, click and delete favorites. Display favorites in the Favorites tab of home.metamask.io
diff --git a/wdio/features/BrowserFlow/InvalidURL.feature b/wdio/features/BrowserFlow/InvalidURL.feature
index 1f00baf5944..4f65eb616c7 100644
--- a/wdio/features/BrowserFlow/InvalidURL.feature
+++ b/wdio/features/BrowserFlow/InvalidURL.feature
@@ -1,6 +1,6 @@
@androidApp
@smoke
-Feature: This feature file covers invalid url functionality in the browser.
+Feature: Browser Invalid URL
Scenario: Searching an invalid url and prompts the user with an error message
Given the app displayed the splash animation
diff --git a/wdio/features/BrowserFlow/PhishingDetection.feature b/wdio/features/BrowserFlow/PhishingDetection.feature
index e23bc049739..9aedc84400e 100644
--- a/wdio/features/BrowserFlow/PhishingDetection.feature
+++ b/wdio/features/BrowserFlow/PhishingDetection.feature
@@ -1,6 +1,6 @@
@androidApp
@smoke
-Feature: This feature file covers phishing detection functionality the browser.
+Feature: Browser Phishing Detection
Scenario: Visiting a malicious website prompts the user with the phishing detection warning
Given the app displayed the splash animation
diff --git a/wdio/features/BrowserFlow/RemovingImportedAccountAfterConnectingToDapp.feature b/wdio/features/BrowserFlow/RemovingImportedAccountAfterConnectingToDapp.feature
index fadaa6174e6..1c342c46633 100644
--- a/wdio/features/BrowserFlow/RemovingImportedAccountAfterConnectingToDapp.feature
+++ b/wdio/features/BrowserFlow/RemovingImportedAccountAfterConnectingToDapp.feature
@@ -1,5 +1,6 @@
-@androidApp
-@regression
+@androidApp
+@smoke
+
Feature: Importing account via private then revoking permissions
Scenario: Import wallet
@@ -11,7 +12,7 @@ Feature: Importing account via private then revoking permissions
Scenario: User grants permission to a sushiswap to access one of their accounts
When I navigate to the browser
- # When I tap on button with text "Wallet"
+ # When I tap on button with text "Wallet"
And I navigate to "https://app.sushi.com/swap"
Then the connect modal should be displayed
And I connect my active wallet to the dapp
diff --git a/wdio/features/BrowserFlow/RevokingSingleAccount.feature b/wdio/features/BrowserFlow/RevokingSingleAccount.feature
index 0589a7ca10b..3db0e9163ad 100644
--- a/wdio/features/BrowserFlow/RevokingSingleAccount.feature
+++ b/wdio/features/BrowserFlow/RevokingSingleAccount.feature
@@ -1,6 +1,6 @@
-@androidApp
+@androidApp
@regression
-Feature: Permission system in MetaMask Mobile
+Feature: Revoking access to a dapp while connected to one account.
Scenario: Import wallet
Given the app displayed the splash animation
diff --git a/wdio/features/Confirmations/SendEthEOA.feature b/wdio/features/Confirmations/SendEthEOA.feature
new file mode 100644
index 00000000000..0c59cb176a5
--- /dev/null
+++ b/wdio/features/Confirmations/SendEthEOA.feature
@@ -0,0 +1,58 @@
+@androidApp
+@confirmations
+@regression
+
+Feature: Sending ETH to an EOA
+ A user should be able to send ETH to another EOA address.
+
+ Scenario: Import wallet
+ Given the app displayed the splash animation
+ And I have imported my wallet
+ And I tap No Thanks on the Enable security check screen
+ And I tap No thanks on the onboarding welcome tutorial
+
+ Scenario: Setting up Ganache local network
+ Given Ganache server is started
+ And I close the Whats New modal
+ When I tap on the burger menu
+ And I tap on "Settings" in the menu
+ And In settings I tap on "Networks"
+ And I tap on the Add Network button
+ Then "POPULAR" tab is displayed on networks screen
+ And "CUSTOM NETWORKS" tab is displayed on networks screen
+
+ When I tap on the "CUSTOM NETWORKS" tab
+
+ When I type "" into Network name field
+ And I type "" into the RPC url field
+ And I type "" into the Chain ID field
+ And I type "" into the Network symbol field
+
+ When I tap on the Add button
+ And I tap on Got it in the network education modal
+ Then I should see the added network name "" in the top navigation bar
+
+ Examples:
+ | Network | rpcUrl | ChainID | Symbol |
+ | Localhost 8545 | http://localhost:8545 | 1337 | ETH |
+
+ Scenario Outline: Sending ETH to an EOA from inside MetaMask wallet
+ When On the Main Wallet view I tap "ETHER"
+ And On the Main Wallet view I tap "Send"
+ And I enter address "" in the sender's input box
+ When I tap button "Next" on Send To view
+ Then I proceed to the amount view
+
+ When I type amount "" into amount input field
+ And I tap button "Next" on the Amount view
+ Then I should be taken to the transaction confirmation view
+ And the token amount to be sent is visible
+
+ When I tap button "Send" on Confirm Amount view
+ Then I am taken to the token overview screen
+ And the transaction is submitted with Transaction Complete! toast appearing
+ Then Ganache server is stopped
+
+ Examples:
+ | Address | Amount |
+ | 0x1FDb169Ef12954F20A15852980e1F0C122BfC1D6 | 1 |
\ No newline at end of file
diff --git a/wdio/features/Onboarding/CreateNewWallet.feature b/wdio/features/Onboarding/CreateNewWallet.feature
index 4cf9cf0ffa3..32c2761caeb 100644
--- a/wdio/features/Onboarding/CreateNewWallet.feature
+++ b/wdio/features/Onboarding/CreateNewWallet.feature
@@ -1,12 +1,12 @@
@androidApp
-@smoke
-Feature: New wallet flow
+@regression
+Feature: Create New Wallet
Scenario: Onboarding New walllet
- User opens the app for first time and creates a new wallet.
+ User opens the app for first time and creates a new wallet.
Given the Welcome Screen is displayed
When I tap "Get started"
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
When On Wallet Setup Screen I tap "Create a new wallet"
And On Wallet Setup Screen I tap "Agree"
And Terms of Use is displayed
diff --git a/wdio/features/Onboarding/ImportWallet.feature b/wdio/features/Onboarding/ImportWallet.feature
index 9e0ccf5b2ca..7e45e487685 100644
--- a/wdio/features/Onboarding/ImportWallet.feature
+++ b/wdio/features/Onboarding/ImportWallet.feature
@@ -1,14 +1,12 @@
@androidApp
-@smoke
+@regression
Feature: Import Wallet
- Users can use the app to import an existing wallet or create a new one.
+ Users can use the app to import an existing wallet or create a new one.
- Scenario Outline: Import Wallet - Manual input SR
- After a user completes the onboarding process then they are presented
- with the option to create a new wallet and back it up.
+ Scenario: Get Started
Given the Welcome Screen is displayed
When I tap "Get started"
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
When I tap "Import using Secret Recovery Phrase"
Then "Help us improve MetaMask" is displayed
When I tap "I agree"
@@ -16,12 +14,40 @@ Feature: Import Wallet
And I agree to terms
And Terms of Use is not displayed
Then "Import from seed" is displayed
+
+ Scenario Outline: Password Strength
+ When I type in new password field
+ Then password strength is displayed
+ Examples:
+ | password | strength |
+ | metapass1 | Password strength: Weak |
+ | Metapass12345 | Password strength: Good |
+ | Metapass12345!@ | Password strength: Strong |
+
+ Scenario Outline: Password Matching
+ When I type in confirm password field
+ Then green check mark is displayed
+ Examples:
+ | password |
+ | Metapass12345!@ |
+
+ Scenario Outline: Invalid SRP
+ When I type in SRP field
+ And I tap "Import"
+ Then device alert is displayed
+ And I tap Yes on alert
+
+ Examples:
+ | invalid_SRP | error |
+ | fold media south not valid secret recovery phrase pause cloth just raven | Invalid Secret Recovery Phrase |
+ | fold media south add since false relax immense pause cloth just | Secret Recovery Phrases contain 12, 15, 18, 21, or 24 words |
+
+ Scenario Outline: Import Wallet
When I type in SRP field
- And I type in new password field
And I type in confirm password field
And I tap "Import"
And I tap No Thanks on the Enable security check screen
Then "Welcome to your new wallet!" is displayed
Examples:
- | SRP | password |
- | fold media south add since false relax immense pause cloth just raven | metapass1 |
+ | SRP | password |
+ | fold media south add since false relax immense pause cloth just raven | Metapass12345!@ |
diff --git a/wdio/features/Onboarding/ImportWalletRegression.feature b/wdio/features/Onboarding/ImportWalletRegression.feature
deleted file mode 100644
index 2bc04fa60ee..00000000000
--- a/wdio/features/Onboarding/ImportWalletRegression.feature
+++ /dev/null
@@ -1,53 +0,0 @@
-@androidApp
-@regression
-Feature: Import Wallet Regression
- Users can use the app to import an existing wallet or create a new one.
-
- Scenario: Get Started
- Given the Welcome Screen is displayed
- When I tap "Get started"
- Then "Wallet setup" is displayed
- When I tap "Import using Secret Recovery Phrase"
- Then "Help us improve MetaMask" is displayed
- When I tap "I agree"
- And Terms of Use is displayed
- And I agree to terms
- And Terms of Use is not displayed
- Then "Import from seed" is displayed
-
- Scenario Outline: Password Strength
- When I type in new password field
- Then password strength is displayed
- Examples:
- | password | strength |
- | metapass1 | Password strength: Weak |
- | Metapass12345 | Password strength: Good |
- | Metapass12345!@ | Password strength: Strong |
-
- Scenario Outline: Password Matching
- When I type in confirm password field
- Then green check mark is displayed
- Examples:
- | password |
- | Metapass12345!@ |
-
- Scenario Outline: Invalid SRP
- When I type in SRP field
- And I tap "Import"
- Then device alert is displayed
- And I tap Yes on alert
-
- Examples:
- | invalid_SRP | error |
- | fold media south not valid secret recovery phrase pause cloth just raven | Invalid Secret Recovery Phrase |
- | fold media south add since false relax immense pause cloth just | Secret Recovery Phrases contain 12, 15, 18, 21, or 24 words |
-
- Scenario Outline: Import Wallet
- When I type in SRP field
- And I type in confirm password field
- And I tap "Import"
- And I tap No Thanks on the Enable security check screen
- Then "Welcome to your new wallet!" is displayed
- Examples:
- | SRP | password |
- | fold media south add since false relax immense pause cloth just raven | Metapass12345!@ |
diff --git a/wdio/features/Onboarding/OnboardingCarousel.feature b/wdio/features/Onboarding/OnboardingCarousel.feature
index f95558259e1..59f9cf6879b 100644
--- a/wdio/features/Onboarding/OnboardingCarousel.feature
+++ b/wdio/features/Onboarding/OnboardingCarousel.feature
@@ -1,11 +1,11 @@
@androidApp
@smoke
-Feature: Onboarding
+Feature: Onboarding Carousel
Users can install MetaMask mobile app from the device app store and read the onboarding carousel
Scenario: New app install setup on a mobile device
- This is the onboarding process after a new user installs the mobile app
- and launches the app for the first time.
+ This is the onboarding process after a new user installs the mobile app
+ and launches the app for the first time.
Given the app displayed the splash animation
And the splash animation disappears
And the Welcome Screen is displayed
@@ -15,7 +15,5 @@ Feature: Onboarding
Then "Your gateway to web3" carousel item is displayed
When I swipe left on the carousel
And I tap "Get started"
- Then "Wallet setup" is displayed
- And "Import an existing wallet or create a new one" is displayed
- And "Import using Secret Recovery Phrase" is displayed
- And "Create a new wallet" is displayed
+ Then Wallet setup screen is displayed
+
diff --git a/wdio/features/Onboarding/TermsOfUse.feature b/wdio/features/Onboarding/TermsOfUse.feature
index 85021b292aa..17f083d454f 100644
--- a/wdio/features/Onboarding/TermsOfUse.feature
+++ b/wdio/features/Onboarding/TermsOfUse.feature
@@ -1,4 +1,4 @@
-@androidApp
+@androidApp
@regression
Feature: Terms of Use
@@ -6,7 +6,7 @@ Feature: Terms of Use
Given the app displayed the splash animation
And the Welcome Screen is displayed
When I tap "Get started"
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
When I tap "Import using Secret Recovery Phrase"
Then "Help us improve MetaMask" is displayed
When I tap "I agree"
@@ -19,7 +19,7 @@ Feature: Terms of Use
And the splash animation disappears
Then the Welcome Screen is displayed
When I tap "Get started"
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
When I tap "Import using Secret Recovery Phrase"
Then Terms of Use is displayed
@@ -34,6 +34,6 @@ Feature: Terms of Use
And the splash animation disappears
Then the Welcome Screen is displayed
When I tap "Get started"
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
When I tap "Import using Secret Recovery Phrase"
Then Terms of Use is not displayed
diff --git a/wdio/features/SecurityAndPrivacy/DeleteWallet.feature b/wdio/features/SecurityAndPrivacy/DeleteWallet.feature
index 6aea89cf9e5..88b039b94cf 100644
--- a/wdio/features/SecurityAndPrivacy/DeleteWallet.feature
+++ b/wdio/features/SecurityAndPrivacy/DeleteWallet.feature
@@ -17,4 +17,4 @@ Feature: This feature deletes the wallet from the Security and Privacy View
And I tap I understand, continue on Delete wallet modal
And I type "delete" on Delete wallet modal permanently
And I tap Delete my wallet on Delete wallet modal permanently
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
diff --git a/wdio/features/Wallet/AddressFlow.feature b/wdio/features/Wallet/AddressFlow.feature
index dc5014dbbfc..245e15002ba 100644
--- a/wdio/features/Wallet/AddressFlow.feature
+++ b/wdio/features/Wallet/AddressFlow.feature
@@ -1,9 +1,9 @@
@androidApp
@regression
Feature: Adding addresses to contacts via address book modal and the contacts screen
-User should see the correct warning when trying to send funds to a contract address
-User can saved an ENS name to your address book
-The contacts you saved on network A does not carry over to network B
+ User should see the correct warning when trying to send funds to a contract address
+ User can saved an ENS name to your address book
+ The contacts you saved on network A does not carry over to network B
Scenario: Import account and navigate to Send
Given the app displayed the splash animation
@@ -47,7 +47,7 @@ The contacts you saved on network A does not carry over to network B
Scenario Outline: A user has a saved address then deletes it
When I tap on contact name ""
And I tap on Edit button to edit Saved contact details
- And I tap button "Delete" to navigate to Contacts view
+ And I tap button Delete to navigate to Contacts view
Then the deleted contact "" should not appear
Examples:
| ContactName |
@@ -55,7 +55,7 @@ The contacts you saved on network A does not carry over to network B
Scenario Outline: A user can add a contact on the contacts view and edit the saved contact
Given I am on the contacts view
- And I tap on the "Add contact" button
+ And I tap on the Add contact button on the Contact view
And I input "" into the contact name field
And I input "" in the Address field
And I tap button Add contact which is now enabled
@@ -68,7 +68,7 @@ The contacts you saved on network A does not carry over to network B
When I go back to the main wallet screen
And On the Main Wallet view I tap on the Send Action
Then I should see the edited name "" contact under Recents on the send screen
- And the deleted contact "" should not appear
+ And I should not see the edited name "" contact under Recents on the send screen
Examples:
| ContactName | AddressName | EditContactName | DeletedContactName |
| Birdis | mike.eth | James | TestAlias |
diff --git a/wdio/features/Wallet/ExploringWizard.feature b/wdio/features/Wallet/ExploringWizard.feature
index b751efb04fc..db064c12ce9 100644
--- a/wdio/features/Wallet/ExploringWizard.feature
+++ b/wdio/features/Wallet/ExploringWizard.feature
@@ -13,10 +13,10 @@ Feature: Exploring wizard
When On the onboarding wizard I tap on "Got it" button
Then the tutorial modal heading should read "Edit Account Name"
And there should be an explanation about adding a nickname to your account.
- #When I tap and hold on the account Name
- #Then I should be able to edit the account Name
- #When I enter "Big Bank" for account name
- #Then the account nickname should read "Big Bank"
+ When I tap and hold on the account Name
+ Then I should be able to edit the account Name
+ When I enter "Big Bank" for account name
+ Then the account nickname should read "Big Bank"
When On the onboarding wizard I tap on "Got it" button
Then the tutorial modal heading should read "Main Menu"
And there should be an explanation of the what exists within the main menu.
@@ -36,18 +36,18 @@ Feature: Exploring wizard
Then the onboarding wizard is no longer visible
And I close the Whats New modal
- # Scenario: A user should be able to tap the Skip button
- # Given the app displayed the splash animation
- # And I have imported my wallet
- # And I tap No Thanks on the Enable security check screen
- # And the onboarding wizard is visible on wallet view
- # When On the onboarding wizard I tap on "Take a Tour" button
- # Then the tutorial modal heading should read "Your Accounts"
- # And there should be an explanation of the accounts functionality.
- # When On the onboarding wizard I tap on "Got it" button
- # Then the tutorial modal heading should read "Edit Account Name"
- # And there should be an explanation about adding a nickname to your account.
- # When On the onboarding wizard I tap on "Skip" button
- # Then the onboarding wizard is no longer visible
- # And the "Skip" button is no longer visible
- # And I close the Whats New modal
+# Scenario: A user should be able to tap the Skip button
+# Given the app displayed the splash animation
+# And I have imported my wallet
+# And I tap No Thanks on the Enable security check screen
+# And the onboarding wizard is visible on wallet view
+# When On the onboarding wizard I tap on "Take a Tour" button
+# Then the tutorial modal heading should read "Your Accounts"
+# And there should be an explanation of the accounts functionality.
+# When On the onboarding wizard I tap on "Got it" button
+# Then the tutorial modal heading should read "Edit Account Name"
+# And there should be an explanation about adding a nickname to your account.
+# When On the onboarding wizard I tap on "Skip" button
+# Then the onboarding wizard is no longer visible
+# And the "Skip" button is no longer visible
+# And I close the Whats New modal
diff --git a/wdio/features/Wallet/ImportCustomToken.feature b/wdio/features/Wallet/ImportCustomToken.feature
index 125563e78a6..18408955cca 100644
--- a/wdio/features/Wallet/ImportCustomToken.feature
+++ b/wdio/features/Wallet/ImportCustomToken.feature
@@ -1,6 +1,6 @@
@androidApp
@smoke
-Feature: Adding a custom token to your wallet
+Feature: Import Custom Token
Scenario: Import wallet
Given the app displayed the splash animation
diff --git a/wdio/features/Wallet/LockResetWallet.feature b/wdio/features/Wallet/LockResetWallet.feature
index fc37c4a7cf5..9c8d2861a8a 100644
--- a/wdio/features/Wallet/LockResetWallet.feature
+++ b/wdio/features/Wallet/LockResetWallet.feature
@@ -1,5 +1,5 @@
@androidApp
-@regression
+@smoke
Feature: Lock and Reset Wallet
Scenario: Import account
@@ -24,4 +24,4 @@ Feature: Lock and Reset Wallet
And I tap I understand, continue on Delete wallet modal
And I type "delete" on Delete wallet modal permanently
And I tap Delete my wallet on Delete wallet modal permanently
- Then "Wallet setup" is displayed
+ Then Wallet setup screen is displayed
diff --git a/wdio/features/Wallet/SendToken.feature b/wdio/features/Wallet/SendToken.feature
index 07e9fffed1f..ed46deb49b6 100644
--- a/wdio/features/Wallet/SendToken.feature
+++ b/wdio/features/Wallet/SendToken.feature
@@ -1,5 +1,5 @@
@androidApp
-@smoke
+@regression
Feature: Sending Native and ERC Tokens
Scenario: Import wallet
@@ -10,76 +10,59 @@ Feature: Sending Native and ERC Tokens
And I close the Whats New modal
Then I am on the wallet view
- Scenario Outline: Adding AVAX testnet to my networks list
- Given I tap on the navbar network title button
- And I tap on the Add a Network button
- And I tap on the "CUSTOM NETWORKS" tab
- And I type "" into Network name field
- And I type "" into the RPC url field
- And I type "" into the Chain ID field
- And I type "" into the Network symbol field
- When I tap on the Add button
- And I tap on Got it in the network education modal
- Then I see "" visible in the top navigation bar
- Examples:
- | Network | rpcUrl | ChainID | Symbol |
- | AVAX Fuji | https://api.avax-test.network/ext/C/rpc | 43113 | AVAX |
-
- Scenario Outline: Import Custom AVAX Fuji Token
+ Scenario Outline: Import ChainLink Token
Given I tap on the navbar network title button
And I tap on on Networks list to switch
+ And I tap on Got it in the network education modal
+ Then I see "" visible in the top navigation bar
When I tap Import Tokens
- And I type into token Address field
+ And I type into token Address field
Then The Token Symbol is displayed
When I tap on the Import button
Then I should see "Imported Token" toast message
Examples:
- | NETWORK | TOKENADDRESS |
- | AVAX Fuji | 0x5425890298aed601595a70AB815c96711a31Bc65 |
+ | NETWORK | TOKEN_ADDRESS |
+ | Sepolia Test Network | 0x779877A7B0D9E8603169DdbD7836e478b4624789 |
- Scenario Outline: A user can send native tokens to an Address via the wallet view send button
- Given I see "" visible in the top navigation bar
- And On the Main Wallet view I tap on the Send Action
+ Scenario Outline: A user can send ERC-20 tokens to an Address via token overview screen
+ Given I am on the wallet view
+ When I tap Token containing text ""
+ Then I am taken to the token overview screen
+ When I tap button Send on Token screen view
And I enter address "" in the sender's input box
- When I tap button "Next" on Send To view
- Then I proceed to the amount view
- When I type amount "" into amount input field
+ And I tap button "Next" on Send To view
+ And I type amount "" into amount input field
And I tap button "Next" on the Amount view
Then I should be taken to the transaction confirmation view
- And the token being sent is visible
+ And the token being sent is visible
And the token amount to be sent is visible
When I tap button "Send" on Confirm Amount view
- Then the transaction is submitted with Transaction Complete! toast appearing
- And Sending token takes me to main wallet view
-
+ # Then the transaction is submitted with Transaction Complete! toast appearing
+ And I am taken to the token overview screen
Examples:
- | NETWORK | TOKEN | AMOUNT | Address |
- | AVAX Fuji | AVAX | 0.005 | 0x2990079bcdEe240329a520d2444386FC119da21a |
+ | TOKEN_NAME | TOKEN_SYMBOL | AMOUNT | Address |
+ | ChainLink Token | LINK | 0.002 | 0x2990079bcdEe240329a520d2444386FC119da21a |
- Scenario Outline: A user can send ERC-20 tokens to an Address via token overview screen
- Given I am on the wallet view
- And I see "" visible in the top navigation bar
- When I tap Token containing text ""
- Then I am taken to the token overview screen
- When I tap button Send on Token screen view
+ Scenario Outline: A user can send native tokens to an Address via the wallet view send button
+ When I tap back from the Token overview page
+ And On the Main Wallet view I tap on the Send Action
And I enter address "" in the sender's input box
- And I tap button "Next" on Send To view
- And I type amount "" into amount input field
+ When I tap button "Next" on Send To view
+ Then I proceed to the amount view
+ When I type amount "" into amount input field
And I tap button "Next" on the Amount view
Then I should be taken to the transaction confirmation view
And the token being sent is visible
And the token amount to be sent is visible
When I tap button "Send" on Confirm Amount view
- Then I am taken to the token overview screen
-
-
+ # Then the transaction is submitted with Transaction Complete! toast appearing
+ And Sending token takes me to main wallet view
Examples:
- | NETWORK | TOKEN | AMOUNT | Address |
- | AVAX Fuji | USDC | 0.002 | 0x2990079bcdEe240329a520d2444386FC119da21a |
+ | TOKEN | AMOUNT | Address |
+ | SepoliaETH | 0.002 | 0x2990079bcdEe240329a520d2444386FC119da21a |
Scenario Outline: A user tries to send an invalid amount
- Given I tap back from the Token overview page
- And I am on the wallet view
+ When I am on the wallet view
And On the Main Wallet view I tap on the Send Action
And I enter address "" in the sender's input box
When I tap button "Next" on Send To view
@@ -87,5 +70,5 @@ Feature: Sending Native and ERC Tokens
And I tap button "Next" on the Amount view
Then "Insufficient funds" is visible
Examples:
- | NETWORK | TOKEN | AMOUNT | Address |
- | AVAX Fuji | 1 USDC | 121212121212121212121 | 0x2990079bcdEe240329a520d2444386FC119da21a |
+ | AMOUNT | Address |
+ | 25 | 0x2990079bcdEe240329a520d2444386FC119da21a |
diff --git a/wdio/helpers/Accounts.js b/wdio/helpers/Accounts.js
index c7b072a8027..ffddf1ac6b6 100644
--- a/wdio/helpers/Accounts.js
+++ b/wdio/helpers/Accounts.js
@@ -3,7 +3,6 @@ const INCORRECT_SECRET_RECOVERY_PHRASE =
'gain lemon refuse sunny identify diesel hand endless first involve wink size';
const CORRECT_PASSWORD = `12345678`;
const SHORT_PASSWORD = `1234567`;
-
const INCORRECT_PASSWORD = `12345679`;
class Accounts {
diff --git a/wdio/helpers/Selectors.js b/wdio/helpers/Selectors.js
index e643b41a479..f79005c4d22 100644
--- a/wdio/helpers/Selectors.js
+++ b/wdio/helpers/Selectors.js
@@ -18,7 +18,7 @@ class Selectors {
}
}
- static async getElementsByPlatform(id) {
+ static async getXpathByContentDesc(id) {
return driver.$$(`//*[@content-desc='${id}']`);
}
diff --git a/wdio/screen-objects/AccountListComponent.js b/wdio/screen-objects/AccountListComponent.js
index 6a95450085d..1aa1b3582ff 100644
--- a/wdio/screen-objects/AccountListComponent.js
+++ b/wdio/screen-objects/AccountListComponent.js
@@ -21,7 +21,7 @@ class AccountListComponent {
}
get accountsListed() {
- return Selectors.getElementsByPlatform(CELL_TITLE_TEST_ID);
+ return Selectors.getXpathByContentDesc(CELL_TITLE_TEST_ID);
}
async tapCreateAccountButton() {
diff --git a/wdio/screen-objects/AddContact.js b/wdio/screen-objects/AddContact.js
index edc01b7a4ab..86f69542249 100644
--- a/wdio/screen-objects/AddContact.js
+++ b/wdio/screen-objects/AddContact.js
@@ -1,42 +1,47 @@
import Gestures from '../helpers/Gestures';
import Selectors from '../helpers/Selectors';
import {
- CONTACT_NAME_INPUT,
- CONTACT_ADD_BUTTON,
- CONTACT_ADDRESS_INPUT,
- CONTACTS_CONTAINER_ID,
-} from './testIDs/Screens/Contacts.testids';
+ ADD_CONTACT_ADD_BUTTON,
+ ADD_CONTACT_ADDRESS_INPUT,
+ ADD_CONTACT_DELETE_BUTTON,
+ ADD_CONTACT_NAME_INPUT,
+ ADD_CONTACTS_CONTAINER_ID,
+} from './testIDs/Screens/AddContact.testIds';
+import { EDIT_BUTTON } from './testIDs/Common.testIds';
-class Contacts {
- get contactInputfield() {
- return Selectors.getElementByPlatform(CONTACT_NAME_INPUT);
+class AddContacts {
+ get container() {
+ return Selectors.getElementByPlatform(ADD_CONTACTS_CONTAINER_ID);
+ }
+
+ get contactInputField() {
+ return Selectors.getElementByPlatform(ADD_CONTACT_NAME_INPUT);
}
get addressInputField() {
- return Selectors.getElementByPlatform(CONTACT_ADDRESS_INPUT);
+ return Selectors.getElementByPlatform(ADD_CONTACT_ADDRESS_INPUT);
}
get addContactButton() {
- return Selectors.getElementByPlatform(CONTACT_ADD_BUTTON);
+ return Selectors.getElementByPlatform(ADD_CONTACT_ADD_BUTTON);
}
- get contactOverviewNameInput() {
- return Selectors.getElementByPlatform(CONTACT_NAME_INPUT);
- }
- get contactScreenContainer() {
- return Selectors.getElementByPlatform(CONTACTS_CONTAINER_ID);
+ get deleteButton() {
+ return Selectors.getElementByPlatform(ADD_CONTACT_DELETE_BUTTON);
}
- async isContactsScreenDisplayed() {
- await expect(this.contactScreenContainer).toBeDisplayed();
+
+ get editButton() {
+ return Selectors.getElementByPlatform(EDIT_BUTTON);
}
- async isAddContactButtonEnabled() {
- await expect(this.addContactButton).toBeEnabled();
+ async waitForDisplay() {
+ const element = await this.container;
+ await element.waitForDisplayed();
}
- async tapOnAddContactButton() {
- await Gestures.tap(this.addContactButton);
- await Gestures.tap(this.addContactButton);
+ async tapAddContactButton() {
+ await Gestures.waitAndTap(this.addContactButton);
+ await Gestures.waitAndTap(this.addContactButton);
}
async waitForAddContactButton() {
@@ -44,20 +49,23 @@ class Contacts {
await element.waitForDisplayed();
}
- async fillContactNamefield(name) {
- await Gestures.typeText(this.contactInputfield, name);
+ async fillContactNameField(name) {
+ await Gestures.typeText(this.contactInputField, name);
+ await driver.hideKeyboard();
}
async fillAddressField(address) {
await Gestures.typeText(this.addressInputField, address);
+ await driver.hideKeyboard();
}
- async tapOnText(text) {
- await Gestures.tapTextByXpath(text);
+ async tapEditButton() {
+ await Gestures.waitAndTap(this.editButton);
}
- async changeContactName(newName) {
- await Gestures.typeText(this.contactOverviewNameInput, newName);
+ async tapDeleteButton() {
+ await Gestures.waitAndTap(this.deleteButton);
}
}
-export default new Contacts();
+
+export default new AddContacts();
diff --git a/wdio/screen-objects/AmountScreen.js b/wdio/screen-objects/AmountScreen.js
index 33424c26315..78688273141 100644
--- a/wdio/screen-objects/AmountScreen.js
+++ b/wdio/screen-objects/AmountScreen.js
@@ -15,7 +15,7 @@ class AmountScreen {
}
async enterAmount(text) {
- await Gestures.tap(this.amountInputField);
+ await Gestures.waitAndTap(this.amountInputField);
await Gestures.typeText(this.amountInputField, text);
}
@@ -29,4 +29,5 @@ class AmountScreen {
expect(await element.getText()).toContain(amount);
}
}
+
export default new AmountScreen();
diff --git a/wdio/screen-objects/CommonScreen.js b/wdio/screen-objects/CommonScreen.js
index 0e5f5a39367..052bb4bf61d 100644
--- a/wdio/screen-objects/CommonScreen.js
+++ b/wdio/screen-objects/CommonScreen.js
@@ -1,6 +1,7 @@
import Selectors from '../helpers/Selectors';
import Gestures from '../helpers/Gestures';
import { ANDROID_PROGRESS_BAR, TOAST_ID } from './testIDs/Common.testIds';
+import { NOTIFICATION_TITLE } from './testIDs/Components/Notification.testIds';
class CommonScreen {
get toast() {
@@ -11,6 +12,10 @@ class CommonScreen {
return Selectors.getElementByCss(ANDROID_PROGRESS_BAR);
}
+ get TokenNotificationTitle() {
+ return Selectors.getElementByPlatform(NOTIFICATION_TITLE);
+ }
+
async waitForToastToDisplay() {
const element = await this.toast;
await element.waitForExist();
@@ -44,6 +49,11 @@ class CommonScreen {
// Taps text that contains the string
await Gestures.tapByTextContaining(text);
}
+
+ async checkNoNotification() {
+ const notification = await this.TokenNotificationTitle;
+ await notification.waitForExist({reverse: true})
+ }
}
export default new CommonScreen();
diff --git a/wdio/screen-objects/Contacts.js b/wdio/screen-objects/Contacts.js
index 36cb3748a00..8baacd7501a 100644
--- a/wdio/screen-objects/Contacts.js
+++ b/wdio/screen-objects/Contacts.js
@@ -1,68 +1,31 @@
import Gestures from '../helpers/Gestures';
import Selectors from '../helpers/Selectors';
import {
- CONTACT_NAME_INPUT,
CONTACT_ADD_BUTTON,
- CONTACT_ADDRESS_INPUT,
- CONTACT_EDIT_BUTTON,
+ CONTACTS_CONTAINER_ID,
} from './testIDs/Screens/Contacts.testids';
class Contacts {
- get contactInputfield() {
- return Selectors.getElementByPlatform(CONTACT_NAME_INPUT);
- }
-
- get addressInputField() {
- return Selectors.getElementByPlatform(CONTACT_ADDRESS_INPUT);
+ get container() {
+ return Selectors.getElementByPlatform(CONTACTS_CONTAINER_ID);
}
get addContactButton() {
return Selectors.getElementByPlatform(CONTACT_ADD_BUTTON);
}
- get editButton() {
- return Selectors.getElementByPlatform(CONTACT_EDIT_BUTTON);
+ async waitForDisplayed() {
+ const screen = await this.container;
+ await screen.waitForDisplayed();
}
- get contactOverviewNameInput() {
- return Selectors.getElementByPlatform(CONTACT_NAME_INPUT);
- }
-
- async isContactsScreenDisplayed() {
- expect(
- await Selectors.getElementByPlatform('add-contact-button'),
- ).toBeDisplayed();
- }
-
- async isAddContactButtonEnabled() {
- expect(
- await Selectors.getElementByPlatform(CONTACT_ADD_BUTTON),
- ).toBeEnabled();
- }
-
- async tapOnAddContactButton() {
- await Gestures.tap(this.addContactButton);
- await Gestures.tap(this.addContactButton);
- }
-
- async tapOnEditContactConfirmButton() {
- await Gestures.longPress(this.addContactButton, 1000);
- }
-
- async fillContactNamefield(name) {
- await Gestures.typeText(this.contactInputfield, name);
- }
-
- async fillAddressField(address) {
- await Gestures.typeText(this.addressInputField, address);
+ async tapAddContactButton() {
+ await Gestures.waitAndTap(this.addContactButton);
}
async tapOnText(text) {
await Gestures.tapTextByXpath(text);
}
-
- async tapOnEditButton() {
- await Gestures.waitAndTap(this.editButton);
- }
}
+
export default new Contacts();
diff --git a/wdio/screen-objects/EnableSecurityChecksScreen.js b/wdio/screen-objects/EnableSecurityChecksScreen.js
index b0668da9749..ceae5d7eefa 100644
--- a/wdio/screen-objects/EnableSecurityChecksScreen.js
+++ b/wdio/screen-objects/EnableSecurityChecksScreen.js
@@ -18,6 +18,8 @@ class EnableAutomaticSecurityChecksScreen {
}
async tapNoThanksButton() {
+ const element = await this.noThanksButton;
+ await element.waitForExist(1500);
await Gestures.waitAndTap(this.noThanksButton);
}
diff --git a/wdio/screen-objects/ImportSuccessScreen.js b/wdio/screen-objects/ImportSuccessScreen.js
index 0b723619926..66de9495900 100644
--- a/wdio/screen-objects/ImportSuccessScreen.js
+++ b/wdio/screen-objects/ImportSuccessScreen.js
@@ -16,8 +16,6 @@ class ImportAccountScreen {
async tapCloseButton() {
await Gestures.waitAndTap(this.closeButton);
- const closeButton = await this.closeButton;
- await closeButton.waitForDisplayed({ reverse: true });
}
async isVisible() {
diff --git a/wdio/screen-objects/Modals/AccountApprovalModal.js b/wdio/screen-objects/Modals/AccountApprovalModal.js
index dd5b5df40d0..e2db17b6a4b 100644
--- a/wdio/screen-objects/Modals/AccountApprovalModal.js
+++ b/wdio/screen-objects/Modals/AccountApprovalModal.js
@@ -43,7 +43,8 @@ class AccountApprovalModal {
}
async isVisible() {
- await expect(this.modalContainer).toBeDisplayed();
+ const modalContainer = await this.modalContainer;
+ await modalContainer.waitForDisplayed();
}
async waitForDisappear() {
diff --git a/wdio/screen-objects/Modals/AddressBookModal.js b/wdio/screen-objects/Modals/AddressBookModal.js
index 11318144ede..f96822acbe2 100644
--- a/wdio/screen-objects/Modals/AddressBookModal.js
+++ b/wdio/screen-objects/Modals/AddressBookModal.js
@@ -1,12 +1,22 @@
import Gestures from '../../helpers/Gestures';
import Selectors from '../../helpers/Selectors';
import { ENTER_ALIAS_INPUT_BOX_ID } from '../testIDs/Screens/AddressBook.testids';
+import { ADD_ADDRESS_MODAL_CONTAINER_ID } from '../../../app/constants/test-ids';
class AddressBookModal {
+ get container() {
+ return Selectors.getElementByPlatform(ADD_ADDRESS_MODAL_CONTAINER_ID);
+ }
+
get addressInputField() {
return Selectors.getElementByPlatform(ENTER_ALIAS_INPUT_BOX_ID);
}
+ async waitForDisplayed() {
+ const container = await this.container;
+ await container.waitForDisplayed();
+ }
+
async fillAddressAliasField(text) {
await Gestures.typeText(this.addressInputField, text);
}
@@ -31,4 +41,5 @@ class AddressBookModal {
expect(await Selectors.getXpathElementByText(contact)).not.toBeDisplayed();
}
}
+
export default new AddressBookModal();
diff --git a/wdio/screen-objects/Modals/DeleteContactModal.js b/wdio/screen-objects/Modals/DeleteContactModal.js
new file mode 100644
index 00000000000..7a8a16f781d
--- /dev/null
+++ b/wdio/screen-objects/Modals/DeleteContactModal.js
@@ -0,0 +1,36 @@
+import Selectors from '../../helpers/Selectors';
+import {
+ DELETE_CONTACT_MODAL_CANCEL_BUTTON,
+ DELETE_CONTACT_MODAL_DELETE_BUTTON,
+ DELETE_CONTACT_MODAL_TITLE,
+} from '../testIDs/Components/DeleteContactModal.testIds';
+import Gestures from '../../helpers/Gestures';
+
+class DeleteContactModal {
+ get title() {
+ return Selectors.getXpathElementByText(DELETE_CONTACT_MODAL_TITLE);
+ }
+
+ get deleteButton() {
+ return Selectors.getXpathElementByText(DELETE_CONTACT_MODAL_DELETE_BUTTON);
+ }
+
+ get cancelButton() {
+ return Selectors.getXpathElementByText(DELETE_CONTACT_MODAL_CANCEL_BUTTON);
+ }
+
+ async waitForTitle() {
+ const title = await this.title;
+ await title.waitForDisplayed();
+ }
+
+ async tapDeleteButton() {
+ await Gestures.waitAndTap(this.deleteButton);
+ }
+
+ async tapCancelButton() {
+ await Gestures.waitAndTap(this.cancelButton);
+ }
+}
+
+export default new DeleteContactModal();
diff --git a/wdio/screen-objects/Modals/SkipAccountSecurityModal.js b/wdio/screen-objects/Modals/SkipAccountSecurityModal.js
index 4077004d93b..ec2326b0461 100644
--- a/wdio/screen-objects/Modals/SkipAccountSecurityModal.js
+++ b/wdio/screen-objects/Modals/SkipAccountSecurityModal.js
@@ -17,8 +17,8 @@ class SkipAccountSecurityModal {
async proceedWithoutWalletSecure() {
const setTimeout = 2000;
await driver.pause(setTimeout);
- await Gestures.tap(await this.skipBackupText);
- await Gestures.tap(await this.skipButton);
+ await Gestures.waitAndTap(this.skipBackupText);
+ await Gestures.waitAndTap(this.skipButton);
}
async isVisible() {
diff --git a/wdio/screen-objects/Modals/TabBarModal.js b/wdio/screen-objects/Modals/TabBarModal.js
index dbff4736c21..b8307875c0b 100644
--- a/wdio/screen-objects/Modals/TabBarModal.js
+++ b/wdio/screen-objects/Modals/TabBarModal.js
@@ -39,7 +39,9 @@ class TabBarModal {
}
async tapActionButton() {
- await Gestures.waitAndTap(this.actionButton);
+ const actionButton = await this.actionButton
+ await actionButton.waitForExist();
+ await Gestures.longPress(actionButton, 500);
}
}
diff --git a/wdio/screen-objects/Modals/TermOfUseScreen.js b/wdio/screen-objects/Modals/TermOfUseScreen.js
index 038452e417d..70b3ed2e811 100644
--- a/wdio/screen-objects/Modals/TermOfUseScreen.js
+++ b/wdio/screen-objects/Modals/TermOfUseScreen.js
@@ -32,11 +32,18 @@ class TermOfUseScreen {
}
async isDisplayed() {
- await expect(await this.container).toBeDisplayed();
+ const container = await this.container;
+ await container.waitForDisplayed();
+ }
+
+ async textIsDisplayed() {
+ const termsText = await Selectors.getXpathElementByTextContains('Last Updated')
+ await termsText.waitForDisplayed();
}
async isNotDisplayed() {
- await expect(await this.container).not.toBeExisting();
+ const container = await this.container;
+ await container.waitForExist({ reverse: true});
}
async tapAgreeCheckBox() {
@@ -50,6 +57,16 @@ class TermOfUseScreen {
await driver.pause(500);
}
+ async acceptIsEnabled() {
+ const element = await this.acceptButton;
+ return element.isEnabled();
+ }
+
+ async isCheckBoxChecked() {
+ const element = await this.checkbox;
+ return element.isEnabled();
+ }
+
async tapAcceptButton() {
await Gestures.tap(this.acceptButton);
}
diff --git a/wdio/screen-objects/Onboarding/CreateNewWalletScreen.js b/wdio/screen-objects/Onboarding/CreateNewWalletScreen.js
index dd44f82119b..80e7f5af8e3 100644
--- a/wdio/screen-objects/Onboarding/CreateNewWalletScreen.js
+++ b/wdio/screen-objects/Onboarding/CreateNewWalletScreen.js
@@ -12,7 +12,7 @@ import { SKIP_BUTTON } from '../testIDs/Components/SkipAccountSecurityModalTestI
import Gestures from '../../helpers/Gestures';
import Selectors from '../../helpers/Selectors';
-class CreateNewWalletScren {
+class CreateNewWalletScreen {
// selectors ====================================
get description() {
return Selectors.getElementByPlatform(WALLET_SETUP_SCREEN_DESCRIPTION_ID);
@@ -57,8 +57,8 @@ class CreateNewWalletScren {
async inputConfirmPasswordField(secondPassword) {
await Gestures.typeText(this.newWalletPasswordConfirm, secondPassword);
await driver.hideKeyboard();
- await Gestures.tap(this.termsAndConditionCheckBox);
- await Gestures.tap(this.newWalletSubmitButton);
+ await Gestures.waitAndTap(this.termsAndConditionCheckBox);
+ await Gestures.waitAndTap(this.newWalletSubmitButton);
}
async inputConfirmResetPasswordField(secondPassword) {
@@ -67,11 +67,11 @@ class CreateNewWalletScren {
}
async tapSubmitButton() {
- await Gestures.tap(this.newWalletSubmitButton);
+ await Gestures.waitAndTap(this.newWalletSubmitButton);
}
async tapRemindMeLater() {
- await Gestures.tap(this.remindMeLaterButton);
+ await Gestures.waitAndTap(this.remindMeLaterButton);
}
async isAccountCreated() {
@@ -92,8 +92,9 @@ class CreateNewWalletScren {
}
async isNotVisible() {
- await expect(this.SECURE_WALLET_SCREEN).not.toBeDisplayed();
+ const secureWalletScreen = await this.secureWalletScreen;
+ await secureWalletScreen.waitForExist({ reverse: true });
}
}
-export default new CreateNewWalletScren();
+export default new CreateNewWalletScreen();
diff --git a/wdio/screen-objects/Onboarding/OnboardingCarousel.js b/wdio/screen-objects/Onboarding/OnboardingCarousel.js
index 914b9499dba..3f4bc9ff245 100644
--- a/wdio/screen-objects/Onboarding/OnboardingCarousel.js
+++ b/wdio/screen-objects/Onboarding/OnboardingCarousel.js
@@ -31,8 +31,15 @@ class WelcomeScreen {
async waitForSplashAnimationToDisplay() {
const elem = await this.splashScreenMetamaskAnimationId;
- await elem.waitForExist();
+ const getStartedElem = await this.getStartedButton;
+ try {
+ await elem.waitForExist();
+ } catch (error) {
+ console.log(`Splash screen animation element '${this.splashScreenMetamaskAnimationId}' not found`)
+ await getStartedElem.waitForExist();
+ }
}
+
async waitForSplashAnimationToNotExit() {
const elem = await this.splashScreenMetamaskAnimationId;
diff --git a/wdio/screen-objects/SendScreen.js b/wdio/screen-objects/SendScreen.js
index bec01768ec9..f47b600aa56 100644
--- a/wdio/screen-objects/SendScreen.js
+++ b/wdio/screen-objects/SendScreen.js
@@ -1,16 +1,21 @@
import Gestures from '../helpers/Gestures';
import Selectors from '../helpers/Selectors';
import {
+ ADD_ADDRESS_BUTTON,
+ AMOUNT_SCREEN,
SEND_ADDRESS_INPUT_FIELD,
+ SEND_CANCEL_BUTTON,
+ SEND_SCREEN_ID,
SEND_WARNING_MESSAGE,
UNDERSTAND_WARNING_CONTINUE,
- AMOUNT_SCREEN,
- ADD_ADDRESS_BUTTON,
- SEND_CANCEL_BUTTON,
} from './testIDs/Screens/SendScreen.testIds';
import { TRANSACTION_AMOUNT_INPUT } from './testIDs/Screens/AmountScreen.testIds.js';
class SendScreen {
+ get container() {
+ return Selectors.getElementByPlatform(SEND_SCREEN_ID);
+ }
+
get sendAddressInputField() {
return Selectors.getElementByPlatform(SEND_ADDRESS_INPUT_FIELD);
}
@@ -65,11 +70,12 @@ class SendScreen {
}
async tapCancelButton() {
- await Gestures.tap(this.sendCancelButton);
+ await Gestures.waitAndTap(this.sendCancelButton);
}
async isAmountScreenDisplayed() {
- expect(await this.amountScreen).toBeDisplayed();
+ const amountScreen = await this.amountScreen;
+ await amountScreen.waitForDisplayed();
}
async isChangedContactNameVisible(contactName) {
@@ -79,8 +85,15 @@ class SendScreen {
async isContactNameVisible(contact) {
expect(await Selectors.getXpathElementByText(contact)).toBeDisplayed();
}
+
async isDeletedContactNameNotVisible(contact) {
expect(await Selectors.getXpathElementByText(contact)).not.toBeDisplayed();
}
+
+ async waitForDisplayed() {
+ const screen = await this.container;
+ await screen.waitForDisplayed();
+ }
}
+
export default new SendScreen();
diff --git a/wdio/screen-objects/TokenOverviewScreen.js b/wdio/screen-objects/TokenOverviewScreen.js
index 0bb6664d5e1..df8282562fd 100644
--- a/wdio/screen-objects/TokenOverviewScreen.js
+++ b/wdio/screen-objects/TokenOverviewScreen.js
@@ -20,7 +20,9 @@ class TokenOverviewScreen {
}
async tapBackButton() {
- await Gestures.tap(this.backButtonTokenOverview);
+ const element = await this.backButtonTokenOverview;
+ await element.waitForDisplayed();
+ await Gestures.waitAndTap(element);
}
async isTokenOverviewVisible() {
diff --git a/wdio/screen-objects/TransactionConfirmScreen.js b/wdio/screen-objects/TransactionConfirmScreen.js
index 89f6d5ba51e..df149d171ec 100644
--- a/wdio/screen-objects/TransactionConfirmScreen.js
+++ b/wdio/screen-objects/TransactionConfirmScreen.js
@@ -1,23 +1,39 @@
import Selectors from '../helpers/Selectors';
-import { COMFIRM_TXN_AMOUNT } from './testIDs/Screens/TransactionConfirm.testIds';
+import {
+ COMFIRM_TXN_AMOUNT,
+ CONFIRM_TRANSACTION_BUTTON_ID,
+} from './testIDs/Screens/TransactionConfirm.testIds';
+import Gestures from '../helpers/Gestures';
class TransactionConfirmScreen {
get confirmAmount() {
return Selectors.getElementByPlatform(COMFIRM_TXN_AMOUNT);
}
+ get sendButton() {
+ return Selectors.getElementByPlatform(CONFIRM_TRANSACTION_BUTTON_ID);
+ }
+
async isCorrectTokenConfirm(token) {
- const element = await this.confirmAmount;
- expect(await element.getText()).toContain(token);
+ const confirmAmount = await this.confirmAmount;
+ await confirmAmount.waitForDisplayed();
+ expect(confirmAmount).toHaveTextContaining(token);
}
async isCorrectTokenAmountDisplayed(amount) {
- const element = await this.confirmAmount;
- expect(await element.getText()).toContain(amount);
+ const confirmAmount = await this.confirmAmount;
+ await confirmAmount.waitForDisplayed();
+ expect(confirmAmount).toHaveTextContaining(amount);
}
async isConfirmScreenVisible() {
- expect(await this.confirmAmount).toBeDisplayed();
+ const confirmAmount = await this.confirmAmount;
+ await confirmAmount.waitForDisplayed();
+ }
+
+ async tapSendButton() {
+ await Gestures.waitAndTap(this.sendButton);
}
}
+
export default new TransactionConfirmScreen();
diff --git a/wdio/screen-objects/WalletMainScreen.js b/wdio/screen-objects/WalletMainScreen.js
index 030bfc2e0b0..efa5a79aeb7 100644
--- a/wdio/screen-objects/WalletMainScreen.js
+++ b/wdio/screen-objects/WalletMainScreen.js
@@ -1,6 +1,6 @@
import Selectors from '../helpers/Selectors';
import Gestures from '../helpers/Gestures.js';
-import {WALLET_CONTAINER_ID} from './testIDs/Screens/WalletScreen-testIds.js';
+import { WALLET_CONTAINER_ID } from './testIDs/Screens/WalletScreen-testIds.js';
import {
ONBOARDING_WIZARD_STEP_1_CONTAINER_ID,
ONBOARDING_WIZARD_STEP_1_NO_THANKS_ID,
@@ -23,11 +23,11 @@ import {
WALLET_VIEW_BURGER_ICON_ID,
} from './testIDs/Screens/WalletView.testIds';
-import {DRAWER_VIEW_SETTINGS_TEXT_ID} from './testIDs/Screens/DrawerView.testIds';
+import { DRAWER_VIEW_SETTINGS_TEXT_ID } from './testIDs/Screens/DrawerView.testIds';
-import {NOTIFICATION_TITLE} from './testIDs/Components/Notification.testIds';
-import {TAB_BAR_WALLET_BUTTON} from './testIDs/Components/TabBar.testIds';
-import {BACK_BUTTON_SIMPLE_WEBVIEW} from './testIDs/Components/SimpleWebView.testIds';
+import { NOTIFICATION_TITLE } from './testIDs/Components/Notification.testIds';
+import { TAB_BAR_WALLET_BUTTON } from './testIDs/Components/TabBar.testIds';
+import { BACK_BUTTON_SIMPLE_WEBVIEW } from './testIDs/Components/SimpleWebView.testIds';
class WalletMainScreen {
get wizardContainer() {
@@ -152,7 +152,7 @@ class WalletMainScreen {
}
async tapImportNFTButton() {
- await Gestures.swipe({x: 100, y: 500}, {x: 100, y: 10});
+ await Gestures.swipe({ x: 100, y: 500 }, { x: 100, y: 10 });
await Gestures.waitAndTap(this.ImportNFT);
}
@@ -190,7 +190,7 @@ class WalletMainScreen {
async isTokenTextVisible(token) {
const tokenText = await Selectors.getXpathElementByTextContains(token);
await expect(tokenText).toBeDisplayed();
- await tokenText.waitForExist({reverse: true});
+ await tokenText.waitForExist({ reverse: true });
}
async isOnboardingWizardVisible() {
@@ -199,13 +199,13 @@ class WalletMainScreen {
async isMainWalletViewVisible() {
const element = await this.mainWalletView;
- await element.waitForDisplayed();
+ await element.waitForDisplayed(2500);
}
async waitForNotificationToDisplayed() {
const element = await this.TokenNotificationTitle;
await element.waitForDisplayed();
- await element.waitForExist({reverse: true});
+ await element.waitForExist({ reverse: true });
}
async isToastNotificationDisplayed() {
@@ -213,7 +213,7 @@ class WalletMainScreen {
await element.waitForDisplayed();
expect(await element.getText()).toContain('Transaction');
expect(await element.getText()).toContain('Complete!');
- await element.waitForExist({reverse: true});
+ await element.waitForExist({ reverse: true });
}
async isNetworkNavbarTitle(text) {
diff --git a/wdio/screen-objects/testIDs/Common.testIds.js b/wdio/screen-objects/testIDs/Common.testIds.js
index 2641fb91d4a..c3fc5be7aba 100644
--- a/wdio/screen-objects/testIDs/Common.testIds.js
+++ b/wdio/screen-objects/testIDs/Common.testIds.js
@@ -1,3 +1,3 @@
export const TOAST_ID = 'toast';
-
export const ANDROID_PROGRESS_BAR = 'android.widget.ProgressBar';
+export const EDIT_BUTTON = 'edit-button';
diff --git a/wdio/screen-objects/testIDs/Components/DeleteContactModal.testIds.js b/wdio/screen-objects/testIDs/Components/DeleteContactModal.testIds.js
new file mode 100644
index 00000000000..7582806da3e
--- /dev/null
+++ b/wdio/screen-objects/testIDs/Components/DeleteContactModal.testIds.js
@@ -0,0 +1,3 @@
+export const DELETE_CONTACT_MODAL_TITLE = 'Delete contact';
+export const DELETE_CONTACT_MODAL_DELETE_BUTTON = 'Delete';
+export const DELETE_CONTACT_MODAL_CANCEL_BUTTON = 'Cancel';
diff --git a/wdio/screen-objects/testIDs/Screens/AddContact.testIds.js b/wdio/screen-objects/testIDs/Screens/AddContact.testIds.js
new file mode 100644
index 00000000000..8048803f9d6
--- /dev/null
+++ b/wdio/screen-objects/testIDs/Screens/AddContact.testIds.js
@@ -0,0 +1,6 @@
+export const ADD_CONTACTS_CONTAINER_ID = 'add-contacts-screen';
+export const ADD_CONTACT_NAME_INPUT = 'add-contact-name-input';
+export const ADD_CONTACT_ADDRESS_INPUT = 'add-contact-address-input';
+export const ADD_CONTACT_MEMO_INPUT = 'add-contact-memo-input';
+export const ADD_CONTACT_ADD_BUTTON = 'add-contact-add-contact-button';
+export const ADD_CONTACT_DELETE_BUTTON = 'add-contact-delete-contact-button';
diff --git a/wdio/screen-objects/testIDs/Screens/Contacts.testids.js b/wdio/screen-objects/testIDs/Screens/Contacts.testids.js
index 1a9ac5e35c7..a54c00ddcca 100644
--- a/wdio/screen-objects/testIDs/Screens/Contacts.testids.js
+++ b/wdio/screen-objects/testIDs/Screens/Contacts.testids.js
@@ -1,9 +1,2 @@
-// eslint-disable-next-line import/prefer-default-export
-export const CONTACT_NAME_INPUT = 'contact-name-input';
-
export const CONTACT_ADD_BUTTON = 'contact-add-contact-button';
-
-export const CONTACT_ADDRESS_INPUT = 'contact-address-input';
-
-export const CONTACT_EDIT_BUTTON = 'contact-edit-button';
export const CONTACTS_CONTAINER_ID = 'contacts-screen';
diff --git a/wdio/screen-objects/testIDs/Screens/SendScreen.testIds.js b/wdio/screen-objects/testIDs/Screens/SendScreen.testIds.js
index b612c315970..065455d4ea8 100644
--- a/wdio/screen-objects/testIDs/Screens/SendScreen.testIds.js
+++ b/wdio/screen-objects/testIDs/Screens/SendScreen.testIds.js
@@ -12,3 +12,4 @@ export const ADD_THIS_ADDRESS_TO_BOOK = 'add-to-address-book';
export const SEND_CANCEL_BUTTON = 'send-cancel-button';
export const ADD_ADDRESS_BUTTON = 'add-address-button-a';
+export const SEND_SCREEN_ID = 'send-screen';
diff --git a/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js b/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js
index 485c3a9308f..41e69630805 100644
--- a/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js
+++ b/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js
@@ -1,2 +1,6 @@
// eslint-disable-next-line import/prefer-default-export
export const COMFIRM_TXN_AMOUNT = 'confirm-txn-amount';
+export const TRANSACTION_VIEW_CONTAINER_ID = 'txn-confirm-screen';
+export const CONFIRM_TRANSACTION_BUTTON_ID = 'txn-confirm-send-button';
+export const NAVBAR_TITLE_TEXT = 'navbar-title-text';
+export const TRANSACTION_ACCOUNT_BALANCE = 'account-balance';
diff --git a/wdio/step-definitions/common-steps.js b/wdio/step-definitions/common-steps.js
index 185215a294b..e56658cb848 100644
--- a/wdio/step-definitions/common-steps.js
+++ b/wdio/step-definitions/common-steps.js
@@ -13,7 +13,12 @@ import SkipAccountSecurityModal from '../screen-objects/Modals/SkipAccountSecuri
import OnboardingWizardModal from '../screen-objects/Modals/OnboardingWizardModal.js';
import LoginScreen from '../screen-objects/LoginScreen';
import TermOfUseScreen from '../screen-objects/Modals/TermOfUseScreen';
-import WhatsNewModal from "../screen-objects/Modals/WhatsNewModal";
+import WhatsNewModal from '../screen-objects/Modals/WhatsNewModal';
+
+import Ganache from '../../app/util/test/ganache';
+
+const ganacheServer = new Ganache();
+const validAccount = Accounts.getValidAccount();
Then(/^the Welcome Screen is displayed$/, async () => {
await WelcomeScreen.waitForScreenToDisplay();
@@ -29,6 +34,7 @@ Given(/^the splash animation disappears$/, async () => {
Then(/^Terms of Use is displayed$/, async () => {
await TermOfUseScreen.isDisplayed();
+ await TermOfUseScreen.textIsDisplayed();
});
When(/^I agree to terms$/, async () => {
@@ -51,10 +57,15 @@ Given(/^I have imported my wallet$/, async () => {
await MetaMetricsScreen.isScreenTitleVisible();
await MetaMetricsScreen.tapIAgreeButton();
await TermOfUseScreen.isDisplayed();
+ await TermOfUseScreen.textIsDisplayed();
await TermOfUseScreen.tapAgreeCheckBox();
await TermOfUseScreen.tapScrollEndButton();
- await driver.pause();
- await TermOfUseScreen.tapAcceptButton();
+ if (!await TermOfUseScreen.isCheckBoxChecked()){
+ await TermOfUseScreen.tapAgreeCheckBox();
+ await TermOfUseScreen.tapAcceptButton();
+ } else {
+ await TermOfUseScreen.tapAcceptButton();
+ }
await ImportFromSeedScreen.isScreenTitleVisible();
await ImportFromSeedScreen.typeSecretRecoveryPhrase(validAccount.seedPhrase);
await ImportFromSeedScreen.typeNewPassword(validAccount.password);
@@ -116,7 +127,7 @@ Given(/^I tap No thanks on the onboarding welcome tutorial/, async () => {
});
Then(/^"([^"]*)?" is visible/, async (text) => {
- const timeout = 1000;
+ const timeout = 2500;
await driver.pause(timeout);
await CommonScreen.isTextDisplayed(text);
});
@@ -133,6 +144,12 @@ Then(/^"([^"]*)?" is not displayed/, async (text) => {
await CommonScreen.isTextElementNotDisplayed(text);
});
+Then(/^"([^"]*)?" is displayed/, async (text) => {
+ const timeout = 1000;
+ await driver.pause(timeout);
+ await CommonScreen.isTextDisplayed(text);
+});
+
Then(/^Sending token takes me to main wallet view/, async () => {
const timeout = 1000;
await driver.pause(timeout);
@@ -185,6 +202,7 @@ When(/^I unlock wallet with (.*)$/, async (password) => {
Then(
/^I tap (.*) "([^"]*)?" on (.*) (.*) view/,
async (elementType, button, screen, type) => {
+ await CommonScreen.checkNoNotification(); // Notification appears a little late and inteferes with clicking function
await CommonScreen.tapOnText(button);
},
);
@@ -223,8 +241,17 @@ When(/^the toast is displayed$/, async () => {
await CommonScreen.waitForToastToDisplay();
await CommonScreen.waitForToastToDisappear();
});
+
Given(/^I close the Whats New modal$/, async () => {
await WhatsNewModal.waitForDisplay();
await WhatsNewModal.tapCloseButton();
await WhatsNewModal.waitForDisappear();
});
+
+Given(/^Ganache server is started$/, async () => {
+ await ganacheServer.start({ mnemonic: validAccount.seedPhrase });
+});
+
+Then(/^Ganache server is stopped$/, async () => {
+ await ganacheServer.quit();
+});
\ No newline at end of file
diff --git a/wdio/step-definitions/contacts.steps.js b/wdio/step-definitions/contacts.steps.js
index 7888c3ea631..4c7f3bef33b 100644
--- a/wdio/step-definitions/contacts.steps.js
+++ b/wdio/step-definitions/contacts.steps.js
@@ -1,24 +1,25 @@
-import { Then } from '@wdio/cucumber-framework';
+import { Given, Then, When } from '@wdio/cucumber-framework';
import AddressBookModal from '../screen-objects/Modals/AddressBookModal';
import Contacts from '../screen-objects/Contacts';
import AddContact from '../screen-objects/AddContact';
import CommonScreen from '../screen-objects/CommonScreen';
+import DeleteContactModal from '../screen-objects/Modals/DeleteContactModal';
Then(/^I am on the contacts view/, async () => {
- await Contacts.isContactsScreenDisplayed();
+ await Contacts.waitForDisplayed();
});
+
Then(/^I tap on the "([^"]*)?" button/, async (text) => {
- await driver.pause(2000);
await CommonScreen.tapOnText(text);
});
Then(/^I tap button Add contact which is now enabled/, async () => {
- await Contacts.isAddContactButtonEnabled();
- await AddContact.tapOnAddContactButton();
+ await AddContact.tapAddContactButton();
});
Then(/^I input "([^"]*)?" into the contact name field/, async (name) => {
- await AddContact.fillContactNamefield(name);
+ await AddContact.waitForDisplay();
+ await AddContact.fillContactNameField(name);
});
Then(/^I input "([^"]*)?" in the Address field/, async (name) => {
@@ -31,12 +32,14 @@ Then(/^I tap on contact name "([^"]*)?"/, async (name) => {
});
Then(/^the saved contact "([^"]*)?" should appear/, async (contactName) => {
+ await Contacts.waitForDisplayed();
await AddressBookModal.isContactNameVisible(contactName);
});
Then(
/^the deleted contact "([^"]*)?" should not appear/,
async (contactName) => {
+ await Contacts.waitForDisplayed();
await AddressBookModal.isDeletedContactNameNotVisible(contactName);
},
);
@@ -49,18 +52,21 @@ Then(
);
Then(/I tap on Edit button to edit Saved contact details/, async () => {
- await Contacts.tapOnEditButton();
+ await AddContact.waitForDisplay();
+ await AddContact.tapEditButton();
+ await AddContact.waitForAddContactButton();
});
Then(/I can edit the contact name to "([^"]*)?"/, async (name) => {
await AddContact.waitForAddContactButton();
- await AddContact.changeContactName(name);
+ await AddContact.fillContactNameField(name);
+ await driver.hideKeyboard();
});
Then(
/^I tap the Edit Contact button which is enabled to confirm the change/,
async () => {
- await Contacts.tapOnAddContactButton(); // same Id as Edit Contact button
+ await AddContact.tapAddContactButton();
},
);
@@ -72,3 +78,12 @@ Then(
await AddressBookModal.isDeletedContactNameNotVisible(contactName);
},
);
+Given(/^I tap on the Add contact button on the Contact view$/, async () => {
+ await Contacts.tapAddContactButton();
+});
+
+When(/^I tap button Delete to navigate to Contacts view$/, async () => {
+ await AddContact.tapDeleteButton();
+ await DeleteContactModal.waitForTitle();
+ await DeleteContactModal.tapDeleteButton();
+});
diff --git a/wdio/step-definitions/create-new-wallet-account.steps.js b/wdio/step-definitions/create-new-wallet-account.steps.js
index d6b67f87427..a2f29f9e2b4 100644
--- a/wdio/step-definitions/create-new-wallet-account.steps.js
+++ b/wdio/step-definitions/create-new-wallet-account.steps.js
@@ -16,8 +16,6 @@ When(/^A new account is created/, async () => {
});
Then(/^I am on the new account/, async () => {
- await AccountListComponent.tapAccount('Account 2');
await WalletMainScreen.tapIdenticon();
- await AccountListComponent.isComponentNotDisplayed();
- await WalletAccountModal.isAccountNameLabelEqualTo('Account 2');
+ await WalletAccountModal.isAccountNameLabelEqualTo('Account 2'); // this could be better. This stemp could be a bit more dynmic
});
diff --git a/wdio/step-definitions/import-wallet-via-private-key.steps.js b/wdio/step-definitions/import-wallet-via-private-key.steps.js
index 53510a40bf7..c4e26cf87ce 100644
--- a/wdio/step-definitions/import-wallet-via-private-key.steps.js
+++ b/wdio/step-definitions/import-wallet-via-private-key.steps.js
@@ -3,6 +3,7 @@ import { Then, When } from '@wdio/cucumber-framework';
import AccountListComponent from '../screen-objects/AccountListComponent';
import ImportAccountScreen from '../screen-objects/ImportAccountScreen';
import ImportSuccessScreen from '../screen-objects/ImportSuccessScreen';
+import WalletMainScreen from "../screen-objects/WalletMainScreen";
When(/^I tap on Import an account/, async () => {
await driver.pause(setTimeout);
@@ -30,6 +31,7 @@ Then(/^The account is imported/, async () => {
});
Then(/^I should see an error (.*)/, async (errorMessage) => {
+ await driver.pause(1000);
await ImportAccountScreen.isAlertTextVisible(errorMessage);
await driver.acceptAlert();
});
diff --git a/wdio/step-definitions/onboarding.steps.js b/wdio/step-definitions/onboarding.steps.js
index 48d0eab2926..c020b5c522a 100644
--- a/wdio/step-definitions/onboarding.steps.js
+++ b/wdio/step-definitions/onboarding.steps.js
@@ -16,9 +16,6 @@ Then(/^"([^"]*)?" is displayed/, async (text) => {
await WelcomeScreen.waitForSplashAnimationToDisplay();
await WelcomeScreen.waitForSplashAnimationToNotExit();
break;
- case 'Wallet setup':
- await OnboardingScreen.isScreenTitleVisible();
- break;
case 'Import an existing wallet or create a new one':
await OnboardingScreen.isScreenDescriptionVisible();
break;
@@ -91,6 +88,11 @@ When(/^I tap "([^"]*)"/, async (text) => {
}
});
+Then(/^Wallet setup screen is displayed/, async () => {
+ // await driver.pause(3000);
+ await OnboardingScreen.isScreenTitleVisible();
+});
+
When(/^I type (.*) in SRP field/, async (text) => {
await ImportFromSeedScreen.typeSecretRecoveryPhrase(text);
});
diff --git a/wdio/step-definitions/send-flow.steps.js b/wdio/step-definitions/send-flow.steps.js
index 849f652fcaf..0a76720c5fa 100644
--- a/wdio/step-definitions/send-flow.steps.js
+++ b/wdio/step-definitions/send-flow.steps.js
@@ -6,6 +6,7 @@ import AmountScreen from '../screen-objects/AmountScreen';
import WalletMainScreen from '../screen-objects/WalletMainScreen';
import TokenOverviewScreen from '../screen-objects/TokenOverviewScreen';
import TransactionConfirmScreen from '../screen-objects/TransactionConfirmScreen';
+import CommonScreen from '../screen-objects/CommonScreen';
Then(/^On the Address book modal Cancel button is enabled/, async () => {
await AddressBookModal.isCancelButtonEnabled();
@@ -30,7 +31,9 @@ Then(/^I tap the Save button/, async () => {
Given(
/^I enter address "([^"]*)?" in the sender's input box/,
async (address) => {
+ await CommonScreen.checkNoNotification(); // Notification appears a little late and inteferes with clicking function
await SendScreen.typeAddressInSendAddressField(address);
+ await driver.hideKeyboard();
},
);
@@ -72,10 +75,19 @@ Then(/^I navigate to the main wallet screen/, async () => {
Then(
/^I should see the edited name "([^"]*)?" contact under Recents on the send screen/,
async (name) => {
+ await SendScreen.waitForDisplayed();
+ await driver.hideKeyboard();
await SendScreen.isChangedContactNameVisible(name);
},
);
+Then(
+ /^I should not see the edited name "([^"]*)" contact under Recents on the send screen$/,
+ async (name) => {
+ await SendScreen.isDeletedContactNameNotVisible(name);
+ },
+);
+
Then(/^I navigate to the main wallet view from Send screen/, async () => {
await SendScreen.tapCancelButton();
});
@@ -95,11 +107,13 @@ Then(
/^I enter invalid address "([^"]*)?" into senders input field/,
async (address) => {
await SendScreen.typeAddressInSendAddressField(address);
+ await driver.hideKeyboard();
},
);
Then(/^I type amount "([^"]*)?" into amount input field/, async (amount) => {
await AmountScreen.enterAmount(amount);
+ await driver.hideKeyboard();
});
Then(
@@ -115,8 +129,12 @@ Then(/^I am taken to the token overview screen/, async () => {
Then(/^I tap back from the Token overview page/, async () => {
await TokenOverviewScreen.tapBackButton();
+ await TokenOverviewScreen.tapBackButton();// Double tap seems to work best on BS
});
When(/^I tap button Send on Token screen view$/, async () => {
await TokenOverviewScreen.tapSendButton();
});
+When(/^I tap button Send on Confirm Amount view$/, async () => {
+ await TransactionConfirmScreen.tapSendButton();
+});
diff --git a/wdio/step-definitions/wallet-view.steps.js b/wdio/step-definitions/wallet-view.steps.js
index 6490eec7021..bb143e4539f 100644
--- a/wdio/step-definitions/wallet-view.steps.js
+++ b/wdio/step-definitions/wallet-view.steps.js
@@ -77,6 +77,7 @@ Then(/^Wallet view is displayed$/, async () => {
});
Given(/^On the Main Wallet view I tap on the Send Action$/, async () => {
+ await CommonScreen.checkNoNotification();
await TabBarModal.tapActionButton();
await WalletActionModal.tapSendButton();
});
diff --git a/wdio/utils/generateTestReports.js b/wdio/utils/generateTestReports.js
index 60fab93507a..0c413b5e444 100644
--- a/wdio/utils/generateTestReports.js
+++ b/wdio/utils/generateTestReports.js
@@ -1,6 +1,13 @@
-const fs = require('fs');
-const xml2js = require('xml2js');
-const { generate } = require('multiple-cucumber-html-reporter');
+import {
+ readdirSync,
+ readFileSync,
+ existsSync,
+ mkdirSync,
+ renameSync,
+ writeFileSync,
+} from 'fs';
+import { Parser } from 'xml2js';
+import { generate } from 'multiple-cucumber-html-reporter';
function generateTestReports() {
// Generate the report when it all tests are done
@@ -10,37 +17,38 @@ function generateTestReports() {
// for more options see https://github.com/wswebcreation/multiple-cucumber-html-reporter#options
});
- const testSuites = fs.readdirSync('./wdio/reports/junit-results');
+ const testSuites = readdirSync('./wdio/reports/junit-results');
testSuites.forEach((testSuite) => {
- const file = fs.readFileSync(
+ const file = readFileSync(
`./wdio/reports/junit-results/${testSuite}`,
'utf8',
);
- const parser = new xml2js.Parser();
+ const parser = new Parser();
parser.parseString(file, (err, result) => {
- if (err) {
- throw err;
- }
- const suiteName = result.testsuites.testsuite[0].$.name;
- // Create dir for each test suite
- if (!fs.existsSync(`./wdio/reports/junit-results/${suiteName}`)) {
- fs.mkdirSync(`./wdio/reports/junit-results/${suiteName}`);
- fs.renameSync(
- `./wdio/reports/junit-results/${testSuite}`,
- `./wdio/reports/junit-results/${suiteName}/${suiteName}.xml`,
- );
- // Create test-info.json file for each test suite
- const testInfo = {
- 'test-name': suiteName,
- };
- fs.writeFileSync(
- `./wdio/reports/junit-results/${suiteName}/test-info.json`,
- JSON.stringify(testInfo),
- );
+ try {
+ const suiteName = result.testsuites.testsuite[0].$.name;
+ // Create dir for each test suite
+ if (!existsSync(`./wdio/reports/junit-results/${suiteName}`)) {
+ mkdirSync(`./wdio/reports/junit-results/${suiteName}`);
+ renameSync(
+ `./wdio/reports/junit-results/${testSuite}`,
+ `./wdio/reports/junit-results/${suiteName}/${suiteName}.xml`,
+ );
+ // Create test-info.json file for each test suite
+ const testInfo = {
+ 'test-name': suiteName,
+ };
+ writeFileSync(
+ `./wdio/reports/junit-results/${suiteName}/test-info.json`,
+ JSON.stringify(testInfo),
+ );
+ }
+ } catch (error) {
+ //do nothing for now
}
});
});
}
-module.exports = generateTestReports;
+export default generateTestReports;
diff --git a/yarn.lock b/yarn.lock
index 1c1e6dbfe68..be5264913de 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6454,6 +6454,11 @@
resolved "https://registry.yarnpkg.com/@types/is-glob/-/is-glob-4.0.2.tgz#c243dd0d09eac2992130142419ff2308ffd988bf"
integrity sha512-4j5G9Y5jljDSICQ1R2f/Rcyoj6DZmYGneny+p/cDkjep0rkqNg0W73Ty0bVjMUTZgLXHf8oiMjg1XC3CDwCz+g==
+"@types/is-url@^1.2.30":
+ version "1.2.30"
+ resolved "https://registry.yarnpkg.com/@types/is-url/-/is-url-1.2.30.tgz#85567e8bee4fee69202bc3448f9fb34b0d56c50a"
+ integrity sha512-AnlNFwjzC8XLda5VjRl4ItSd8qp8pSNowvsut0WwQyBWHpOxjxRJm8iO6uETWqEyLdYdb9/1j+Qd9gQ4l5I4fw==
+
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@@ -6832,6 +6837,11 @@
resolved "https://registry.yarnpkg.com/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz#9bd0b47f26b5a3151be21ba4ce9f5fa457c5f190"
integrity sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==
+"@types/url-parse@^1.4.8":
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.8.tgz#c3825047efbca1295b7f1646f38203d9145130d6"
+ integrity sha512-zqqcGKyNWgTLFBxmaexGUKQyWqeG7HjXj20EuQJSJWwXe54BjX0ihIo5cJB9yAQzH8dNugJ9GvkBYMjPXs/PJw==
+
"@types/uuid@8.3.1", "@types/uuid@^8.3.0":
version "8.3.1"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f"
@@ -9960,11 +9970,6 @@ camelcase-keys@^3.0.0:
camelcase "^3.0.0"
map-obj "^1.0.0"
-camelcase@^2.0.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
- integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
-
camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
@@ -10374,7 +10379,7 @@ clipboardy@^2.3.0:
execa "^1.0.0"
is-wsl "^2.1.1"
-cliui@^3.0.3, cliui@^3.2.0:
+cliui@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
@@ -10600,7 +10605,7 @@ commander@9.3.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-9.3.0.tgz#f619114a5a2d2054e0d9ff1b31d5ccf89255e26b"
integrity sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==
-commander@^2.19.0, commander@^2.20.0, commander@^2.8.1, commander@^2.9.0:
+commander@^2.19.0, commander@^2.20.0, commander@^2.8.1:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -10693,15 +10698,6 @@ compression@^1.7.1:
safe-buffer "5.1.2"
vary "~1.1.2"
-concat-cli@4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/concat-cli/-/concat-cli-4.0.0.tgz#a73a0fb0d18b25804ebe703bcc35922324dbf74d"
- integrity sha1-pzoPsNGLJYBOvnA7zDWSIyTb900=
- dependencies:
- chalk "^1.1.1"
- concat "^1.0.0"
- yargs "^3.30.0"
-
concat-stream@^1.4.4:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
@@ -10712,13 +10708,6 @@ concat-stream@^1.4.4:
readable-stream "^2.2.2"
typedarray "^0.0.6"
-concat@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/concat/-/concat-1.0.3.tgz#40f3353089d65467695cb1886b45edd637d8cca8"
- integrity sha1-QPM1MInWVGdpXLGIa0Xt1jfYzKg=
- dependencies:
- commander "^2.9.0"
-
connect@^3.6.5:
version "3.7.0"
resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
@@ -22404,6 +22393,11 @@ redux-persist@6.0.0:
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==
+redux-thunk@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
+ integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
+
redux@4.1.1, redux@^4.0.0, redux@^4.0.5:
version "4.1.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47"
@@ -25864,11 +25858,6 @@ wide-align@^1.1.2, wide-align@^1.1.5:
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
-window-size@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
- integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=
-
winston-transport@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa"
@@ -26080,15 +26069,7 @@ xml-parse-from-string@^1.0.0:
resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28"
integrity sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==
-xml2js@^0.4.17, xml2js@^0.4.5:
- version "0.4.23"
- resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
- integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
- dependencies:
- sax ">=0.6.0"
- xmlbuilder "~11.0.0"
-
-xml2js@^0.5.0:
+xml2js@>=0.5.0, xml2js@^0.4.17, xml2js@^0.4.5, xml2js@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7"
integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==
@@ -26109,7 +26090,7 @@ xmlbuilder@^15.1.1:
xmlbuilder@^9.0.1, xmlbuilder@^9.0.7:
version "9.0.7"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
- integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
+ integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==
xmlbuilder@~11.0.0:
version "11.0.1"
@@ -26171,7 +26152,7 @@ xtend@~3.0.0:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a"
integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=
-y18n@^3.2.0, y18n@^3.2.1, "y18n@^3.2.1 || ^4.0.0", y18n@^3.2.2, y18n@^4.0.0, y18n@^5.0.5:
+y18n@^3.2.1, "y18n@^3.2.1 || ^4.0.0", y18n@^3.2.2, y18n@^4.0.0, y18n@^5.0.5:
version "3.2.2"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
@@ -26338,19 +26319,6 @@ yargs@^17.5.1:
y18n "^5.0.5"
yargs-parser "^21.1.1"
-yargs@^3.30.0:
- version "3.32.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995"
- integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=
- dependencies:
- camelcase "^2.0.1"
- cliui "^3.0.3"
- decamelize "^1.1.1"
- os-locale "^1.4.0"
- string-width "^1.0.1"
- window-size "^0.1.4"
- y18n "^3.2.0"
-
yargs@^7.0.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db"