Skip to content

Commit d6c9db2

Browse files
authored
test: split Solana WebSocket Mock setup logic from Websocket Mocks (#35552)
<!-- 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** As a follow-up of [this PR](#35425), which separated the logic for setting up the Websocket, from the test steps, now this PR separates the logic for handling mocked wss requests from the setup websocket server. This means that now we can pass an array of websocket mocks. Note, that the websocket mocks are different from the regular Http mocks, they look like this: ``` export interface WebSocketMessageMock { messageIncludes: string | string[]; response: object; delay?: number; logMessage?: string; } ``` and the logic for handling the mocks is also different, as we are handling this in our local websocket server. Meaning: 1. From Mocktt default mocks --> we check if we need a Websocket Solana server, if so, we re-route any wss to the local wss 2. We setup the local Websocket server 3. We add 'onMessage' listeners to intercept the requests we want to mock, and we send back the desired response [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/35552?quickstart=1) ## **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` 4. 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: ## **Manual testing steps** 1. Go to this page... 2. 5. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/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-extension/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.
1 parent b764cd1 commit d6c9db2

File tree

6 files changed

+128
-82
lines changed

6 files changed

+128
-82
lines changed

test/e2e/helpers.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const {
2626
getServerMochaToBackground,
2727
} = require('./background-socket/server-mocha-to-background');
2828
const LocalWebSocketServer = require('./websocket-server').default;
29-
const { setSolanaWebsocketMocks } = require('./websocket-solana-mocks');
29+
const { setupSolanaWebsocketMocks } = require('./websocket-solana-mocks');
3030

3131
const tinyDelayMs = 200;
3232
const regularDelayMs = tinyDelayMs * 2;
@@ -128,7 +128,10 @@ async function withFixtures(options, testSuite) {
128128
ethConversionInUsd,
129129
monConversionInUsd,
130130
manifestFlags,
131-
withSolanaWebSocket = false,
131+
withSolanaWebSocket = {
132+
server: false,
133+
mocks: [],
134+
},
132135
} = options;
133136

134137
// Normalize localNodeOptions
@@ -261,12 +264,10 @@ async function withFixtures(options, testSuite) {
261264
}
262265
}
263266

264-
if (withSolanaWebSocket) {
267+
if (withSolanaWebSocket.server) {
265268
localWebSocketServer = LocalWebSocketServer.getServerInstance();
266269
localWebSocketServer.start();
267-
// All specs use the same ws mocks.
268-
// If we need custom ws mocks we can expand logic for supporting custom ws mocks like with http
269-
await setSolanaWebsocketMocks();
270+
await setupSolanaWebsocketMocks(withSolanaWebSocket.mocks);
270271
}
271272

272273
const { mockedEndpoint, getPrivacyReport } = await setupMocking(
@@ -454,7 +455,7 @@ async function withFixtures(options, testSuite) {
454455
})(),
455456
);
456457

457-
if (withSolanaWebSocket) {
458+
if (withSolanaWebSocket.server) {
458459
shutdownTasks.push(localWebSocketServer.stopAndCleanup());
459460
}
460461

test/e2e/mock-e2e.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ const privateHostMatchers = [
143143
* @param {object} options - Network mock options.
144144
* @param {string} options.chainId - The chain ID used by the default configured network.
145145
* @param {string} options.ethConversionInUsd - The USD conversion rate for ETH.
146-
* @param {boolean} withSolanaWebSocket - If we want to re-route all the ws requests to our Solana Local WS server
146+
* @param {object} withSolanaWebSocket - Solana WebSocket configuration with server flag and mocks function
147147
* @returns {Promise<SetupMockReturn>}
148148
*/
149149
async function setupMocking(
@@ -946,7 +946,7 @@ async function setupMocking(
946946
* Solana Websocket
947947
* Setup HTTP intercept for WebSocket handshake requests
948948
*/
949-
if (withSolanaWebSocket) {
949+
if (withSolanaWebSocket.server) {
950950
await server
951951
.forAnyWebSocket()
952952
.matching((req) =>

test/e2e/tests/solana/common-solana.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ACCOUNT_TYPE } from '../../constants';
1111
import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow';
1212
import { mockProtocolSnap } from '../../mock-response-data/snaps/snap-binary-mocks';
1313
import AssetListPage from '../../page-objects/pages/home/asset-list';
14+
import { DEFAULT_SOLANA_WS_MOCKS } from './mocks/websocketDefaultMocks';
1415

1516
const SOLANA_URL_REGEX_MAINNET =
1617
/^https:\/\/solana-(mainnet|devnet)\.infura\.io\/v3*/u;
@@ -1611,7 +1612,10 @@ export async function withSolanaAccountSnap(
16111612
fixtures: fixtures.build(),
16121613
title,
16131614
dapp: true,
1614-
withSolanaWebSocket: true,
1615+
withSolanaWebSocket: {
1616+
server: true,
1617+
mocks: DEFAULT_SOLANA_WS_MOCKS,
1618+
},
16151619
manifestFlags: {
16161620
// This flag is used to enable/disable the remote mode for the carousel
16171621
// component, which will impact to the slides count.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Configuration for a WebSocket message mock
3+
*/
4+
export type WebSocketMessageMock = {
5+
/** String(s) that the message should include to trigger this mock */
6+
messageIncludes: string | string[];
7+
/** The JSON response to send back */
8+
response: object;
9+
/** Delay before sending the response (in milliseconds) */
10+
delay?: number;
11+
/** Custom log message for this mock */
12+
logMessage?: string;
13+
};
14+
15+
export const DEFAULT_SOLANA_WS_MOCKS: WebSocketMessageMock[] = [
16+
{
17+
messageIncludes: 'signatureSubscribe',
18+
response: {
19+
jsonrpc: '2.0',
20+
result: 8648699534240963,
21+
id: '1',
22+
},
23+
delay: 500,
24+
logMessage: 'Signature subscribe message received from client',
25+
},
26+
{
27+
messageIncludes: 'accountSubscribe',
28+
response: {
29+
jsonrpc: '2.0',
30+
result:
31+
'b07ebf7caf2238a9b604d4dfcaf1934280fcd347d6eded62bc0def6cbb767d11',
32+
id: '1',
33+
},
34+
delay: 500,
35+
logMessage: 'Account subscribe message received from client',
36+
},
37+
{
38+
messageIncludes: [
39+
'programSubscribe',
40+
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
41+
],
42+
response: {
43+
jsonrpc: '2.0',
44+
result:
45+
'568eafd45635c108d0d426361143de125a841628a58679f5a024cbab9a20b41c',
46+
id: '1',
47+
},
48+
delay: 500,
49+
logMessage: 'Program subscribe message received from client',
50+
},
51+
{
52+
messageIncludes: [
53+
'programSubscribe',
54+
'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb',
55+
],
56+
response: {
57+
jsonrpc: '2.0',
58+
result:
59+
'f33dd9975158af47bf16c7f6062a73191d4595c59cfec605d5a51e25c65ffb51',
60+
id: '1',
61+
},
62+
delay: 500,
63+
logMessage: 'Program subscribe message received from client',
64+
},
65+
];

test/e2e/tests/solana/web-socket-connection.spec.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ import HeaderNavbar from '../../page-objects/pages/header-navbar';
88
import AccountListPage from '../../page-objects/pages/account-list-page';
99
import FixtureBuilder from '../../fixture-builder';
1010
import LocalWebSocketServer from '../../websocket-server';
11+
import { DEFAULT_SOLANA_WS_MOCKS } from './mocks/websocketDefaultMocks';
1112

1213
describe('Solana Web Socket', function (this: Suite) {
1314
it('a websocket connection is open when MetaMask full view is open', async function () {
1415
await withFixtures(
1516
{
1617
fixtures: new FixtureBuilder().build(),
1718
title: this.test?.fullTitle(),
18-
withSolanaWebSocket: true,
19+
withSolanaWebSocket: {
20+
server: true,
21+
mocks: DEFAULT_SOLANA_WS_MOCKS,
22+
},
1923
manifestFlags: {
2024
remoteFeatureFlags: {
2125
addSolanaAccount: true,
@@ -50,7 +54,10 @@ describe('Solana Web Socket', function (this: Suite) {
5054
{
5155
fixtures: new FixtureBuilder().build(),
5256
title: this.test?.fullTitle(),
53-
withSolanaWebSocket: true,
57+
withSolanaWebSocket: {
58+
server: true,
59+
mocks: DEFAULT_SOLANA_WS_MOCKS,
60+
},
5461
manifestFlags: {
5562
remoteFeatureFlags: {
5663
addSolanaAccount: true,
@@ -95,7 +102,10 @@ describe('Solana Web Socket', function (this: Suite) {
95102
{
96103
fixtures: new FixtureBuilder().build(),
97104
title: this.test?.fullTitle(),
98-
withSolanaWebSocket: true,
105+
withSolanaWebSocket: {
106+
server: true,
107+
mocks: DEFAULT_SOLANA_WS_MOCKS,
108+
},
99109
manifestFlags: {
100110
remoteFeatureFlags: {
101111
addSolanaAccount: true,

test/e2e/websocket-solana-mocks.ts

Lines changed: 35 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
// eslint-disable-next-line @typescript-eslint/no-shadow
22
import { WebSocket } from 'ws';
33
import LocalWebSocketServer from './websocket-server';
4+
import { WebSocketMessageMock } from './tests/solana/mocks/websocketDefaultMocks';
45

56
/**
6-
* WebSocket Solana mocks
7-
* This function should be called after the WebSocket server is started
7+
* Sets up Solana WebSocket mocks with configurable message handlers
8+
*
9+
* @param mocks - Array of message mock configurations
810
*/
9-
export async function setSolanaWebsocketMocks(): Promise<void> {
11+
export async function setupSolanaWebsocketMocks(
12+
mocks: WebSocketMessageMock[] = [],
13+
): Promise<void> {
1014
const localWebSocketServer = LocalWebSocketServer.getServerInstance();
1115
const wsServer = localWebSocketServer.getServer();
1216

@@ -18,72 +22,34 @@ export async function setSolanaWebsocketMocks(): Promise<void> {
1822
socket.on('message', (data) => {
1923
const message = data.toString();
2024
console.log('Message received from client:', message);
21-
if (message.includes('signatureSubscribe')) {
22-
console.log('Signature subscribe message received from client');
23-
setTimeout(() => {
24-
socket.send(
25-
JSON.stringify({
26-
jsonrpc: '2.0',
27-
result: 8648699534240963,
28-
id: '1',
29-
}),
30-
);
31-
console.log('Simulated message sent to the client');
32-
}, 500); // Delay the message by 500ms
33-
}
34-
if (message.includes('accountSubscribe')) {
35-
console.log('Account subscribe message received from client');
36-
setTimeout(() => {
37-
socket.send(
38-
JSON.stringify({
39-
jsonrpc: '2.0',
40-
result:
41-
'b07ebf7caf2238a9b604d4dfcaf1934280fcd347d6eded62bc0def6cbb767d11',
42-
id: '1',
43-
}),
44-
);
45-
console.log(
46-
'Simulated message for accountSubscribe sent to the client',
47-
);
48-
}, 500); // Delay the message by 500ms
49-
}
50-
if (
51-
message.includes('programSubscribe') &&
52-
message.includes('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA')
53-
) {
54-
console.log('Program subscribe message received from client');
55-
setTimeout(() => {
56-
socket.send(
57-
JSON.stringify({
58-
jsonrpc: '2.0',
59-
result:
60-
'568eafd45635c108d0d426361143de125a841628a58679f5a024cbab9a20b41c',
61-
id: '1',
62-
}),
63-
);
64-
console.log(
65-
'Simulated message for programSubscribe Token2022 sent to the client',
66-
);
67-
}, 500); // Delay the message by 500ms
68-
}
69-
if (
70-
message.includes('programSubscribe') &&
71-
message.includes('TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb')
72-
) {
73-
console.log('Program subscribe message received from client');
74-
setTimeout(() => {
75-
socket.send(
76-
JSON.stringify({
77-
jsonrpc: '2.0',
78-
result:
79-
'f33dd9975158af47bf16c7f6062a73191d4595c59cfec605d5a51e25c65ffb51',
80-
id: '1',
81-
}),
82-
);
83-
console.log(
84-
'Simulated message for programSubscribe sent to the client',
85-
);
86-
}, 500); // Delay the message by 500ms
25+
26+
// Check each mock configuration
27+
for (const mock of mocks) {
28+
const includes = Array.isArray(mock.messageIncludes)
29+
? mock.messageIncludes
30+
: [mock.messageIncludes];
31+
32+
// Check if all required strings are included in the message
33+
const matches = includes.every((includeStr) =>
34+
message.includes(includeStr),
35+
);
36+
37+
if (matches) {
38+
if (mock.logMessage) {
39+
console.log(mock.logMessage);
40+
}
41+
42+
const delay = mock.delay || 500;
43+
setTimeout(() => {
44+
socket.send(JSON.stringify(mock.response));
45+
console.log(
46+
`Simulated message sent to the client for: ${includes.join(' + ')}`,
47+
);
48+
}, delay);
49+
50+
// Break after first match to avoid multiple responses
51+
break;
52+
}
8753
}
8854
});
8955
});

0 commit comments

Comments
 (0)