Skip to content

Commit

Permalink
test: Refining E2E Testing with API Mocking (#11797)
Browse files Browse the repository at this point in the history
<!--
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**
This change enhances the mock functionality to enable multiple dynamic
events to be passed through. This will evolve over time with how people
use it. A corresponding document has been created in the contributors'
repository.
<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Related issues**

Fixes:

https://github.com/orgs/MetaMask/projects/60/views/3?pane=issue&itemId=81125053&issue=MetaMask%7Cmobile-planning%7C1944


## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->


## **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
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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.
  • Loading branch information
Andepande authored Oct 30, 2024
1 parent a803756 commit f36d6c9
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 191 deletions.
57 changes: 57 additions & 0 deletions e2e/api-mocking/mock-config/mock-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Mock events for gas fee API responses.
*/

import {
suggestedGasApiResponses,
suggestedGasFeesApiGanache,
} from '../mock-responses/gas-api-responses.json';

export const mockEvents = {
/**
* Mock GET request events.
*/
GET: {
/**
* Mainnet gas fees endpoint with a mock 500 error response.
* @property {string} urlEndpoint - API endpoint for mainnet gas fees.
* @property {Object} response - Error response data.
*/
suggestedGasFeesMainNetError: {
urlEndpoint: 'https://gas.api.cx.metamask.io/networks/1/suggestedGasFees',
response: suggestedGasApiResponses.error,
responseCode: 500,
},

/**
* Ganache gas fees endpoint with a mock 200 success response.
* @property {string} urlEndpoint - API endpoint for Ganache gas fees.
* @property {Object} response - Success response data.
*/
suggestedGasFeesApiGanache: {
urlEndpoint: 'https://gas.api.cx.metamask.io/networks/1337/suggestedGasFees',
response: suggestedGasFeesApiGanache,
responseCode: 200,
},
},

/**
* Mock POST request events.
*/
POST: {
/**
* Mainnet gas fees endpoint with a mock success response for POST requests.
* @property {string} urlEndpoint - API endpoint for mainnet gas fees.
* @property {Object} response - Success response data.
* @property {Object} requestBody - Expected fields for the POST request body.
*/
suggestedGasApiPostResponse: {
urlEndpoint: 'https://gas.api.cx.metamask.io/networks/1/suggestedGasFees',
response: suggestedGasApiResponses.success,
requestBody: {
priorityFee: '2',
maxFee: '2.000855333',
},
},
},
};
3 changes: 3 additions & 0 deletions e2e/api-mocking/mock-config/mockUrlCollection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"defaultMockPort": 8000
}
36 changes: 36 additions & 0 deletions e2e/api-mocking/mock-responses/gas-api-responses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"suggestedGasApiResponses": {
"error": {
"message": "Internal Server Error"
}
},

"suggestedGasFeesApiGanache": {
"low": {
"suggestedMaxPriorityFeePerGas": "1",
"suggestedMaxFeePerGas": "1.000503137",
"minWaitTimeEstimate": 15000,
"maxWaitTimeEstimate": 60000
},
"medium": {
"suggestedMaxPriorityFeePerGas": "1.5",
"suggestedMaxFeePerGas": "1.500679235",
"minWaitTimeEstimate": 15000,
"maxWaitTimeEstimate": 45000
},
"high": {
"suggestedMaxPriorityFeePerGas": "2",
"suggestedMaxFeePerGas": "2.000855333",
"minWaitTimeEstimate": 15000,
"maxWaitTimeEstimate": 30000
},
"estimatedBaseFee": "0.000503137",
"networkCongestion": 0.34,
"latestPriorityFeeRange": ["1.5", "2"],
"historicalPriorityFeeRange": ["0.000001", "236.428872442"],
"historicalBaseFeeRange": ["0.000392779", "0.00100495"],
"priorityFeeTrend": "up",
"baseFeeTrend": "up",
"version": "0.0.1"
}
}
81 changes: 81 additions & 0 deletions e2e/api-mocking/mock-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* eslint-disable no-console */
import { getLocal } from 'mockttp';
import portfinder from 'portfinder';

const mockServer = getLocal();

