diff --git a/.e2e.env.example b/.e2e.env.example index fd46ecba336..29071a958ef 100644 --- a/.e2e.env.example +++ b/.e2e.env.example @@ -3,3 +3,5 @@ export MM_TEST_ACCOUNT_SRP='word1 word... word12' export MM_TEST_ACCOUNT_ADDRESS='0x...' export MM_TEST_ACCOUNT_PRIVATE_KEY='' export IS_TEST="true" +# Temporary mechanism to enable security alerts API prior to release. +export MM_SECURITY_ALERTS_API_ENABLED="true" diff --git a/app/components/Views/confirmations/SendFlow/Confirm/index.js b/app/components/Views/confirmations/SendFlow/Confirm/index.js index ad10c31e164..29704d55652 100644 --- a/app/components/Views/confirmations/SendFlow/Confirm/index.js +++ b/app/components/Views/confirmations/SendFlow/Confirm/index.js @@ -1386,7 +1386,7 @@ class Confirm extends PureComponent { {transactionValue} diff --git a/app/components/Views/confirmations/components/BlockaidBanner/BlockaidBanner.tsx b/app/components/Views/confirmations/components/BlockaidBanner/BlockaidBanner.tsx index 48d2c92906c..22e5f5efc6c 100644 --- a/app/components/Views/confirmations/components/BlockaidBanner/BlockaidBanner.tsx +++ b/app/components/Views/confirmations/components/BlockaidBanner/BlockaidBanner.tsx @@ -32,6 +32,7 @@ import { BLOCKAID_SUPPORTED_NETWORK_NAMES } from '../../../../../util/networks'; import BlockaidVersionInfo from '../../../../../lib/ppom/blockaid-version'; import { WALLET_CONNECT_ORIGIN } from '../../../../../util/walletconnect'; import AppConstants from '../../../../../core/AppConstants'; +import { ConfirmationTopSheetSelectorsIDs } from '../../../../../../e2e/selectors/ConfirmationView.selectors'; const getReportUrl = (encodedData: string) => `${FALSE_POSITIVE_REPORT_BASE_URL}?data=${encodeURIComponent( @@ -123,6 +124,7 @@ const BlockaidBanner = (bannerProps: BlockaidBannerProps) => { severity={BannerAlertSeverity.Warning} title={title} description={description} + testID={ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_RESPONSE_FAILED_BANNER} /> ); @@ -177,6 +179,7 @@ const BlockaidBanner = (bannerProps: BlockaidBannerProps) => { title={title} description={description} {...bannerProps} + testID={ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_BANNER} > {renderDetails()} diff --git a/app/components/Views/confirmations/components/BlockaidBanner/__snapshots__/BlockaidBanner.test.tsx.snap b/app/components/Views/confirmations/components/BlockaidBanner/__snapshots__/BlockaidBanner.test.tsx.snap index 041cd22e6c5..515328ab1ae 100644 --- a/app/components/Views/confirmations/components/BlockaidBanner/__snapshots__/BlockaidBanner.test.tsx.snap +++ b/app/components/Views/confirmations/components/BlockaidBanner/__snapshots__/BlockaidBanner.test.tsx.snap @@ -37,7 +37,7 @@ exports[`BlockaidBanner should render correctly 1`] = ` "paddingLeft": 8, } } - testID="banneralert" + testID="security-alert-banner" > { MockEngine.context.PreferencesController.state.securityAlertsEnabled = false; await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK); - expect(MockEngine.context.PPOMController?.usePPOM).toBeCalledTimes(0); - expect(spyTransactionAction).toBeCalledTimes(0); + expect(MockEngine.context.PPOMController?.usePPOM).toHaveBeenCalledTimes(0); + expect(spyTransactionAction).toHaveBeenCalledTimes(0); }); it('should not validate if request is send to users own account ', async () => { @@ -307,6 +313,22 @@ describe('PPOM Utils', () => { }); }); + it('logs error if normalization fails', async () => { + const error = new Error('Test Error'); + normalizeTransactionParamsMock.mockImplementation(() => { + throw error; + }); + + const spyLogger = jest.spyOn(Logger, 'log'); + + await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK); + + expect(spyLogger).toHaveBeenCalledTimes(1); + expect(spyLogger).toHaveBeenCalledWith( + `Error validating JSON RPC using PPOM: ${error}`, + ); + }); + it('normalizes transaction request origin before validation', async () => { const validateMock = jest.fn(); @@ -385,5 +407,34 @@ describe('PPOM Utils', () => { await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK); expect(spy).toHaveBeenCalledTimes(2); }); + + it('sets security alerts response to failed when security alerts API and controller PPOM throws', async () => { + const spy = jest.spyOn( + TransactionActions, + 'setTransactionSecurityAlertResponse', + ); + + const validateMock = new Error('Test Error'); + + const ppomMock = { + validateJsonRpc: validateMock, + }; + + MockEngine.context.PPOMController?.usePPOM.mockImplementation( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (callback: any) => callback(ppomMock), + ); + + await PPOMUtil.validateRequest(mockRequest, CHAIN_ID_MOCK); + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toHaveBeenCalledWith(CHAIN_ID_MOCK, { + chainId: CHAIN_ID_MOCK, + req: mockRequest, + result_type: ResultType.Failed, + reason: Reason.failed, + description: 'Validating the confirmation failed by throwing error.', + source: SecurityAlertSource.Local, + }); + }); }); -}); +}); \ No newline at end of file diff --git a/app/lib/ppom/ppom-util.ts b/app/lib/ppom/ppom-util.ts index 2385ac5a6c4..3a7716eb87f 100644 --- a/app/lib/ppom/ppom-util.ts +++ b/app/lib/ppom/ppom-util.ts @@ -24,7 +24,7 @@ import { } from './security-alerts-api'; import { PPOMController } from '@metamask/ppom-validator'; import { Hex } from '@metamask/utils'; -import { BLOCKAID_SUPPORTED_CHAIN_IDS } from '../../util/networks'; +import { SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST } from '../../util/networks'; export interface PPOMRequest { method: string; @@ -137,7 +137,7 @@ async function validateRequest(req: PPOMRequest, transactionId?: string) { } async function isChainSupported(chainId: Hex): Promise { - let supportedChainIds = BLOCKAID_SUPPORTED_CHAIN_IDS; + let supportedChainIds = SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST; try { if (isSecurityAlertsAPIEnabled()) { supportedChainIds = await getSecurityAlertsAPISupportedChainIds(); @@ -155,14 +155,19 @@ async function validateWithController( ppomController: PPOMController, request: PPOMRequest, ): Promise { - const response = (await ppomController.usePPOM((ppom) => - ppom.validateJsonRpc(request as unknown as Record), - )) as SecurityAlertResponse; + try{ + const response = (await ppomController.usePPOM((ppom) => + ppom.validateJsonRpc(request as unknown as Record), + )) as SecurityAlertResponse; - return { - ...response, - source: SecurityAlertSource.Local, - }; + return { + ...response, + source: SecurityAlertSource.Local, + }; + } catch (e) { + Logger.log(`Error validating request with PPOM: ${e}`); + return {...SECURITY_ALERT_RESPONSE_FAILED, source: SecurityAlertSource.Local,}; + } } async function validateWithAPI( diff --git a/app/util/networks/index.js b/app/util/networks/index.js index e7e6cbb0d9d..418fe5583e4 100644 --- a/app/util/networks/index.js +++ b/app/util/networks/index.js @@ -118,7 +118,7 @@ export const NetworkList = { const NetworkListKeys = Object.keys(NetworkList); -export const BLOCKAID_SUPPORTED_CHAIN_IDS = [ +export const SECURITY_PROVIDER_SUPPORTED_CHAIN_IDS_FALLBACK_LIST = [ NETWORKS_CHAIN_ID.MAINNET, NETWORKS_CHAIN_ID.BSC, NETWORKS_CHAIN_ID.BASE, diff --git a/e2e/api-mocking/mock-config/mock-events.js b/e2e/api-mocking/mock-config/mock-events.js index 2cbf715f87c..0e3423416b1 100644 --- a/e2e/api-mocking/mock-config/mock-events.js +++ b/e2e/api-mocking/mock-config/mock-events.js @@ -34,6 +34,27 @@ export const mockEvents = { response: suggestedGasFeesApiGanache, responseCode: 200, }, + + securityAlertApiSupportedChains: { + urlEndpoint: 'https://security-alerts.api.cx.metamask.io/supportedChains', + response: [ + '0xa4b1', + '0xa86a', + '0x2105', + '0x138d5', + '0x38', + '0xe708', + '0x1', + '0x1b6e6', + '0xcc', + '0xa', + '0x89', + '0x82750', + '0xaa36a7', + '0x144' + ], + responseCode: 200, + }, }, /** @@ -54,5 +75,29 @@ export const mockEvents = { maxFee: '2.000855333', }, }, + + securityAlertApiValidate: { + urlEndpoint: 'https://security-alerts.api.cx.metamask.io/validate/0xaa36a7', + response: { + block: 20733513, + result_type: 'Benign', + reason: '', + description: '', + features: [], + }, + requestBody: { + jsonrpc: '2.0', + method: 'eth_sendTransaction', + origin: 'metamask', + params: [ + { + from: '0x76cf1cdd1fcc252442b50d6e97207228aa4aefc3', + to: '0x50587e46c5b96a3f6f9792922ec647f13e6efae4', + value: '0x0' + } + ] + }, + responseCode: 201, + }, }, }; diff --git a/e2e/api-mocking/mock-server.js b/e2e/api-mocking/mock-server.js index 45fd1273820..316e9a7f38b 100644 --- a/e2e/api-mocking/mock-server.js +++ b/e2e/api-mocking/mock-server.js @@ -50,7 +50,7 @@ export const startMockServer = async (events, port) => { await mockServer .forPost('/proxy') .withQuery({ url: urlEndpoint }) - .withJsonBody(requestBody || {}) + .withJsonBodyIncluding(requestBody || {}) .thenReply(responseCode, JSON.stringify(response)); } } diff --git a/e2e/fixtures/fixture-helper.js b/e2e/fixtures/fixture-helper.js index 0bf98ec755c..093ae108b41 100644 --- a/e2e/fixtures/fixture-helper.js +++ b/e2e/fixtures/fixture-helper.js @@ -9,6 +9,7 @@ import createStaticServer from '../create-static-server'; import { getFixturesServerPort, getLocalTestDappPort } from './utils'; import Utilities from '../utils/Utilities'; import { device } from 'detox'; +import { startMockServer, stopMockServer } from '../api-mocking/mock-server'; export const DEFAULT_DAPP_SERVER_PORT = 8085; @@ -99,9 +100,15 @@ export async function withFixtures(options, testSuite) { dappOptions, dappPath = undefined, dappPaths, + testSpecificMock, } = options; const fixtureServer = new FixtureServer(); + + if (testSpecificMock) { + await startMockServer(testSpecificMock); + } + let ganacheServer; if (!disableGanache) { ganacheServer = new Ganache(); @@ -191,6 +198,10 @@ export async function withFixtures(options, testSuite) { } } await stopFixtureServer(fixtureServer); + + if (testSpecificMock) { + await stopMockServer(); + } } } diff --git a/e2e/pages/Confirmation/ConfirmationView.js b/e2e/pages/Confirmation/ConfirmationView.js new file mode 100644 index 00000000000..41518400a9b --- /dev/null +++ b/e2e/pages/Confirmation/ConfirmationView.js @@ -0,0 +1,15 @@ +import { ConfirmationTopSheetSelectorsIDs } from '../../selectors/ConfirmationView.selectors'; +import Matchers from '../../utils/Matchers'; + +class ConfirmationView { + get securityAlertBanner() { + return Matchers.getElementByID(ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_BANNER); + } + + get securityAlertResponseFailedBanner() { + return Matchers.getElementByID(ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_RESPONSE_FAILED_BANNER); + } + +} + +export default new ConfirmationView(); diff --git a/e2e/pages/Send/TransactionConfirmView.js b/e2e/pages/Send/TransactionConfirmView.js index f9b80f1439c..f9ac56ff9ec 100644 --- a/e2e/pages/Send/TransactionConfirmView.js +++ b/e2e/pages/Send/TransactionConfirmView.js @@ -8,6 +8,7 @@ import { TransactionConfirmViewSelectorsText, TransactionConfirmViewSelectorsIDs, } from '../../selectors/TransactionConfirmView.selectors.js'; +import { ConfirmationTopSheetSelectorsIDs } from '../../selectors/ConfirmationView.selectors.js'; class TransactionConfirmationView { get confirmButton() { @@ -40,7 +41,7 @@ class TransactionConfirmationView { get transactionAmount() { return Matchers.getElementByID( - TransactionConfirmViewSelectorsIDs.COMFIRM_TXN_AMOUNT, + TransactionConfirmViewSelectorsIDs.CONFIRM_TXN_AMOUNT, ); } @@ -77,6 +78,18 @@ class TransactionConfirmationView { ); } + get securityAlertBanner() { + return Matchers.getElementByID( + ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_BANNER, + ); + } + + get securityAlertResponseFailedBanner() { + return Matchers.getElementByID( + ConfirmationTopSheetSelectorsIDs.SECURITY_ALERT_RESPONSE_FAILED_BANNER, + ); + } + async tapConfirmButton() { await Gestures.waitAndTap(this.confirmButton); } diff --git a/e2e/selectors/ConfirmationView.selectors.js b/e2e/selectors/ConfirmationView.selectors.js new file mode 100644 index 00000000000..2fde7c2460e --- /dev/null +++ b/e2e/selectors/ConfirmationView.selectors.js @@ -0,0 +1,4 @@ +export const ConfirmationTopSheetSelectorsIDs = { + SECURITY_ALERT_BANNER: 'security-alert-banner', + SECURITY_ALERT_RESPONSE_FAILED_BANNER: 'security-alert-response-failed-banner', +}; diff --git a/e2e/selectors/TransactionConfirmView.selectors.js b/e2e/selectors/TransactionConfirmView.selectors.js index 2ca017253e5..ae784f6264e 100644 --- a/e2e/selectors/TransactionConfirmView.selectors.js +++ b/e2e/selectors/TransactionConfirmView.selectors.js @@ -1,7 +1,7 @@ import enContent from '../../locales/languages/en.json'; export const TransactionConfirmViewSelectorsIDs = { - COMFIRM_TXN_AMOUNT: 'confirm-txn-amount', + CONFIRM_TXN_AMOUNT: 'confirm-txn-amount', TRANSACTION_VIEW_CONTAINER_ID: 'txn-confirm-screen', CONFIRM_TRANSACTION_BUTTON_ID: 'txn-confirm-send-button', NAVBAR_TITLE_TEXT: 'navbar-title-text', diff --git a/e2e/specs/confirmations/security-alert-send-eth.mock.spec.js b/e2e/specs/confirmations/security-alert-send-eth.mock.spec.js new file mode 100644 index 00000000000..d08795071aa --- /dev/null +++ b/e2e/specs/confirmations/security-alert-send-eth.mock.spec.js @@ -0,0 +1,122 @@ +'use strict'; + +import { SmokeConfirmations } from '../../tags'; +import TestHelpers from '../../helpers'; + +import AmountView from '../../pages/Send/AmountView'; +import SendView from '../../pages/Send/SendView'; +import TransactionConfirmationView from '../../pages/Send/TransactionConfirmView'; +import { loginToApp } from '../../viewHelper'; +import TabBarComponent from '../../pages/TabBarComponent'; +import WalletActionsModal from '../../pages/modals/WalletActionsModal'; +import FixtureBuilder from '../../fixtures/fixture-builder'; +import { + withFixtures, +} from '../../fixtures/fixture-helper'; +import { mockEvents } from '../../api-mocking/mock-config/mock-events'; +import Assertions from '../../utils/Assertions'; + +describe(SmokeConfirmations('Security Alert API - Send flow'), () => { + const BENIGN_ADDRESS_MOCK = '0x50587E46C5B96a3F6f9792922EC647F13E6EFAE4'; + + beforeAll(async () => { + jest.setTimeout(2500000); + await TestHelpers.reverseServerPort(); + }); + + const defaultFixture = new FixtureBuilder().withSepoliaNetwork().build(); + + const navigateToSendConfirmation = async () => { + await loginToApp(); + await TabBarComponent.tapActions(); + await WalletActionsModal.tapSendButton(); + await SendView.inputAddress(BENIGN_ADDRESS_MOCK); + await SendView.tapNextButton(); + await AmountView.typeInTransactionAmount('0'); + await AmountView.tapNextButton(); + }; + + const runTest = async (testSpecificMock, alertAssertion) => { + await withFixtures( + { + fixture: defaultFixture, + restartDevice: true, + testSpecificMock, + }, + async () => { + await navigateToSendConfirmation(); + await alertAssertion(); + }, + ); + }; + + it('should not show security alerts for benign requests', async () => { + const testSpecificMock = { + GET: [ + mockEvents.GET.securityAlertApiSupportedChains, + ], + POST: [mockEvents.POST.securityAlertApiValidate,] + }; + + await runTest(testSpecificMock, async () => { + try { + await Assertions.checkIfNotVisible( + TransactionConfirmationView.securityAlertBanner, + ); + } catch (e) { + // eslint-disable-next-line no-console + console.log('The banner alert is not visible'); + } + }); + }); + + it('should show security alerts for malicious request', async () => { + const testSpecificMock = { + GET: [ + mockEvents.GET.securityAlertApiSupportedChains, + ], + POST: [{ + ...mockEvents.POST.securityAlertApiValidate, + response: { + block: 20733277, + result_type: 'Malicious', + reason: 'transfer_farming', + description: '', + features: ['Interaction with a known malicious address'], + }, + }] + }; + + await runTest(testSpecificMock, async () => { + await Assertions.checkIfVisible( + TransactionConfirmationView.securityAlertBanner, + ); + }); + }); + + it('should show security alerts for error when validating request fails', async () => { + const testSpecificMock = { + GET: [ + mockEvents.GET.securityAlertApiSupportedChains, + { + urlEndpoint: 'https://static.cx.metamask.io/api/v1/confirmations/ppom/ppom_version.json', + responseCode: 500 + } + ], + POST: [{ + ...mockEvents.POST.securityAlertApiValidate, + response: { + error: 'Internal Server Error', + message: 'An unexpected error occurred on the server.' + }, + responseCode: 500 + }] + }; + + await runTest(testSpecificMock, async () => { + await Assertions.checkIfVisible( + TransactionConfirmationView.securityAlertResponseFailedBanner, + ); + }); + }); +}); diff --git a/e2e/specs/confirmations/signatures/security-alert-typed-sign.mock.spec.js b/e2e/specs/confirmations/signatures/security-alert-typed-sign.mock.spec.js new file mode 100644 index 00000000000..0539bc724f1 --- /dev/null +++ b/e2e/specs/confirmations/signatures/security-alert-typed-sign.mock.spec.js @@ -0,0 +1,131 @@ +'use strict'; +import Browser from '../../../pages/Browser/BrowserView'; +import TabBarComponent from '../../../pages/TabBarComponent'; +import { loginToApp } from '../../../viewHelper'; +import SigningBottomSheet from '../../../pages/Browser/SigningBottomSheet'; +import TestDApp from '../../../pages/Browser/TestDApp'; +import FixtureBuilder from '../../../fixtures/fixture-builder'; +import { + withFixtures, +} from '../../../fixtures/fixture-helper'; +import { SmokeConfirmations } from '../../../tags'; +import TestHelpers from '../../../helpers'; +import Assertions from '../../../utils/Assertions'; +import { mockEvents } from '../../../api-mocking/mock-config/mock-events'; +import ConfirmationView from '../../../pages/Confirmation/ConfirmationView'; + +describe(SmokeConfirmations('Security Alert API - Typed Sign'), () => { + beforeAll(async () => { + jest.setTimeout(2500000); + await TestHelpers.reverseServerPort(); + }); + + const defaultFixture = new FixtureBuilder() + .withSepoliaNetwork() + .withPermissionControllerConnectedToTestDapp() + .build(); + + const navigateToTestDApp = async () => { + await loginToApp(); + await TabBarComponent.tapBrowser(); + await Browser.navigateToTestDApp(); + await TestDApp.tapTypedSignButton(); + await Assertions.checkIfVisible(SigningBottomSheet.typedRequest); + }; + + const runTest = async (testSpecificMock, alertAssertion) => { + await withFixtures( + { + dapp: true, + fixture: defaultFixture, + restartDevice: true, + testSpecificMock, + }, + async () => { + await navigateToTestDApp(); + await alertAssertion(); + }, + ); + }; + + const typedSignRequestBody = { + method: 'eth_signTypedData', + params: [ + [ + { type: 'string', name: 'Message', value: 'Hi, Alice!' }, + { type: 'uint32', name: 'A number', value: '1337' } + ], + '0x76cf1cdd1fcc252442b50d6e97207228aa4aefc3' + ], + origin: 'localhost', + }; + + it('should sign typed message', async () => { + const testSpecificMock = { + GET: [ + mockEvents.GET.securityAlertApiSupportedChains, + ], + POST: [{ + ...mockEvents.POST.securityAlertApiValidate, + requestBody: typedSignRequestBody, + }] + }; + + await runTest(testSpecificMock, async () => { + try { + await Assertions.checkIfNotVisible(ConfirmationView.securityAlertBanner); + } catch (e) { + // eslint-disable-next-line no-console + console.log('The banner alert is not visible'); + } + }); + }); + + it('should show security alert for malicious request', async () => { + const testSpecificMock = { + GET: [ + mockEvents.GET.securityAlertApiSupportedChains, + ], + POST: [{ + ...mockEvents.POST.securityAlertApiValidate, + requestBody: typedSignRequestBody, + response: { + block: 20733277, + result_type: 'Malicious', + reason: 'malicious_domain', + description: `You're interacting with a malicious domain. If you approve this request, you might lose your assets.`, + features: [], + }, + }] + }; + + await runTest(testSpecificMock, async () => { + await Assertions.checkIfVisible(ConfirmationView.securityAlertBanner); + }); + }); + + it('should show security alert for error when validating request fails', async () => { + const testSpecificMock = { + GET: [ + mockEvents.GET.securityAlertApiSupportedChains, + { + urlEndpoint: 'https://static.cx.metamask.io/api/v1/confirmations/ppom/ppom_version.json', + responseCode: 500 + } + ], + POST: [{ + ...mockEvents.POST.securityAlertApiValidate, + requestBody: typedSignRequestBody, + response: { + error: 'Internal Server Error', + message: 'An unexpected error occurred on the server.' + }, + responseCode: 500 + }] + }; + + await runTest(testSpecificMock, async () => { + await Assertions.checkIfVisible(ConfirmationView.securityAlertResponseFailedBanner); + }); + }); +}); diff --git a/wdio/screen-objects/SendScreen.js b/wdio/screen-objects/SendScreen.js index f47b600aa56..916a42d90f2 100644 --- a/wdio/screen-objects/SendScreen.js +++ b/wdio/screen-objects/SendScreen.js @@ -42,7 +42,7 @@ class SendScreen { get confirmAmount() { // eslint-disable-next-line no-undef - return Selectors.getElementByPlatform(COMFIRM_TXN_AMOUNT); + return Selectors.getElementByPlatform(CONFIRM_TXN_AMOUNT); } get addAddressButton() { diff --git a/wdio/screen-objects/TransactionConfirmScreen.js b/wdio/screen-objects/TransactionConfirmScreen.js index 6e9caf7031c..5ac7a346b84 100644 --- a/wdio/screen-objects/TransactionConfirmScreen.js +++ b/wdio/screen-objects/TransactionConfirmScreen.js @@ -1,6 +1,6 @@ import Selectors from '../helpers/Selectors'; import { - COMFIRM_TXN_AMOUNT, + CONFIRM_TXN_AMOUNT, CONFIRM_TRANSACTION_BUTTON_ID, } from './testIDs/Screens/TransactionConfirm.testIds'; import { ESTIMATED_FEE_TEST_ID } from './testIDs/Screens/TransactionSummaryScreen.testIds'; @@ -9,7 +9,7 @@ import Gestures from '../helpers/Gestures'; class TransactionConfirmScreen { get confirmAmount() { - return Selectors.getElementByPlatform(COMFIRM_TXN_AMOUNT); + return Selectors.getElementByPlatform(CONFIRM_TXN_AMOUNT); } get estimatedGasFee() { diff --git a/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js b/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js index 9bc174327c6..d8326038db7 100644 --- a/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js +++ b/wdio/screen-objects/testIDs/Screens/TransactionConfirm.testIds.js @@ -1,5 +1,5 @@ -export const COMFIRM_TXN_AMOUNT = 'confirm-txn-amount'; +export const CONFIRM_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';