Skip to content

Commit 75f64d7

Browse files
jpuriccharly
andauthored
test: fix 7702 transactions related e2e on mobile (MetaMask#21020)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** fix 7702 transactions related e2e on mobile ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: MetaMask#17930 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** NA ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Updates E2E to stabilize and enable smart account flows for 7702 transactions. > > - Adds testIDs to `SmartAccount` link and modal back button; imports `SwitchAccountModalSelectorIDs` > - Updates selectors (`SMART_ACCOUNT_BUTTON_LOCALHOST` → `switch_account_button-Local RPC`) and adds `SMART_ACCOUNT_BACK_BUTTON`/`SMART_ACCOUNT_LINK` > - Extends `SwitchAccountModal` page object with smart account link/back actions > - Introduces new spec `e2e/specs/confirmations-redesigned/transactions/7702/batch-transaction.spec.ts` with Local RPC fixture; removes old failing quarantine spec > - Adjusts test flow for network switching and Dapp navigation; some steps commented/skipped pending toggle click fix > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit aa26f47. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Charly Chevalier <charlyy.chevalier@gmail.com>
1 parent 19636de commit 75f64d7

6 files changed

Lines changed: 293 additions & 208 deletions

File tree

app/components/Views/MultichainAccounts/AccountDetails/components/SmartAccount/SmartAccount.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { useCallback } from 'react';
2+
3+
import { SwitchAccountModalSelectorIDs } from '../../../../../../../e2e/selectors/wallet/SwitchAccountModal.selectors';
24
import Text, {
35
TextVariant,
46
} from '../../../../../../component-library/components/Texts/Text';
@@ -37,7 +39,10 @@ export const SmartAccountDetails = ({ account }: SmartAccountDetailsProps) => {
3739
}
3840

3941
return (
40-
<TouchableOpacity onPress={onSmartAccountPress}>
42+
<TouchableOpacity
43+
onPress={onSmartAccountPress}
44+
testID={SwitchAccountModalSelectorIDs.SMART_ACCOUNT_LINK}
45+
>
4146
<Box
4247
style={styles.container}
4348
flexDirection={FlexDirection.Row}

app/components/Views/MultichainAccounts/AccountDetails/components/SmartAccountModal/SmartAccountModal.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
useNavigation,
2020
useRoute,
2121
} from '@react-navigation/native';
22+
23+
import { SwitchAccountModalSelectorIDs } from '../../../../../../../e2e/selectors/wallet/SwitchAccountModal.selectors';
2224
import AppConstants from '../../../../../../core/AppConstants';
2325
import HeaderBase from '../../../../../../component-library/components/HeaderBase';
2426
import ButtonLink from '../../../../../../component-library/components/Buttons/Button/variants/ButtonLink';
@@ -73,6 +75,7 @@ const SmartAccountModal = () => {
7375
labelTextVariant={TextVariant.BodyMDMedium}
7476
label={<Icon name={IconName.ArrowLeft} size={IconSize.Md} />}
7577
onPress={() => navigation.goBack()}
78+
testID={SwitchAccountModalSelectorIDs.SMART_ACCOUNT_BACK_BUTTON}
7679
/>
7780
}
7881
>
Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
1-
import Matchers from '../../framework/Matchers.ts';
21
import Gestures from '../../framework/Gestures.ts';
2+
import Matchers from '../../framework/Matchers.ts';
33
import { SwitchAccountModalSelectorIDs } from '../../selectors/wallet/SwitchAccountModal.selectors.js';
44
class SwitchAccountModal {
5-
get switchAccountButton(): DetoxElement {
5+
get smartAccountLink() {
6+
return Matchers.getElementByText('Smart account');
7+
}
8+
9+
get switchAccountButton() {
610
return Matchers.getElementByID(
711
SwitchAccountModalSelectorIDs.SWITCH_ACCOUNT_BUTTON_LOCALHOST,
812
);
913
}
1014

15+
get smartAccountBackButton() {
16+
return Matchers.getElementByID(
17+
SwitchAccountModalSelectorIDs.SMART_ACCOUNT_BACK_BUTTON,
18+
);
19+
}
20+
1121
async tapSwitchAccountButton(): Promise<void> {
1222
await Gestures.waitAndTap(this.switchAccountButton, {
1323
checkEnabled: false,
1424
elemDescription: 'Switch Account button',
1525
});
1626
}
27+
28+
async tapSmartAccountLink(): Promise<void> {
29+
await Gestures.waitAndTap(this.smartAccountLink, {
30+
elemDescription: 'Smart Account link',
31+
});
32+
}
33+
34+
async tapSmartAccountBackButton(): Promise<void> {
35+
await Gestures.waitAndTap(this.smartAccountBackButton, {
36+
elemDescription: 'Smart Account back button',
37+
});
38+
}
1739
}
1840

1941
export default new SwitchAccountModal();
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export const SwitchAccountModalSelectorIDs = {
2+
SMART_ACCOUNT_BACK_BUTTON: 'smart-account-back-button',
3+
SMART_ACCOUNT_LINK: 'smart-account-link',
24
SWITCH_ACCOUNT_BUTTON: 'switch_account_button',
3-
SWITCH_ACCOUNT_BUTTON_LOCALHOST: 'switch_account_button-Localhost',
5+
SWITCH_ACCOUNT_BUTTON_LOCALHOST: 'switch_account_button-Local RPC',
46
};
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import AccountDetails from '../../../../pages/MultichainAccounts/AccountDetails';
2+
import AccountListBottomSheet from '../../../../pages/wallet/AccountListBottomSheet';
3+
import Assertions from '../../../../framework/Assertions';
4+
import BrowserView from '../../../../pages/Browser/BrowserView';
5+
import ConfirmationUITypes from '../../../../pages/Browser/Confirmations/ConfirmationUITypes';
6+
import FixtureBuilder from '../../../../framework/fixtures/FixtureBuilder';
7+
import FooterActions from '../../../../pages/Browser/Confirmations/FooterActions';
8+
import NetworkListModal from '../../../../pages/Network/NetworkListModal';
9+
import RowComponents from '../../../../pages/Browser/Confirmations/RowComponents';
10+
import SwitchAccountModal from '../../../../pages/wallet/SwitchAccountModal';
11+
import TabBarComponent from '../../../../pages/wallet/TabBarComponent';
12+
import TestDApp from '../../../../pages/Browser/TestDApp';
13+
import WalletView from '../../../../pages/wallet/WalletView';
14+
import { SIMULATION_ENABLED_NETWORKS_MOCK } from '../../../../api-mocking/mock-responses/simulations';
15+
import {
16+
AnvilPort,
17+
buildPermissions,
18+
} from '../../../../framework/fixtures/FixtureUtils';
19+
import { loginToApp } from '../../../../viewHelper';
20+
import { SmokeConfirmationsRedesigned } from '../../../../tags';
21+
import { withFixtures } from '../../../../framework/fixtures/FixtureHelper';
22+
import { DappVariants } from '../../../../framework/Constants';
23+
import {
24+
AnvilNodeOptions,
25+
LocalNode,
26+
LocalNodeType,
27+
} from '../../../../framework';
28+
import { Mockttp } from 'mockttp';
29+
import { setupMockRequest } from '../../../../api-mocking/helpers/mockHelpers';
30+
import { confirmationsRedesignedFeatureFlags } from '../../../../api-mocking/mock-responses/feature-flags-mocks';
31+
import { setupRemoteFeatureFlagsMock } from '../../../../api-mocking/helpers/remoteFeatureFlagsHelper';
32+
import { AnvilManager } from '../../../../seeder/anvil-manager';
33+
34+
const LOCAL_CHAIN_NAME = 'Local RPC';
35+
36+
const localNodeOptions = [
37+
{
38+
type: 'anvil',
39+
options: {
40+
hardfork: 'prague',
41+
loadState:
42+
'./e2e/specs/confirmations-redesigned/transactions/7702/withDelegatorContracts.json',
43+
},
44+
},
45+
];
46+
47+
async function changeNetworkFromNetworkListModal() {
48+
await WalletView.tapTokenNetworkFilter();
49+
await NetworkListModal.tapOnCustomTab();
50+
await NetworkListModal.changeNetworkTo(LOCAL_CHAIN_NAME);
51+
}
52+
53+
async function checkConfirmationPage() {
54+
await Assertions.expectElementToBeVisible(RowComponents.AccountNetwork);
55+
await Assertions.expectElementToBeVisible(RowComponents.GasFeesDetails);
56+
await Assertions.expectElementToBeVisible(RowComponents.AdvancedDetails);
57+
}
58+
59+
async function tapSwitchAccountModal() {
60+
await WalletView.tapIdenticon();
61+
await AccountListBottomSheet.tapEditAccountActionsAtIndex(0);
62+
await SwitchAccountModal.tapSmartAccountLink();
63+
await SwitchAccountModal.tapSwitchAccountButton();
64+
}
65+
66+
async function goBackToWalletPage() {
67+
await SwitchAccountModal.tapSmartAccountBackButton();
68+
await AccountDetails.tapBackButton();
69+
try {
70+
await AccountListBottomSheet.dismissAccountListModalV2();
71+
} catch (error) {
72+
// Modal might already be dismissed, continue with test
73+
console.log('Modal already dismissed or not found, continuing...');
74+
}
75+
}
76+
77+
async function connectTestDappToLocalhost() {
78+
await TabBarComponent.tapBrowser();
79+
await BrowserView.navigateToTestDApp();
80+
}
81+
82+
/**
83+
* The test cases have been partially commented out and skipped.
84+
* Click event on switch account toggle is not working in the e2e tests.
85+
* Once that is fixed, the test cases can be uncommented and unskipped.
86+
*/
87+
describe(SmokeConfirmationsRedesigned('7702 - smart account'), () => {
88+
const testSpecificMock = async (mockServer: Mockttp) => {
89+
await setupMockRequest(mockServer, {
90+
requestMethod: 'GET',
91+
url: SIMULATION_ENABLED_NETWORKS_MOCK.urlEndpoint,
92+
response: SIMULATION_ENABLED_NETWORKS_MOCK.response,
93+
responseCode: 200,
94+
});
95+
await setupRemoteFeatureFlagsMock(
96+
mockServer,
97+
Object.assign({}, ...confirmationsRedesignedFeatureFlags),
98+
);
99+
};
100+
beforeAll(async () => {
101+
jest.setTimeout(2500000);
102+
});
103+
104+
it('submit sendCalls, upgrade, downgrade account', async () => {
105+
await withFixtures(
106+
{
107+
dapps: [
108+
{
109+
dappVariant: DappVariants.TEST_DAPP,
110+
},
111+
],
112+
fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
113+
const node = localNodes?.[0] as unknown as AnvilManager;
114+
const rpcPort =
115+
node instanceof AnvilManager
116+
? (node.getPort() ?? AnvilPort())
117+
: undefined;
118+
119+
return new FixtureBuilder()
120+
.withNetworkController({
121+
providerConfig: {
122+
chainId: '0x539',
123+
rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
124+
type: 'custom',
125+
nickname: 'Local RPC',
126+
ticker: 'ETH',
127+
},
128+
})
129+
.withPermissionControllerConnectedToTestDapp(
130+
buildPermissions(['0x539']),
131+
)
132+
.build();
133+
},
134+
restartDevice: true,
135+
localNodeOptions: [
136+
{
137+
type: LocalNodeType.anvil,
138+
options: localNodeOptions[0].options as AnvilNodeOptions,
139+
},
140+
],
141+
testSpecificMock,
142+
},
143+
async () => {
144+
await loginToApp();
145+
146+
// Submit send calls
147+
await changeNetworkFromNetworkListModal();
148+
await connectTestDappToLocalhost();
149+
150+
await TestDApp.tapSendCallsButton();
151+
152+
// Check all expected elements are visible
153+
await Assertions.expectTextDisplayed('Includes 2 transactions');
154+
await Assertions.expectElementToBeVisible(
155+
ConfirmationUITypes.ModalConfirmationContainer,
156+
);
157+
await checkConfirmationPage();
158+
await Assertions.expectElementToBeVisible(
159+
RowComponents.SimulationDetails,
160+
);
161+
162+
// Accept confirmation
163+
await FooterActions.tapConfirmButton();
164+
165+
// Check activity tab
166+
await TabBarComponent.tapActivity();
167+
await Assertions.expectTextDisplayed('Smart contract interaction');
168+
169+
// // open switch account modal to downgrade account
170+
// await TabBarComponent.tapWallet();
171+
// await tapSwitchAccountModal();
172+
173+
// // Check all expected elements are visible
174+
// await Assertions.expectTextDisplayed('Account update');
175+
// await Assertions.expectTextDisplayed(
176+
// "You're switching back to a standard account (EOA).",
177+
// );
178+
// await Assertions.expectElementToBeVisible(
179+
// ConfirmationUITypes.ModalConfirmationContainer,
180+
// );
181+
// await checkConfirmationPage();
182+
183+
// // Accept confirmation
184+
// await FooterActions.tapConfirmButton();
185+
186+
// await goBackToWalletPage();
187+
// // Check activity tab
188+
// await TabBarComponent.tapActivity();
189+
// await Assertions.expectTextDisplayed('Switch to standard account');
190+
},
191+
);
192+
});
193+
194+
it.skip('upgrades an account', async () => {
195+
await withFixtures(
196+
{
197+
dapps: [
198+
{
199+
dappVariant: DappVariants.TEST_DAPP,
200+
},
201+
],
202+
fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
203+
const node = localNodes?.[0] as unknown as AnvilManager;
204+
const rpcPort =
205+
node instanceof AnvilManager
206+
? (node.getPort() ?? AnvilPort())
207+
: undefined;
208+
209+
return new FixtureBuilder()
210+
.withNetworkController({
211+
providerConfig: {
212+
chainId: '0x539',
213+
rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
214+
type: 'custom',
215+
nickname: 'Local RPC',
216+
ticker: 'ETH',
217+
},
218+
})
219+
.build();
220+
},
221+
restartDevice: true,
222+
localNodeOptions: [
223+
{
224+
type: LocalNodeType.anvil,
225+
options: localNodeOptions[0].options as AnvilNodeOptions,
226+
},
227+
],
228+
testSpecificMock,
229+
},
230+
async () => {
231+
await loginToApp();
232+
233+
// Create confirmation to upgrade account
234+
await changeNetworkFromNetworkListModal();
235+
await tapSwitchAccountModal();
236+
237+
// Check all expected elements are visible
238+
await Assertions.expectTextDisplayed('Account update');
239+
await Assertions.expectTextDisplayed(
240+
"You're switching to a smart account.",
241+
);
242+
await Assertions.expectElementToBeVisible(
243+
ConfirmationUITypes.ModalConfirmationContainer,
244+
);
245+
await checkConfirmationPage();
246+
247+
// Accept confirmation
248+
await FooterActions.tapConfirmButton();
249+
250+
await goBackToWalletPage();
251+
// Check activity tab
252+
await TabBarComponent.tapActivity();
253+
await Assertions.expectTextDisplayed('Upgrade to smart account');
254+
},
255+
);
256+
});
257+
});

0 commit comments

Comments
 (0)