/**
* Starts the mock server and sets up mock events.
*
* @param {Object} events - The events to mock, organised by method.
* @param {number} [port] - Optional port number. If not provided, a free port will be used.
* @returns {Promise} Resolves to the running mock server.
*/
export const startMockServer = async (events, port) => {
port = port || (await portfinder.getPortPromise());

await mockServer.start(port);
console.log(`Mockttp server running at http://localhost:${port}`);

await mockServer
.forGet('/health-check')
.thenReply(200, 'Mock server is running');

for (const method in events) {
const methodEvents = events[method];

console.log(`Setting up mock events for ${method} requests...`);

for (const {
urlEndpoint,
response,
requestBody,
responseCode,
} of methodEvents) {
console.log(`Mocking ${method} request to: ${urlEndpoint}`);
console.log(`Response status: ${responseCode}`);
console.log('Response:', response);
if (requestBody) {
console.log(`POST request body ${requestBody}`);
}

if (method === 'GET') {
await mockServer
.forGet('/proxy')
.withQuery({ url: urlEndpoint })
.thenReply(responseCode, JSON.stringify(response));
}

if (method === 'POST') {
await mockServer
.forPost('/proxy')
.withQuery({ url: urlEndpoint })
.withJsonBody(requestBody || {})
.thenReply(responseCode, JSON.stringify(response));
}
}
}

await mockServer.forUnmatchedRequest().thenPassThrough({
beforeRequest: async ({ url, method }) => {
const returnUrl = new URL(url).searchParams.get('url') || url;
const updatedUrl =
device.getPlatform() === 'android'
? returnUrl.replace('localhost', '127.0.0.1')
: returnUrl;
console.log(`Mock proxy forwarding request to: ${updatedUrl}`);
return { url: updatedUrl };
},
});

return mockServer;
};

/**
* Stops the mock server.
*
*/
export const stopMockServer = async () => {
await mockServer.stop();
console.log('Mock server shutting down');
};
7 changes: 4 additions & 3 deletions e2e/fixtures/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { DEFAULT_GANACHE_PORT } from '../../app/util/test/ganache';
import { DEFAULT_FIXTURE_SERVER_PORT } from './fixture-server';
import { DEFAULT_DAPP_SERVER_PORT } from './fixture-helper';
import { defaultMockPort } from './../mockServer/mockUrlCollection';
import { DEFAULT_MOCKSERVER_PORT } from '../api-mocking/mock-server';


function transformToValidPort(defaultPort, pid) {
// Improve uniqueness by using a simple transformation
Expand Down Expand Up @@ -30,6 +31,6 @@ export function getLocalTestDappPort() {
return getServerPort(DEFAULT_DAPP_SERVER_PORT);
}

export function getMockttpPort() {
return getServerPort(defaultMockPort);
export function getMockServerPort() {
return getServerPort(DEFAULT_MOCKSERVER_PORT);
}
2 changes: 0 additions & 2 deletions e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
getFixturesServerPort,
getGanachePort,
getLocalTestDappPort,
getMockttpPort,
} from './fixtures/utils';
import Utilities from './utils/Utilities';

Expand Down Expand Up @@ -277,7 +276,6 @@ export default class TestHelpers {
await device.reverseTcpPort(getGanachePort());
await device.reverseTcpPort(getFixturesServerPort());
await device.reverseTcpPort(getLocalTestDappPort());
await device.reverseTcpPort(getMockttpPort());
}
}
}
37 changes: 0 additions & 37 deletions e2e/mockServer/data/suggestedGasApiGanacheResponseBody.json

This file was deleted.

45 changes: 0 additions & 45 deletions e2e/mockServer/mockServer.js

This file was deleted.

8 changes: 0 additions & 8 deletions e2e/mockServer/mockUrlCollection.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import Assertions from '../../utils/Assertions';
import {
startMockServer,
stopMockServer
} from '../../mockServer/mockServer';
import { urls } from '../../mockServer/mockUrlCollection.json';
import responseBody from '../../mockServer/data/suggestedGasApiGanacheResponseBody.json';
} from '../../api-mocking/mock-server';
import { mockEvents } from '../../api-mocking/mock-config/mock-events';

const VALID_ADDRESS = '0xebe6CcB6B55e1d094d9c58980Bc10Fed69932cAb';

Expand All @@ -28,11 +27,10 @@ describe(SmokeConfirmations('Advanced Gas Fees and Priority Tests'), () => {
beforeAll(async () => {
jest.setTimeout(170000);
await TestHelpers.reverseServerPort();

mockServer = await startMockServer({ // Configure mock server
mockUrl: urls.suggestedGasApiGanache,
responseCode: 200,
responseBody
mockServer = await startMockServer({
GET: [
mockEvents.GET.suggestedGasFeesApiGanache
],
});

});
Expand Down
Loading

0 comments on commit f36d6c9

Please sign in to comment.