1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Expand network controller unit test coverage (#17498)

The network controller has some tests, but they are incomplete and don't
follow our latest best practices for writing unit tests.

This commit greatly increases the amount of test coverage for the API
that we want to retain in NetworkController, in particular the seemingly
myriad paths that the code takes starting from `initializeProvider`,
`resetConnection`, `setRpcTarget`, `setProviderType`,
`rollbackToPreviousProvider`, and `lookupNetwork`.

There were a couple of pieces of logic I noted which don't seem to have
any effect due to being redundant or unreachable, but they also don't
make our lives more difficult, either, so I opted to leave them in.

Co-authored-by: Mark Stacey <markjstacey@gmail.com>
Co-authored-by: Zachary Belford <belfordz66@gmail.com>
This commit is contained in:
Elliot Winkler 2023-02-08 13:10:04 -07:00 committed by GitHub
parent 53205b6bff
commit 7e97ff2be4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 4944 additions and 193 deletions

View File

@ -125,7 +125,9 @@ export default class NetworkController extends EventEmitter {
} }
this._infuraProjectId = infuraProjectId; this._infuraProjectId = infuraProjectId;
this.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, this.lookupNetwork); this.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
this.lookupNetwork();
});
} }
/** /**
@ -158,6 +160,8 @@ export default class NetworkController extends EventEmitter {
*/ */
async getEIP1559Compatibility() { async getEIP1559Compatibility() {
const { EIPS } = this.networkDetails.getState(); const { EIPS } = this.networkDetails.getState();
// NOTE: This isn't necessary anymore because the block cache middleware
// already prevents duplicate requests from taking place
if (EIPS[1559] !== undefined) { if (EIPS[1559] !== undefined) {
return EIPS[1559]; return EIPS[1559];
} }
@ -185,6 +189,9 @@ export default class NetworkController extends EventEmitter {
return; return;
} }
// NOTE: This will never happen in practice because you can't pass null or
// undefined for chainId to setRpcTarget, and all of the known networks have
// a chain ID
const chainId = this.getCurrentChainId(); const chainId = this.getCurrentChainId();
if (!chainId) { if (!chainId) {
log.warn( log.warn(

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,80 @@ jest.mock('webextension-polyfill', () => {
}; };
}); });
const UNRESOLVED = Symbol('timedOut');
// Store this in case it gets stubbed later
const originalSetTimeout = global.setTimeout;
const TIME_TO_WAIT_UNTIL_UNRESOLVED = 100;
/**
* Produces a sort of dummy promise which can be used in conjunction with a
* "real" promise to determine whether the "real" promise was ever resolved. If
* the promise that is produced by this function resolves first, then the other
* one must be unresolved.
*
* @param {number} duration - How long to wait before resolving the promise returned by
* this function.
* @returns A promise that resolves to a symbol.
*/
function treatUnresolvedAfter(duration) {
return new Promise((resolve) => {
originalSetTimeout(resolve, duration, UNRESOLVED);
});
}
/* eslint-disable-next-line jest/require-top-level-describe */ /* eslint-disable-next-line jest/require-top-level-describe */
beforeEach(() => { beforeEach(() => {
nock.cleanAll(); nock.cleanAll();
}); });
expect.extend({
/**
* Tests that the given promise is never fulfilled or rejected past a certain
* amount of time (which is the default time that Jest tests wait before
* timing out as configured in the Jest configuration file).
*
* Inspired by <https://stackoverflow.com/a/68409467/260771>.
*
* @param {Promise<any>} promise - The promise to test.
* @returns The result of the matcher.
*/
async toNeverResolve(promise) {
if (this.isNot) {
throw new Error(
'Using `.not.toNeverResolve(...)` is not supported. ' +
'You probably want to either `await` the promise and test its ' +
'resolution value or use `.rejects` to test its rejection value instead.',
);
}
let resolutionValue;
let rejectionValue;
try {
resolutionValue = await Promise.race([
promise,
treatUnresolvedAfter(TIME_TO_WAIT_UNTIL_UNRESOLVED),
]);
} catch (e) {
rejectionValue = e;
}
return resolutionValue === UNRESOLVED
? {
message: () =>
`Expected promise to resolve after ${TIME_TO_WAIT_UNTIL_UNRESOLVED}ms, but it did not`,
pass: true,
}
: {
message: () => {
return `Expected promise to never resolve after ${TIME_TO_WAIT_UNTIL_UNRESOLVED}ms, but it ${
rejectionValue
? `was rejected with ${rejectionValue}`
: `resolved with ${resolutionValue}`
}`;
},
pass: false,
};
},
});