Skip to content

Commit 450aa69

Browse files
test: Add Ethereum provider Snap test (#16672)
<!-- 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** Adds an E2E test that verifies the Ethereum provider functionality using an example Snap. ## **Related issues** Closes: MetaMask/snaps#3482
1 parent eb02319 commit 450aa69

File tree

7 files changed

+156
-11
lines changed

7 files changed

+156
-11
lines changed

e2e/api-mocking/mock-config/mock-events.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,30 @@ export const mockEvents = {
8484
responseCode: 200,
8585
},
8686

87+
remoteFeatureFlagsRedesignedConfirmationsFlask: {
88+
urlEndpoint:
89+
'https://client-config.api.cx.metamask.io/v1/flags?client=mobile&distribution=flask&environment=dev',
90+
response: [
91+
{
92+
mobileMinimumVersions: {
93+
appMinimumBuild: 1243,
94+
appleMinimumOS: 6,
95+
androidMinimumAPIVersion: 21,
96+
},
97+
},
98+
{
99+
confirmation_redesign: {
100+
signatures: true,
101+
staking_confirmations: true,
102+
contract_deployment: true,
103+
contract_interaction: true,
104+
transfer: true,
105+
},
106+
},
107+
],
108+
responseCode: 200,
109+
},
110+
87111
// TODO: Remove when this feature is no longer behind a feature flag
88112
remoteFeatureFlagsDefiPositionsEnabled: {
89113
urlEndpoint:

e2e/pages/Browser/TestSnaps.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import TestHelpers from '../../helpers';
1717
import Assertions from '../../utils/Assertions';
1818
import { IndexableWebElement } from 'detox/detox';
1919
import Utilities from '../../utils/Utilities';
20+
import { ConfirmationFooterSelectorIDs } from '../../selectors/Confirmation/ConfirmationView.selectors';
2021

2122
export const TEST_SNAPS_URL =
2223
'https://metamask.github.io/snaps/test-snaps/2.25.0/';
@@ -40,6 +41,10 @@ class TestSnaps {
4041
);
4142
}
4243

44+
get confirmSignatureButton() {
45+
return Matchers.getElementByID(ConfirmationFooterSelectorIDs.CONFIRM_BUTTON);
46+
}
47+
4348
async checkResultSpan(
4449
selector: keyof typeof TestSnapResultSelectorWebIDS,
4550
expectedMessage: string,
@@ -69,6 +74,7 @@ class TestSnaps {
6974
}
7075

7176
async navigateToTestSnap() {
77+
await Browser.tapUrlInputBox();
7278
await Browser.navigateToURL(TEST_SNAPS_URL);
7379
}
7480

@@ -94,16 +100,16 @@ class TestSnaps {
94100
);
95101
}
96102

97-
async selectEntropySource(
103+
async selectInDropdown(
98104
selector: keyof typeof EntropyDropDownSelectorWebIDS,
99-
entropySource: string,
105+
text: string,
100106
) {
101107
const webElement = (await Matchers.getElementByWebID(
102108
BrowserViewSelectorsIDs.BROWSER_WEBVIEW_ID,
103109
EntropyDropDownSelectorWebIDS[selector],
104110
)) as IndexableWebElement;
105111

106-
const source = await this.getOptionValueByText(webElement, entropySource);
112+
const source = await this.getOptionValueByText(webElement, text);
107113

108114
await webElement.runScript(
109115
(el, value) => {
@@ -139,6 +145,10 @@ class TestSnaps {
139145
await Gestures.waitAndTap(this.getApproveSignRequestButton);
140146
}
141147

148+
async approveNativeConfirmation() {
149+
await Gestures.waitAndTap(this.confirmSignatureButton);
150+
}
151+
142152
async waitForWebSocketUpdate(state: {
143153
open: boolean;
144154
origin: string | null;

e2e/selectors/Browser/TestSnaps.selectors.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const TestSnapViewSelectorWebIDS = {
33
connectBip32Button: 'connectbip32',
44
connectBip44Button: 'connectbip44',
55
connectNetworkAccessButton: 'connectnetwork-access',
6+
connectEthereumProviderButton: 'connectethereum-provider',
67
getPublicKeyBip44Button: 'sendBip44Test',
78
signMessageBip44Button: 'signBip44Message',
89
getPublicKeyBip32Button: 'bip32GetPublic',
@@ -14,6 +15,10 @@ export const TestSnapViewSelectorWebIDS = {
1415
startWebSocket: 'startWebSocket',
1516
stopWebSocket: 'stopWebSocket',
1617
getWebSocketState: 'getWebSocketState',
18+
getChainIdButton: 'sendEthprovider',
19+
getAccountsButton: 'sendEthproviderAccounts',
20+
personalSignButton: 'signPersonalSignMessage',
21+
signTypedDataButton: 'signTypedDataButton',
1722
};
1823

1924
export const TestSnapInputSelectorWebIDS = {
@@ -22,21 +27,27 @@ export const TestSnapInputSelectorWebIDS = {
2227
messageEd25519Input: 'bip32Message-ed25519',
2328
messageSecp256k1Input: 'bip32Message-secp256k1',
2429
webSocketUrlInput: 'webSocketUrl',
30+
personalSignMessageInput: 'personalSignMessage',
31+
signTypedDataMessageInput: 'signTypedData',
2532
};
2633

2734
export const EntropyDropDownSelectorWebIDS = {
2835
bip32EntropyDropDown: 'bip32-entropy-selector',
2936
bip44EntropyDropDown: 'bip44-entropy-selector',
37+
networkDropDown: 'select-chain',
3038
};
3139

3240
export const TestSnapResultSelectorWebIDS = {
3341
bip44ResultSpan: 'bip44Result',
3442
bip44SignResultSpan: 'bip44SignResult',
3543
bip32MessageResultEd25519Span: 'bip32MessageResult-ed25519',
3644
bip32MessageResultSecp256k1Span: 'bip32MessageResult-secp256k1',
37-
bip32MessageResultEd25519Bip32Span: '#bip32MessageResult-ed25519Bip32',
45+
bip32MessageResultEd25519Bip32Span: 'bip32MessageResult-ed25519Bip32',
3846
bip32PublicKeyResultSpan: 'bip32PublicKeyResult',
3947
networkAccessResultSpan: 'networkAccessResult',
48+
ethereumProviderResultSpan: 'ethproviderResult',
49+
personalSignResultSpan: 'personalSignResult',
50+
signTypedDataResultSpan: 'signTypedDataResult',
4051
};
4152

4253
export const TestSnapBottomSheetSelectorWebIDS = {

e2e/specs/snaps/test-snap-bip-32.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe(FlaskBuildTests('BIP-32 Snap Tests'), () => {
9191
});
9292

9393
it('can sign BIP-32 message using secp256k1 and SRP 1', async () => {
94-
await TestSnaps.selectEntropySource('bip32EntropyDropDown', 'SRP 1');
94+
await TestSnaps.selectInDropdown('bip32EntropyDropDown', 'SRP 1');
9595
await TestSnaps.fillMessage('messageSecp256k1Input', 'bar baz');
9696
await TestSnaps.tapButton('signMessageBip32Secp256k1Button');
9797
await Assertions.checkIfTextIsDisplayed('Signature request');
@@ -103,7 +103,7 @@ describe(FlaskBuildTests('BIP-32 Snap Tests'), () => {
103103
});
104104

105105
it('can sign BIP-32 message using secp256k1 and SRP 2', async () => {
106-
await TestSnaps.selectEntropySource('bip32EntropyDropDown', 'SRP 2');
106+
await TestSnaps.selectInDropdown('bip32EntropyDropDown', 'SRP 2');
107107
await TestSnaps.fillMessage('messageSecp256k1Input', 'bar baz');
108108
await TestSnaps.tapButton('signMessageBip32Secp256k1Button');
109109
await Assertions.checkIfTextIsDisplayed('Signature request');
@@ -115,7 +115,7 @@ describe(FlaskBuildTests('BIP-32 Snap Tests'), () => {
115115
});
116116

117117
it('fails when choosing the invalid entropy source', async () => {
118-
await TestSnaps.selectEntropySource('bip32EntropyDropDown', 'Invalid');
118+
await TestSnaps.selectInDropdown('bip32EntropyDropDown', 'Invalid');
119119
await TestSnaps.fillMessage('messageSecp256k1Input', 'bar baz');
120120
await TestSnaps.tapButton('signMessageBip32Secp256k1Button');
121121
await Assertions.checkIfTextIsDisplayed(

e2e/specs/snaps/test-snap-bip-44.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe(FlaskBuildTests('BIP-44 Snap Tests'), () => {
6868
});
6969

7070
it('can sign with entropy from SRP 1', async () => {
71-
await TestSnaps.selectEntropySource('bip44EntropyDropDown', 'SRP 1');
71+
await TestSnaps.selectInDropdown('bip44EntropyDropDown', 'SRP 1');
7272
await TestSnaps.fillMessage('messageBip44Input', 'foo bar');
7373
await TestSnaps.tapButton('signMessageBip44Button');
7474
await TestSnaps.approveSignRequest();
@@ -79,7 +79,7 @@ describe(FlaskBuildTests('BIP-44 Snap Tests'), () => {
7979
});
8080

8181
it('can sign with entropy from SRP 2', async () => {
82-
await TestSnaps.selectEntropySource('bip44EntropyDropDown', 'SRP 2');
82+
await TestSnaps.selectInDropdown('bip44EntropyDropDown', 'SRP 2');
8383
await TestSnaps.fillMessage('messageBip44Input', 'foo bar');
8484
await TestSnaps.tapButton('signMessageBip44Button');
8585
await TestSnaps.approveSignRequest();
@@ -90,7 +90,7 @@ describe(FlaskBuildTests('BIP-44 Snap Tests'), () => {
9090
});
9191

9292
it('fails when choosing the invalid entropy source', async () => {
93-
await TestSnaps.selectEntropySource('bip44EntropyDropDown', 'Invalid');
93+
await TestSnaps.selectInDropdown('bip44EntropyDropDown', 'Invalid');
9494
await TestSnaps.fillMessage('messageBip44Input', 'foo bar');
9595
await TestSnaps.tapButton('signMessageBip44Button');
9696
await Assertions.checkIfTextIsDisplayed(
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import TestHelpers from '../../helpers';
2+
import { FlaskBuildTests } from '../../tags';
3+
import { loginToApp } from '../../viewHelper';
4+
import FixtureBuilder from '../../fixtures/fixture-builder';
5+
import { withFixtures } from '../../fixtures/fixture-helper';
6+
import Assertions from '../../utils/Assertions';
7+
import TabBarComponent from '../../pages/wallet/TabBarComponent';
8+
import BrowserView from '../../pages/Browser/BrowserView';
9+
import TestSnaps from '../../pages/Browser/TestSnaps';
10+
import ConnectBottomSheet from '../../pages/Browser/ConnectBottomSheet';
11+
import { mockEvents } from '../../api-mocking/mock-config/mock-events';
12+
import RequestTypes from '../../pages/Browser/Confirmations/RequestTypes';
13+
14+
describe(FlaskBuildTests('Ethereum Provider Snap Tests'), () => {
15+
beforeAll(async () => {
16+
await TestHelpers.reverseServerPort();
17+
});
18+
19+
beforeEach(() => {
20+
jest.setTimeout(150000);
21+
});
22+
23+
it('can use the Ethereum provider', async () => {
24+
await withFixtures(
25+
{
26+
fixture: new FixtureBuilder().withMultiSRPKeyringController().build(),
27+
restartDevice: true,
28+
testSpecificMock: {
29+
GET: [mockEvents.GET.remoteFeatureFlagsRedesignedConfirmationsFlask],
30+
},
31+
},
32+
async () => {
33+
await loginToApp();
34+
35+
// Navigate to test snaps URL once for all tests
36+
await TabBarComponent.tapBrowser();
37+
await TestSnaps.navigateToTestSnap();
38+
await TestHelpers.delay(3500); // Wait for page to load
39+
await Assertions.checkIfVisible(BrowserView.browserScreenID);
40+
41+
await TestSnaps.installSnap('connectEthereumProviderButton');
42+
43+
await TestSnaps.tapButton('getChainIdButton');
44+
await TestHelpers.delay(500);
45+
await TestSnaps.checkResultSpan('ethereumProviderResultSpan', '"0x1"');
46+
47+
await TestSnaps.tapButton('getAccountsButton');
48+
await Assertions.checkIfVisible(ConnectBottomSheet.connectButton);
49+
await ConnectBottomSheet.tapConnectButton();
50+
await TestHelpers.delay(500);
51+
await TestSnaps.checkResultSpanIncludes(
52+
'ethereumProviderResultSpan',
53+
'"0x5cfe73b6021e818b776b421b1c4db2474086a7e1"',
54+
);
55+
56+
// Test `personal_sign`.
57+
await TestSnaps.fillMessage('personalSignMessageInput', 'foo');
58+
await TestSnaps.tapButton('personalSignButton');
59+
await Assertions.checkIfVisible(RequestTypes.PersonalSignRequest);
60+
await TestSnaps.approveNativeConfirmation();
61+
await TestSnaps.checkResultSpan(
62+
'personalSignResultSpan',
63+
'"0xf63c587cd42e7775e2e815a579f9744ea62944f263b3e69fad48535ba98a5ea107bc878088a99942733a59a89ef1d590eafdb467d59cf76564158d7e78351b751b"',
64+
);
65+
66+
// Test `eth_signTypedData_v4`.
67+
await TestSnaps.fillMessage('signTypedDataMessageInput', 'bar');
68+
await TestSnaps.tapButton('signTypedDataButton');
69+
await Assertions.checkIfVisible(RequestTypes.TypedSignRequest);
70+
await TestSnaps.approveNativeConfirmation();
71+
await TestSnaps.checkResultSpan(
72+
'signTypedDataResultSpan',
73+
'"0x7024dc071a7370eee444b2a3edc08d404dd03393694403cdca864653a7e8dd7c583419293d53602666cbe77faa8819fba04f8c57e95df2d4c0190968eece28021c"',
74+
);
75+
76+
// Check other networks.
77+
await TestSnaps.selectInDropdown('networkDropDown', 'Ethereum');
78+
await TestSnaps.tapButton('getChainIdButton');
79+
await TestHelpers.delay(500);
80+
await TestSnaps.checkResultSpan('ethereumProviderResultSpan', '"0x1"');
81+
82+
await TestSnaps.selectInDropdown('networkDropDown', 'Linea');
83+
await TestSnaps.tapButton('getChainIdButton');
84+
await TestHelpers.delay(500);
85+
await TestSnaps.checkResultSpan(
86+
'ethereumProviderResultSpan',
87+
'"0xe708"',
88+
);
89+
90+
await TestSnaps.selectInDropdown('networkDropDown', 'Sepolia');
91+
await TestSnaps.tapButton('getChainIdButton');
92+
await TestHelpers.delay(500);
93+
await TestSnaps.checkResultSpan(
94+
'ethereumProviderResultSpan',
95+
'"0xaa36a7"',
96+
);
97+
},
98+
);
99+
});
100+
});

e2e/specs/snaps/test-snap-network-access.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe(FlaskBuildTests('Network Access Snap Tests'), () => {
4747

4848
// Use WebSockets
4949
const webSocketUrl = `ws://localhost:${AnvilPort()}`;
50-
await TestSnaps.fillMessage('webSocketUrlInput', webSocketUrl)
50+
await TestSnaps.fillMessage('webSocketUrlInput', webSocketUrl);
5151
await TestSnaps.tapButton('startWebSocket');
5252

5353
await TestSnaps.waitForWebSocketUpdate({

0 commit comments

Comments
 (0)