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';