From 30600f7b795f519cae22b8680584628c39974ccf Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Fri, 16 Oct 2026 21:36:06 +0200 Subject: [PATCH 1/2] test: run network-controller unit tests on Vitest --- oxlint-suppressions.json | 21 +- packages/network-controller/jest.config.cjs | 32 --- packages/network-controller/package.json | 15 +- ...create-auto-managed-network-client.test.ts | 17 +- .../classify-retry-reason.test.ts | 1 + .../block-hash-in-response.test.ts | 2 + .../ethereum-spec/block-param.test.ts | 2 + .../ethereum-spec/no-block-param.test.ts | 2 + .../not-handled-by-middleware.test.ts | 2 + .../ethereum-spec/other-methods.test.ts | 2 + .../ex-ethereum-spec/no-block-param.test.ts | 2 + .../not-handled-by-middleware.test.ts | 2 + .../ex-ethereum-spec/other-methods.test.ts | 2 + .../rpc-endpoint-events.test.ts | 71 ++--- .../rpc-endpoint-failover.test.ts | 2 + .../src/rpc-service-analytics.test.ts | 1 + .../src/rpc-service/rpc-service-chain.test.ts | 172 ++++++------ .../src/rpc-service/rpc-service.test.ts | 180 +++++++------ .../network-controller/src/selectors.test.ts | 2 + .../tests/NetworkController.analytics.test.ts | 8 +- .../tests/NetworkController.provider.test.ts | 43 +-- .../tests/NetworkController.test.ts | 255 ++++++++---------- packages/network-controller/tests/helpers.ts | 8 +- .../network-client/block-hash-in-response.ts | 9 +- .../tests/network-client/block-param.ts | 15 +- .../tests/network-client/helpers.ts | 13 +- .../tests/network-client/no-block-param.ts | 11 +- .../not-handled-by-middleware.ts | 1 + .../tests/network-client/rpc-failover.ts | 24 +- packages/network-controller/tsconfig.json | 3 +- packages/network-controller/vitest.config.js | 27 ++ test-runner-package-map.cjs | 1 - tests/vitest/helpers.ts | 18 +- yarn.lock | 5 +- 34 files changed, 502 insertions(+), 469 deletions(-) delete mode 100644 packages/network-controller/jest.config.cjs create mode 100644 packages/network-controller/vitest.config.js diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 3be4c03aecd..04e69eaa9dc 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -4232,11 +4232,6 @@ "count": 1 } }, - "packages/network-controller/jest.config.cjs": { - "import/unambiguous": { - "count": 1 - } - }, "packages/network-controller/src/NetworkController.ts": { "typescript/no-unsafe-assignment": { "count": 1 @@ -4291,7 +4286,7 @@ "count": 1 }, "typescript/no-unsafe-argument": { - "count": 4 + "count": 1 }, "typescript/no-unsafe-assignment": { "count": 14 @@ -4321,16 +4316,10 @@ "count": 2 }, "typescript/no-unsafe-assignment": { - "count": 60 - }, - "typescript/no-unsafe-return": { - "count": 1 + "count": 59 } }, "packages/network-controller/tests/network-client/block-hash-in-response.ts": { - "typescript/no-unsafe-argument": { - "count": 4 - }, "typescript/no-unsafe-assignment": { "count": 14 }, @@ -4339,9 +4328,6 @@ } }, "packages/network-controller/tests/network-client/block-param.ts": { - "typescript/no-unsafe-argument": { - "count": 4 - }, "typescript/no-unsafe-assignment": { "count": 25 }, @@ -4364,9 +4350,6 @@ } }, "packages/network-controller/tests/network-client/no-block-param.ts": { - "typescript/no-unsafe-argument": { - "count": 4 - }, "typescript/no-unsafe-assignment": { "count": 16 }, diff --git a/packages/network-controller/jest.config.cjs b/packages/network-controller/jest.config.cjs deleted file mode 100644 index 13e94ab8056..00000000000 --- a/packages/network-controller/jest.config.cjs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * For a detailed explanation regarding each configuration property and type check, visit: - * https://jestjs.io/docs/configuration - */ - -const merge = require('deepmerge'); -const path = require('path'); - -const baseConfig = require('../../jest.config.packages.cjs'); - -const displayName = path.basename(__dirname); - -module.exports = merge(baseConfig, { - // The display name when running multiple projects - displayName, - - coveragePathIgnorePatterns: ['\\.tst\\.ts$'], - - // An object that configures minimum threshold enforcement for coverage results - coverageThreshold: { - global: { - branches: 93.69, - functions: 97.95, - lines: 97.87, - statements: 97.77, - }, - }, - - // Currently the tests for NetworkController have a race condition which - // causes intermittent failures. This seems to fix it. - testEnvironment: 'jsdom', -}); diff --git a/packages/network-controller/package.json b/packages/network-controller/package.json index d711c547d23..7681aac82f1 100644 --- a/packages/network-controller/package.json +++ b/packages/network-controller/package.json @@ -47,11 +47,11 @@ "test": "yarn test:unit && yarn test:types", "test:clean": "yarn test:unit:clean && yarn test:types", "test:types": "tstyche", - "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", - "test:unit:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache", - "test:unit:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose", + "test:unit": "vitest", + "test:unit:clean": "vitest --clearCache", + "test:unit:verbose": "vitest --reporter=verbose", "test:verbose": "yarn test:unit:verbose && yarn test:types", - "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" + "test:watch": "vitest --watch" }, "dependencies": { "@metamask/analytics-controller": "^4.0.0", @@ -89,18 +89,15 @@ "cockatiel": "^3.2.1", "deep-freeze-strict": "^1.1.1", "deepmerge": "^4.3.1", - "jest": "^30.5.2", - "jest-environment-jsdom": "^30.5.2", - "jest-when": "^4.0.3", "lodash-es": "^4.18.1", "nock": "^13.5.6", "node-fetch": "^2.7.0", "rimraf": "^6.1.3", - "ts-jest": "^29.4.14", "tstyche": "^7.2.5", "typedoc": "^0.28.20", "typedoc-plugin-missing-exports": "^4.1.4", - "typescript": "npm:@typescript/typescript6@^6.0.2" + "typescript": "npm:@typescript/typescript6@^6.0.2", + "vitest": "^5.0.0" }, "engines": { "node": "^22.14.0 || ^24" diff --git a/packages/network-controller/src/create-auto-managed-network-client.test.ts b/packages/network-controller/src/create-auto-managed-network-client.test.ts index cf32b1d09bf..7c365595269 100644 --- a/packages/network-controller/src/create-auto-managed-network-client.test.ts +++ b/packages/network-controller/src/create-auto-managed-network-client.test.ts @@ -1,5 +1,6 @@ import { BUILT_IN_NETWORKS, NetworkType } from '@metamask/controller-utils'; import { PollingBlockTrackerOptions } from '@metamask/eth-block-tracker'; +import { describe, expect, it, vi } from 'vitest'; import { buildNetworkControllerMessenger } from '../tests/helpers.js'; import { mockNetwork } from '../tests/mock-network.js'; @@ -141,7 +142,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -206,7 +207,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -281,7 +282,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -463,7 +464,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -524,7 +525,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -593,7 +594,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -664,7 +665,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); @@ -739,7 +740,7 @@ describe('createAutoManagedNetworkClient', () => { }, ], }); - const createNetworkClientMock = jest.spyOn( + const createNetworkClientMock = vi.spyOn( createNetworkClientModule, 'createNetworkClient', ); diff --git a/packages/network-controller/src/create-network-client-tests/classify-retry-reason.test.ts b/packages/network-controller/src/create-network-client-tests/classify-retry-reason.test.ts index 2521aeaba96..593ef93ead1 100644 --- a/packages/network-controller/src/create-network-client-tests/classify-retry-reason.test.ts +++ b/packages/network-controller/src/create-network-client-tests/classify-retry-reason.test.ts @@ -1,5 +1,6 @@ import { HttpError } from '@metamask/controller-utils'; import { FetchError } from 'node-fetch'; +import { describe, expect, it } from 'vitest'; import { classifyRetryReason } from '../create-network-client.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-hash-in-response.test.ts b/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-hash-in-response.test.ts index 72d0783427c..8210fbe0b7e 100644 --- a/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-hash-in-response.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-hash-in-response.test.ts @@ -1,3 +1,5 @@ +import { describe } from 'vitest'; + import { testsForRpcMethodsThatCheckForBlockHashInResponse } from '../../../tests/network-client/block-hash-in-response.js'; import { NetworkClientType } from '../../types.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-param.test.ts b/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-param.test.ts index a92213abe1c..f2cabf2e74b 100644 --- a/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-param.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ethereum-spec/block-param.test.ts @@ -1,3 +1,5 @@ +import { describe } from 'vitest'; + import { testsForRpcMethodSupportingBlockParam } from '../../../tests/network-client/block-param.js'; import { NetworkClientType } from '../../types.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ethereum-spec/no-block-param.test.ts b/packages/network-controller/src/create-network-client-tests/ethereum-spec/no-block-param.test.ts index ef78a00fa2f..b597dc3f844 100644 --- a/packages/network-controller/src/create-network-client-tests/ethereum-spec/no-block-param.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ethereum-spec/no-block-param.test.ts @@ -1,3 +1,5 @@ +import { describe } from 'vitest'; + import { testsForRpcMethodAssumingNoBlockParam } from '../../../tests/network-client/no-block-param.js'; import { NetworkClientType } from '../../types.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ethereum-spec/not-handled-by-middleware.test.ts b/packages/network-controller/src/create-network-client-tests/ethereum-spec/not-handled-by-middleware.test.ts index 3b1597fe3ca..cdfd10b6d22 100644 --- a/packages/network-controller/src/create-network-client-tests/ethereum-spec/not-handled-by-middleware.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ethereum-spec/not-handled-by-middleware.test.ts @@ -1,3 +1,5 @@ +import { describe } from 'vitest'; + import { testsForRpcMethodNotHandledByMiddleware } from '../../../tests/network-client/not-handled-by-middleware.js'; import { NetworkClientType } from '../../types.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ethereum-spec/other-methods.test.ts b/packages/network-controller/src/create-network-client-tests/ethereum-spec/other-methods.test.ts index aee90938e77..f766985762e 100644 --- a/packages/network-controller/src/create-network-client-tests/ethereum-spec/other-methods.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ethereum-spec/other-methods.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { withMockedCommunications, withNetworkClient, diff --git a/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/no-block-param.test.ts b/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/no-block-param.test.ts index c575356ad7c..097039e7e28 100644 --- a/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/no-block-param.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/no-block-param.test.ts @@ -1,3 +1,5 @@ +import { describe } from 'vitest'; + import { testsForRpcMethodAssumingNoBlockParam } from '../../../tests/network-client/no-block-param.js'; import { NetworkClientType } from '../../types.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/not-handled-by-middleware.test.ts b/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/not-handled-by-middleware.test.ts index 3d5a2513369..5358a28a8a4 100644 --- a/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/not-handled-by-middleware.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/not-handled-by-middleware.test.ts @@ -1,3 +1,5 @@ +import { describe } from 'vitest'; + import { testsForRpcMethodNotHandledByMiddleware } from '../../../tests/network-client/not-handled-by-middleware.js'; import { NetworkClientType } from '../../types.js'; diff --git a/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/other-methods.test.ts b/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/other-methods.test.ts index 7091a2f6203..ae9c981fc7b 100644 --- a/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/other-methods.test.ts +++ b/packages/network-controller/src/create-network-client-tests/ex-ethereum-spec/other-methods.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { TESTNET } from '../../../tests/helpers.js'; import { withMockedCommunications, diff --git a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts index 4822ddbabf6..9f9257c156c 100644 --- a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts +++ b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-events.test.ts @@ -5,6 +5,7 @@ import { HttpError, } from '@metamask/controller-utils'; import { errorCodes } from '@metamask/rpc-errors'; +import { describe, expect, it, vi } from 'vitest'; import { buildRootMessenger } from '../../tests/helpers.js'; import { @@ -65,7 +66,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }); const messenger = buildRootMessenger(); - const rpcEndpointChainUnavailableEventHandler = jest.fn(); + const rpcEndpointChainUnavailableEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainUnavailable', rpcEndpointChainUnavailableEventHandler, @@ -93,7 +94,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -182,7 +183,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }); const messenger = buildRootMessenger(); - const rpcEndpointUnavailableEventHandler = jest.fn(); + const rpcEndpointUnavailableEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointUnavailable', rpcEndpointUnavailableEventHandler, @@ -210,7 +211,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -300,7 +301,7 @@ describe('createNetworkClient - RPC endpoint events', () => { connectivityStatus: CONNECTIVITY_STATUSES.Offline, }); - const rpcEndpointRetriedEventHandler = jest.fn(); + const rpcEndpointRetriedEventHandler = vi.fn(); rootMessenger.subscribe( 'NetworkController:rpcEndpointRetried', rpcEndpointRetriedEventHandler, @@ -383,7 +384,7 @@ describe('createNetworkClient - RPC endpoint events', () => { connectivityStatus: CONNECTIVITY_STATUSES.Offline, }); - const rpcEndpointUnavailableEventHandler = jest.fn(); + const rpcEndpointUnavailableEventHandler = vi.fn(); rootMessenger.subscribe( 'NetworkController:rpcEndpointUnavailable', rpcEndpointUnavailableEventHandler, @@ -408,7 +409,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -456,7 +457,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }, async (failoverComms) => { const messenger = buildRootMessenger(); - const rpcEndpointChainDegradedEventHandler = jest.fn(); + const rpcEndpointChainDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainDegraded', rpcEndpointChainDegradedEventHandler, @@ -508,7 +509,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -569,7 +570,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }, async (failoverComms) => { const messenger = buildRootMessenger(); - const rpcEndpointChainDegradedEventHandler = jest.fn(); + const rpcEndpointChainDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainDegraded', rpcEndpointChainDegradedEventHandler, @@ -611,7 +612,7 @@ describe('createNetworkClient - RPC endpoint events', () => { params: [], }, response: () => { - jest.advanceTimersByTime( + vi.advanceTimersByTime( DEFAULT_DEGRADED_THRESHOLD + 1, ); return { @@ -622,7 +623,7 @@ describe('createNetworkClient - RPC endpoint events', () => { failoverComms.mockRpcCall({ request, response: () => { - jest.advanceTimersByTime( + vi.advanceTimersByTime( DEFAULT_DEGRADED_THRESHOLD + 1, ); return { @@ -636,7 +637,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -694,7 +695,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }, async (failoverComms) => { const messenger = buildRootMessenger(); - const rpcEndpointDegradedEventHandler = jest.fn(); + const rpcEndpointDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointDegraded', rpcEndpointDegradedEventHandler, @@ -746,7 +747,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -837,7 +838,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }, async (failoverComms) => { const messenger = buildRootMessenger(); - const rpcEndpointDegradedEventHandler = jest.fn(); + const rpcEndpointDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointDegraded', rpcEndpointDegradedEventHandler, @@ -879,7 +880,7 @@ describe('createNetworkClient - RPC endpoint events', () => { params: [], }, response: () => { - jest.advanceTimersByTime( + vi.advanceTimersByTime( DEFAULT_DEGRADED_THRESHOLD + 1, ); return { @@ -890,7 +891,7 @@ describe('createNetworkClient - RPC endpoint events', () => { failoverComms.mockRpcCall({ request, response: () => { - jest.advanceTimersByTime( + vi.advanceTimersByTime( DEFAULT_DEGRADED_THRESHOLD + 1, ); return { @@ -904,7 +905,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -1032,7 +1033,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }); const messenger = buildRootMessenger(); - const rpcEndpointChainAvailableEventHandler = jest.fn(); + const rpcEndpointChainAvailableEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainAvailable', rpcEndpointChainAvailableEventHandler, @@ -1060,7 +1061,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -1122,7 +1123,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }); const messenger = buildRootMessenger(); - const rpcEndpointChainDegradedEventHandler = jest.fn(); + const rpcEndpointChainDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainDegraded', rpcEndpointChainDegradedEventHandler, @@ -1148,7 +1149,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -1197,7 +1198,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType }, async (comms) => { const messenger = buildRootMessenger(); - const rpcEndpointChainDegradedEventHandler = jest.fn(); + const rpcEndpointChainDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainDegraded', rpcEndpointChainDegradedEventHandler, @@ -1227,7 +1228,7 @@ describe('createNetworkClient - RPC endpoint events', () => { params: [], }, response: () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { result: '0x1', }; @@ -1236,7 +1237,7 @@ describe('createNetworkClient - RPC endpoint events', () => { comms.mockRpcCall({ request, response: () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { result: 'ok', }; @@ -1293,7 +1294,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }); const messenger = buildRootMessenger(); - const rpcEndpointDegradedEventHandler = jest.fn(); + const rpcEndpointDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointDegraded', rpcEndpointDegradedEventHandler, @@ -1319,7 +1320,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -1406,7 +1407,7 @@ describe('createNetworkClient - RPC endpoint events', () => { connectivityStatus: CONNECTIVITY_STATUSES.Offline, }); - const rpcEndpointRetriedEventHandler = jest.fn(); + const rpcEndpointRetriedEventHandler = vi.fn(); rootMessenger.subscribe( 'NetworkController:rpcEndpointRetried', rpcEndpointRetriedEventHandler, @@ -1466,7 +1467,7 @@ describe('createNetworkClient - RPC endpoint events', () => { connectivityStatus: CONNECTIVITY_STATUSES.Offline, }); - const rpcEndpointDegradedEventHandler = jest.fn(); + const rpcEndpointDegradedEventHandler = vi.fn(); rootMessenger.subscribe( 'NetworkController:rpcEndpointDegraded', rpcEndpointDegradedEventHandler, @@ -1489,7 +1490,7 @@ describe('createNetworkClient - RPC endpoint events', () => { () => { // Ensure that we advance to the next RPC request // retry, not the next block tracker request. - jest.advanceTimersByTime(backoffDuration); + vi.advanceTimersByTime(backoffDuration); }, ); @@ -1526,7 +1527,7 @@ describe('createNetworkClient - RPC endpoint events', () => { { providerType: networkClientType }, async (comms) => { const messenger = buildRootMessenger(); - const rpcEndpointDegradedEventHandler = jest.fn(); + const rpcEndpointDegradedEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointDegraded', rpcEndpointDegradedEventHandler, @@ -1559,7 +1560,7 @@ describe('createNetworkClient - RPC endpoint events', () => { params: [], }, response: () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { result: '0x1', }; @@ -1568,7 +1569,7 @@ describe('createNetworkClient - RPC endpoint events', () => { comms.mockRpcCall({ request, response: () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { result: 'ok', }; @@ -1631,7 +1632,7 @@ describe('createNetworkClient - RPC endpoint events', () => { }); const messenger = buildRootMessenger(); - const rpcEndpointChainAvailableEventHandler = jest.fn(); + const rpcEndpointChainAvailableEventHandler = vi.fn(); messenger.subscribe( 'NetworkController:rpcEndpointChainAvailable', rpcEndpointChainAvailableEventHandler, diff --git a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts index fa6f9d5e0c0..9d99d1e340f 100644 --- a/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts +++ b/packages/network-controller/src/create-network-client-tests/rpc-endpoint-failover.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { buildRootMessenger } from '../../tests/helpers.js'; import { withMockedCommunications, diff --git a/packages/network-controller/src/rpc-service-analytics.test.ts b/packages/network-controller/src/rpc-service-analytics.test.ts index 381bd6217d0..11846ec00db 100644 --- a/packages/network-controller/src/rpc-service-analytics.test.ts +++ b/packages/network-controller/src/rpc-service-analytics.test.ts @@ -1,4 +1,5 @@ import type { Hex } from '@metamask/utils'; +import { describe, expect, it } from 'vitest'; import { buildRpcServiceDegradedAnalyticsTrackingEvent, diff --git a/packages/network-controller/src/rpc-service/rpc-service-chain.test.ts b/packages/network-controller/src/rpc-service/rpc-service-chain.test.ts index 702cf7f3f2e..95ab93c68c7 100644 --- a/packages/network-controller/src/rpc-service/rpc-service-chain.test.ts +++ b/packages/network-controller/src/rpc-service/rpc-service-chain.test.ts @@ -5,6 +5,7 @@ import { } from '@metamask/controller-utils'; import { errorCodes } from '@metamask/rpc-errors'; import nock from 'nock'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { RpcServiceChain } from './rpc-service-chain.js'; import { @@ -39,11 +40,11 @@ const DEFAULT_RPC_CHAIN_ATTEMPTS_UNTIL_BREAK = describe('RpcServiceChain', () => { beforeEach(() => { - jest.useFakeTimers({ doNotFake: ['nextTick', 'queueMicrotask'] }); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); describe('onServiceRetry', () => { @@ -264,7 +265,7 @@ describe('RpcServiceChain', () => { }, ]); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -378,7 +379,7 @@ describe('RpcServiceChain', () => { }, ]); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -442,9 +443,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); @@ -534,8 +535,8 @@ describe('RpcServiceChain', () => { endpointUrl: tertiaryEndpointUrl, }, ]); - const onServiceRetryListener = jest.fn(() => { - jest.advanceTimersToNextTimer(); + const onServiceRetryListener = vi.fn(() => { + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceRetry(onServiceRetryListener); @@ -574,6 +575,7 @@ describe('RpcServiceChain', () => { expect(onServiceRetryListener).toHaveBeenNthCalledWith(attempt + 1, { primaryEndpointUrl: `${primaryEndpointUrl}/`, endpointUrl: + // eslint-disable-next-line vitest/no-conditional-in-test attempt >= 12 ? `${secondaryEndpointUrl}/` : `${primaryEndpointUrl}/`, @@ -618,9 +620,9 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); @@ -684,9 +686,9 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); @@ -724,7 +726,7 @@ describe('RpcServiceChain', () => { expect(onBreakListener).toHaveBeenCalledTimes(1); expect(onBreakListener).toHaveBeenCalledWith({ - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); }); @@ -785,10 +787,10 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onBreakListener = jest.fn(); - const onAvailableListener = jest.fn(); + const onBreakListener = vi.fn(); + const onAvailableListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); rpcServiceChain.onAvailable(onAvailableListener); @@ -808,7 +810,7 @@ describe('RpcServiceChain', () => { } // Wait until the circuit break duration passes, try the first endpoint // and see that it succeeds. - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); await rpcServiceChain.request(jsonRpcRequest); // Do it again: retry the first endpoint until its circuit breaks, then // retry the second endpoint until *its* circuit breaks. @@ -820,10 +822,10 @@ describe('RpcServiceChain', () => { expect(onBreakListener).toHaveBeenCalledTimes(2); expect(onBreakListener).toHaveBeenNthCalledWith(1, { - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); expect(onBreakListener).toHaveBeenNthCalledWith(2, { - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); }); @@ -847,7 +849,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -887,9 +889,9 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); @@ -908,7 +910,7 @@ describe('RpcServiceChain', () => { } // Wait until the circuit break duration passes, try the first endpoint // and see that it succeeds. - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); await rpcServiceChain.request(jsonRpcRequest); // Do it again: retry the first endpoint until its circuit breaks, then // retry the second endpoint until *its* circuit breaks. @@ -920,10 +922,10 @@ describe('RpcServiceChain', () => { expect(onBreakListener).toHaveBeenCalledTimes(2); expect(onBreakListener).toHaveBeenNthCalledWith(1, { - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); expect(onBreakListener).toHaveBeenNthCalledWith(2, { - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); }); @@ -979,9 +981,9 @@ describe('RpcServiceChain', () => { endpointUrl: tertiaryEndpointUrl, }, ]); - const onServiceBreakListener = jest.fn(); + const onServiceBreakListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceBreak(onServiceBreakListener); @@ -1029,17 +1031,17 @@ describe('RpcServiceChain', () => { expect(onServiceBreakListener).toHaveBeenNthCalledWith(1, { primaryEndpointUrl: `${primaryEndpointUrl}/`, endpointUrl: `${primaryEndpointUrl}/`, - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); expect(onServiceBreakListener).toHaveBeenNthCalledWith(2, { primaryEndpointUrl: `${primaryEndpointUrl}/`, endpointUrl: `${secondaryEndpointUrl}/`, - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); expect(onServiceBreakListener).toHaveBeenNthCalledWith(3, { primaryEndpointUrl: `${primaryEndpointUrl}/`, endpointUrl: `${tertiaryEndpointUrl}/`, - error: new Error("Fetch failed with status '503'"), + error: new HttpError(503), }); }); @@ -1064,9 +1066,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); @@ -1110,7 +1112,7 @@ describe('RpcServiceChain', () => { }) .times(2) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1125,9 +1127,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); @@ -1167,7 +1169,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1193,9 +1195,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); @@ -1246,7 +1248,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 2, jsonrpc: '2.0', @@ -1263,9 +1265,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); @@ -1315,7 +1317,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1338,10 +1340,10 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onBreakListener = jest.fn(); - const onDegradedListener = jest.fn(); + const onBreakListener = vi.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); rpcServiceChain.onDegraded(onDegradedListener); @@ -1393,7 +1395,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1410,9 +1412,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); @@ -1437,7 +1439,7 @@ describe('RpcServiceChain', () => { ); // Wait until the circuit break duration passes, try the endpoint again, // and see that it succeeds, but slowly. - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); await rpcServiceChain.request(jsonRpcRequest); expect(onDegradedListener).toHaveBeenCalledTimes(2); @@ -1474,7 +1476,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1506,9 +1508,9 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); @@ -1541,7 +1543,7 @@ describe('RpcServiceChain', () => { await expect(rpcServiceChain.request(jsonRpcRequest)).rejects.toThrow( expectedError, ); - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); // Hit the first endpoint again, and see that it succeeds, but slowly await rpcServiceChain.request(jsonRpcRequest); @@ -1580,9 +1582,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onServiceDegradedListener = jest.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); @@ -1636,7 +1638,7 @@ describe('RpcServiceChain', () => { }) .times(2) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1651,9 +1653,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onServiceDegradedListener = jest.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); @@ -1702,7 +1704,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1728,9 +1730,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onServiceDegradedListener = jest.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); @@ -1797,7 +1799,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1820,10 +1822,10 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onBreakListener = jest.fn(); - const onServiceDegradedListener = jest.fn(); + const onBreakListener = vi.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onBreak(onBreakListener); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); @@ -1892,7 +1894,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1909,9 +1911,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onServiceDegradedListener = jest.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); @@ -1936,7 +1938,7 @@ describe('RpcServiceChain', () => { ); // Wait until the circuit break duration passes, try the endpoint again, // and see that it succeeds, but slowly. - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); await rpcServiceChain.request(jsonRpcRequest); expect(onServiceDegradedListener).toHaveBeenCalledTimes(3); @@ -1985,7 +1987,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -2017,9 +2019,9 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onServiceDegradedListener = jest.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); @@ -2052,7 +2054,7 @@ describe('RpcServiceChain', () => { await expect(rpcServiceChain.request(jsonRpcRequest)).rejects.toThrow( expectedError, ); - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); // Hit the first endpoint again, and see that it succeeds, but slowly await rpcServiceChain.request(jsonRpcRequest); @@ -2110,7 +2112,7 @@ describe('RpcServiceChain', () => { .reply( 200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -2127,7 +2129,7 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); rpcServiceChain.onDegraded(onDegradedListener); await rpcServiceChain.request({ @@ -2157,7 +2159,7 @@ describe('RpcServiceChain', () => { .reply( 200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -2174,7 +2176,7 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onServiceDegradedListener = jest.fn(); + const onServiceDegradedListener = vi.fn(); rpcServiceChain.onServiceDegraded(onServiceDegradedListener); await rpcServiceChain.request({ @@ -2217,9 +2219,9 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onAvailable(onAvailableListener); @@ -2276,9 +2278,9 @@ describe('RpcServiceChain', () => { endpointUrl: secondaryEndpointUrl, }, ]); - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onAvailable(onAvailableListener); @@ -2315,7 +2317,7 @@ describe('RpcServiceChain', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -2342,10 +2344,10 @@ describe('RpcServiceChain', () => { endpointUrl, }, ]); - const onDegradedListener = jest.fn(); - const onAvailableListener = jest.fn(); + const onDegradedListener = vi.fn(); + const onAvailableListener = vi.fn(); rpcServiceChain.onServiceRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); rpcServiceChain.onDegraded(onDegradedListener); rpcServiceChain.onAvailable(onAvailableListener); diff --git a/packages/network-controller/src/rpc-service/rpc-service.test.ts b/packages/network-controller/src/rpc-service/rpc-service.test.ts index 2e98f9c8094..1b54e1dd20a 100644 --- a/packages/network-controller/src/rpc-service/rpc-service.test.ts +++ b/packages/network-controller/src/rpc-service/rpc-service.test.ts @@ -8,6 +8,7 @@ import { CircuitState } from 'cockatiel'; import deepFreeze from 'deep-freeze-strict'; import nock from 'nock'; import { FetchError } from 'node-fetch'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { CUSTOM_RPC_ERRORS, @@ -15,13 +16,19 @@ import { RpcService, } from './rpc-service.js'; +/** + * An error class, as `toThrow` accepts one. Stands in for `jest.Constructable`, + * which Vitest does not re-export. + */ +type Constructable = new (...args: never[]) => unknown; + describe('RpcService', () => { beforeEach(() => { - jest.useFakeTimers({ doNotFake: ['nextTick', 'queueMicrotask'] }); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); describe('resetPolicy', () => { @@ -55,7 +62,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -106,7 +113,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -165,7 +172,7 @@ describe('RpcService', () => { jsonrpc: '2.0', result: 'ok', }); - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -173,7 +180,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onAvailable(onAvailableListener); @@ -227,7 +234,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -268,7 +275,7 @@ describe('RpcService', () => { params: [], }) .reply(500); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -276,7 +283,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -324,7 +331,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); expect(service.getCircuitState()).toBe(CircuitState.Closed); @@ -336,7 +343,7 @@ describe('RpcService', () => { await ignoreRejection(service.request(jsonRpcRequest)); expect(service.getCircuitState()).toBe(CircuitState.Open); - jest.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); + vi.advanceTimersByTime(DEFAULT_CIRCUIT_BREAK_DURATION); const promise = ignoreRejection(service.request(jsonRpcRequest)); expect(service.getCircuitState()).toBe(CircuitState.HalfOpen); await promise; @@ -679,7 +686,7 @@ describe('RpcService', () => { describe('when offline', () => { it('does not retry when offline, only makes one fetch call', async () => { const expectedError = new TypeError('Failed to fetch'); - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw expectedError; }); const service = new RpcService({ @@ -689,7 +696,7 @@ describe('RpcService', () => { isOffline: (): boolean => true, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -707,11 +714,11 @@ describe('RpcService', () => { it('does not call onDegraded when offline', async () => { const expectedError = new TypeError('Failed to fetch'); - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw expectedError; }); const endpointUrl = 'https://rpc.example.chain'; - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch: mockFetch, btoa, @@ -719,7 +726,7 @@ describe('RpcService', () => { isOffline: (): boolean => true, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onDegraded(onDegradedListener); @@ -739,11 +746,11 @@ describe('RpcService', () => { it('does not call onBreak when offline', async () => { const expectedError = new TypeError('Failed to fetch'); - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw expectedError; }); const endpointUrl = 'https://rpc.example.chain'; - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = new RpcService({ fetch: mockFetch, btoa, @@ -751,7 +758,7 @@ describe('RpcService', () => { isOffline: (): boolean => true, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -1086,14 +1093,14 @@ describe('RpcService', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', result: '0x1', }; }); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1128,7 +1135,7 @@ describe('RpcService', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1143,14 +1150,14 @@ describe('RpcService', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 2, jsonrpc: '2.0', result: '0x100', }; }); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1201,7 +1208,7 @@ describe('RpcService', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1218,7 +1225,7 @@ describe('RpcService', () => { }) .times(5) .reply(503); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1226,7 +1233,7 @@ describe('RpcService', () => { isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onDegraded(onDegradedListener); @@ -1273,7 +1280,7 @@ describe('RpcService', () => { .reply( 200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1282,7 +1289,7 @@ describe('RpcService', () => { }, { 'X-Trace-Id': 'abc-123-trace' }, ); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1323,7 +1330,7 @@ describe('RpcService', () => { result: '0x1', }; }); - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1355,7 +1362,7 @@ describe('RpcService', () => { params: [], }) .reply(200, () => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return { id: 1, jsonrpc: '2.0', @@ -1375,7 +1382,7 @@ describe('RpcService', () => { result: '0x1', }; }); - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1456,13 +1463,13 @@ function testsForNonRetriableErrors({ }) => void; createService?: (args: { endpointUrl: string; - expectedError: string | RegExp | Error | jest.Constructable | undefined; + expectedError: string | RegExp | Error | Constructable | undefined; }) => RpcService; endpointUrl?: string; rpcMethod?: string; - expectedError: string | RegExp | Error | jest.Constructable | undefined; + expectedError: string | RegExp | Error | Constructable | undefined; }): void { - /* eslint-disable jest/require-top-level-describe */ + /* eslint-disable vitest/require-top-level-describe */ it('re-throws the error without retrying the request', async () => { beforeCreateService({ endpointUrl, rpcMethod }); @@ -1480,7 +1487,7 @@ function testsForNonRetriableErrors({ it('does not call onRetry', async () => { beforeCreateService({ endpointUrl, rpcMethod }); - const onRetryListener = jest.fn(); + const onRetryListener = vi.fn(); const service = createService({ endpointUrl, expectedError }); service.onRetry(onRetryListener); @@ -1497,7 +1504,7 @@ function testsForNonRetriableErrors({ it('does not call onBreak', async () => { beforeCreateService({ endpointUrl, rpcMethod }); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = createService({ endpointUrl, expectedError }); service.onBreak(onBreakListener); @@ -1514,7 +1521,7 @@ function testsForNonRetriableErrors({ it('does not call onDegraded', async () => { beforeCreateService({ endpointUrl, rpcMethod }); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = createService({ endpointUrl, expectedError }); service.onDegraded(onDegradedListener); @@ -1531,7 +1538,7 @@ function testsForNonRetriableErrors({ it('does not call onAvailable', async () => { beforeCreateService({ endpointUrl, rpcMethod }); - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); const service = createService({ endpointUrl, expectedError }); service.onAvailable(onAvailableListener); @@ -1546,7 +1553,7 @@ function testsForNonRetriableErrors({ expect(onAvailableListener).not.toHaveBeenCalled(); }); - /* eslint-enable jest/require-top-level-describe */ + /* eslint-enable vitest/require-top-level-describe */ } /** @@ -1563,14 +1570,14 @@ function testsForRetriableFetchErrors({ expectedError, }: { producedError: Error; - expectedError: string | jest.Constructable | RegExp | Error; + expectedError: string | Constructable | RegExp | Error; }): void { // This function is designed to be used inside of a describe, so this won't be // a problem in practice. - /* eslint-disable jest/require-top-level-describe */ + /* eslint-disable vitest/require-top-level-describe */ it('retries a constantly failing request up to 4 more times before re-throwing the error, if `request` is only called once', async () => { - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw producedError; }); const service = new RpcService({ @@ -1580,7 +1587,7 @@ function testsForRetriableFetchErrors({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -1596,11 +1603,11 @@ function testsForRetriableFetchErrors({ }); it('calls the onDegraded callback once for each retry round', async () => { - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw producedError; }); const endpointUrl = 'https://rpc.example.chain'; - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch: mockFetch, btoa, @@ -1608,7 +1615,7 @@ function testsForRetriableFetchErrors({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onDegraded(onDegradedListener); @@ -1633,7 +1640,7 @@ function testsForRetriableFetchErrors({ }); it('still re-throws the error even after the circuit breaks', async () => { - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw producedError; }); const service = new RpcService({ @@ -1643,7 +1650,7 @@ function testsForRetriableFetchErrors({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -1661,11 +1668,11 @@ function testsForRetriableFetchErrors({ }); it('calls the onBreak callback once after the circuit breaks', async () => { - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw producedError; }); const endpointUrl = 'https://rpc.example.chain'; - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = new RpcService({ fetch: mockFetch, btoa, @@ -1673,7 +1680,7 @@ function testsForRetriableFetchErrors({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -1696,11 +1703,11 @@ function testsForRetriableFetchErrors({ }); it('throws an error that includes the number of minutes until the circuit is re-closed if a request is attempted while the circuit is open', async () => { - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw producedError; }); const endpointUrl = 'https://rpc.example.chain'; - const logger = { warn: jest.fn() }; + const logger = { warn: vi.fn() }; const service = new RpcService({ fetch: mockFetch, btoa, @@ -1709,7 +1716,7 @@ function testsForRetriableFetchErrors({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -1725,7 +1732,7 @@ function testsForRetriableFetchErrors({ await ignoreRejection(service.request(jsonRpcRequest)); // Advance a minute to test that the message updates dynamically as time passes - jest.advanceTimersByTime(60000); + vi.advanceTimersByTime(60000); await expect(service.request(jsonRpcRequest)).rejects.toThrow( expect.objectContaining({ code: errorCodes.rpc.resourceUnavailable, @@ -1736,11 +1743,11 @@ function testsForRetriableFetchErrors({ }); it('logs the original CircuitBreakError if a request is attempted while the circuit is open', async () => { - const mockFetch = jest.fn(() => { + const mockFetch = vi.fn(() => { throw producedError; }); const endpointUrl = 'https://rpc.example.chain'; - const logger = { warn: jest.fn() }; + const logger = { warn: vi.fn() }; const service = new RpcService({ fetch: mockFetch, btoa, @@ -1749,7 +1756,7 @@ function testsForRetriableFetchErrors({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -1772,8 +1779,9 @@ function testsForRetriableFetchErrors({ it('calls the onAvailable callback if the endpoint becomes degraded via errors and then recovers', async () => { let invocationIndex = -1; - const mockFetch = jest.fn(async () => { + const mockFetch = vi.fn(async () => { invocationIndex += 1; + // eslint-disable-next-line vitest/no-conditional-in-test if (invocationIndex === DEFAULT_MAX_RETRIES + 1) { // Only used for testing. // eslint-disable-next-line no-restricted-globals @@ -1788,7 +1796,7 @@ function testsForRetriableFetchErrors({ throw producedError; }); const endpointUrl = 'https://rpc.example.chain'; - const onAvailableListener = jest.fn(); + const onAvailableListener = vi.fn(); const service = new RpcService({ fetch: mockFetch, btoa, @@ -1797,7 +1805,7 @@ function testsForRetriableFetchErrors({ }); service.onAvailable(onAvailableListener); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -1813,7 +1821,7 @@ function testsForRetriableFetchErrors({ expect(onAvailableListener).toHaveBeenCalledTimes(1); }); - /* eslint-enable jest/require-top-level-describe */ + /* eslint-enable vitest/require-top-level-describe */ } /** @@ -1836,12 +1844,12 @@ function testsForRetriableResponses({ }: { httpStatus: number; responseBody?: string; - expectedError: string | jest.Constructable | RegExp | Error; - expectedOnBreakError?: string | jest.Constructable | RegExp | Error; + expectedError: string | Constructable | RegExp | Error; + expectedOnBreakError?: string | Constructable | RegExp | Error; }): void { // This function is designed to be used inside of a describe, so this won't be // a problem in practice. - /* eslint-disable jest/require-top-level-describe,jest/no-identical-title */ + /* eslint-disable vitest/require-top-level-describe,vitest/no-identical-title */ it('retries a constantly failing request up to 4 more times before re-throwing the error, if `request` is only called once', async () => { const scope = nock('https://rpc.example.chain') @@ -1860,7 +1868,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -1886,7 +1894,7 @@ function testsForRetriableResponses({ .times(10) .reply(httpStatus, responseBody); const endpointUrl = 'https://rpc.example.chain'; - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1894,7 +1902,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onDegraded(onDegradedListener); @@ -1929,7 +1937,7 @@ function testsForRetriableResponses({ .times(10) .reply(httpStatus, responseBody, { 'X-Trace-Id': 'abc-123-trace' }); const endpointUrl = 'https://rpc.example.chain'; - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1937,7 +1945,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onDegraded(onDegradedListener); @@ -1984,7 +1992,7 @@ function testsForRetriableResponses({ params: [], }) .replyWithError('Connection refused'); - const onDegradedListener = jest.fn(); + const onDegradedListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -1992,7 +2000,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onDegraded(onDegradedListener); @@ -2034,7 +2042,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -2062,7 +2070,7 @@ function testsForRetriableResponses({ }) .times(15) .reply(httpStatus, responseBody); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -2070,7 +2078,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -2103,7 +2111,7 @@ function testsForRetriableResponses({ }) .times(15) .reply(httpStatus, responseBody); - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -2111,7 +2119,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -2128,7 +2136,7 @@ function testsForRetriableResponses({ await ignoreRejection(service.request(jsonRpcRequest)); // Advance a minute to test that the message updates dynamically as time passes - jest.advanceTimersByTime(60000); + vi.advanceTimersByTime(60000); await expect(service.request(jsonRpcRequest)).rejects.toThrow( expect.objectContaining({ code: errorCodes.rpc.resourceUnavailable, @@ -2149,8 +2157,8 @@ function testsForRetriableResponses({ }) .times(15) .reply(httpStatus, responseBody); - const logger = { warn: jest.fn() }; - const onBreakListener = jest.fn(); + const logger = { warn: vi.fn() }; + const onBreakListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -2159,7 +2167,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => false, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -2198,7 +2206,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => true, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const jsonRpcRequest = { @@ -2225,7 +2233,7 @@ function testsForRetriableResponses({ .times(3) .reply(httpStatus, responseBody); const endpointUrl = 'https://rpc.example.chain'; - const onBreakListener = jest.fn(); + const onBreakListener = vi.fn(); const service = new RpcService({ fetch, btoa, @@ -2233,7 +2241,7 @@ function testsForRetriableResponses({ isOffline: (): boolean => true, }); service.onRetry(() => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); service.onBreak(onBreakListener); @@ -2261,5 +2269,5 @@ function testsForRetriableResponses({ expect(scope.isDone()).toBe(true); }); - /* eslint-enable jest/require-top-level-describe,jest/no-identical-title */ + /* eslint-enable vitest/require-top-level-describe,vitest/no-identical-title */ } diff --git a/packages/network-controller/src/selectors.test.ts b/packages/network-controller/src/selectors.test.ts index 0fed844f57e..ecb5522d696 100644 --- a/packages/network-controller/src/selectors.test.ts +++ b/packages/network-controller/src/selectors.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { getRpcFailoverMode } from './selectors.js'; /** diff --git a/packages/network-controller/tests/NetworkController.analytics.test.ts b/packages/network-controller/tests/NetworkController.analytics.test.ts index 017d0a861e6..77b10c15ddb 100644 --- a/packages/network-controller/tests/NetworkController.analytics.test.ts +++ b/packages/network-controller/tests/NetworkController.analytics.test.ts @@ -1,4 +1,6 @@ import type { Hex } from '@metamask/utils'; +import { describe, expect, it, vi } from 'vitest'; +import type { Mock } from 'vitest'; import { NetworkController } from '../src/index.js'; import type { NetworkControllerAnalyticsOptions } from '../src/rpc-service-analytics.js'; @@ -59,9 +61,9 @@ function buildController({ networkControllerMessenger: ReturnType< typeof buildNetworkControllerMessenger >; - trackEvent: jest.Mock; + trackEvent: Mock; } { - const trackEvent = jest.fn(); + const trackEvent = vi.fn(); const rootMessenger = buildRootMessenger({ trackEvent, ...(analyticsId === undefined ? {} : { analyticsId }), @@ -215,7 +217,7 @@ describe('NetworkController analytics', () => { rpcServiceEventsSampleRate: 1, }, }); - const captureExceptionSpy = jest.spyOn(rootMessenger, 'captureException'); + const captureExceptionSpy = vi.spyOn(rootMessenger, 'captureException'); expect(() => { networkControllerMessenger.publish( diff --git a/packages/network-controller/tests/NetworkController.provider.test.ts b/packages/network-controller/tests/NetworkController.provider.test.ts index 8456454907c..044de43553f 100644 --- a/packages/network-controller/tests/NetworkController.provider.test.ts +++ b/packages/network-controller/tests/NetworkController.provider.test.ts @@ -4,6 +4,7 @@ import { } from '@metamask/controller-utils'; import { Duration, inMilliseconds } from '@metamask/utils'; import nock from 'nock'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { NetworkStatus } from '../src/constants.js'; import { @@ -16,11 +17,11 @@ import { describe('NetworkController provider tests', () => { beforeEach(() => { - jest.useFakeTimers({ doNotFake: ['nextTick', 'queueMicrotask'] }); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it('sets the status of a network client to "available" the first time its (sole) RPC endpoint returns a 2xx response', async () => { @@ -107,7 +108,7 @@ describe('NetworkController provider tests', () => { params: [], }) .reply(() => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return [ 200, { @@ -124,7 +125,7 @@ describe('NetworkController provider tests', () => { params: [], }) .reply(() => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return [ 200, { @@ -219,7 +220,7 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const { provider } = controller.getNetworkClientById(networkClientId); @@ -303,9 +304,9 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -403,9 +404,9 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -458,7 +459,7 @@ describe('NetworkController provider tests', () => { params: [], }) .reply(() => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return [ 200, { @@ -475,7 +476,7 @@ describe('NetworkController provider tests', () => { params: [], }) .reply(() => { - jest.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); + vi.advanceTimersByTime(DEFAULT_DEGRADED_THRESHOLD + 1); return [ 200, { @@ -510,9 +511,9 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -590,7 +591,7 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const { provider } = controller.getNetworkClientById(networkClientId); const request = { @@ -699,7 +700,7 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); const { provider } = controller.getNetworkClientById(networkClientId); const request = { @@ -789,9 +790,9 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -813,7 +814,7 @@ describe('NetworkController provider tests', () => { await expect(provider.request(request)).rejects.toThrow(expectedError); // Wait until the circuit break duration passes and hit the endpoint // again. - jest.advanceTimersByTime(inMilliseconds(30, Duration.Second)); + vi.advanceTimersByTime(inMilliseconds(30, Duration.Second)); await provider.request(request); expect(stateChangeListener).toHaveBeenCalledTimes(3); @@ -918,9 +919,9 @@ describe('NetworkController provider tests', () => { }, async ({ controller, messenger }) => { messenger.subscribe('NetworkController:rpcEndpointRetried', () => { - jest.advanceTimersToNextTimer(); + vi.advanceTimersToNextTimer(); }); - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -938,7 +939,7 @@ describe('NetworkController provider tests', () => { await provider.request(request); // Wait for the block tracker to reset the cache. (For some reason, // multiple timers exist.) - jest.runAllTimers(); + vi.runAllTimers(); // Hit the endpoint, run out of retries. await expect(provider.request(request)).rejects.toThrow(expectedError); // Hit the endpoint, run out of retries. diff --git a/packages/network-controller/tests/NetworkController.test.ts b/packages/network-controller/tests/NetworkController.test.ts index 70542902662..594d9c4f3d3 100644 --- a/packages/network-controller/tests/NetworkController.test.ts +++ b/packages/network-controller/tests/NetworkController.test.ts @@ -17,9 +17,10 @@ import { rpcErrors } from '@metamask/rpc-errors'; import type { Hex } from '@metamask/utils'; import assert from 'assert'; import type { Patch } from 'immer'; -import { when, resetAllWhenMocks, WhenMock } from 'jest-when'; import { inspect, isDeepStrictEqual, promisify } from 'util'; import { v4 as uuidV4 } from 'uuid'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import type { MockedFunction } from 'vitest'; import { NetworkStatus } from '../src/constants.js'; import * as createAutoManagedNetworkClientModule from '../src/create-auto-managed-network-client.js'; @@ -68,14 +69,14 @@ import { } from './helpers.js'; import type { RootMessenger } from './helpers.js'; -jest.mock('../src/create-network-client'); +vi.mock('../src/create-network-client.js'); -jest.mock('uuid', () => { - const actual = jest.requireActual('uuid'); +vi.mock('uuid', async () => { + const actual = await vi.importActual('uuid'); return { ...actual, - v4: jest.fn(), + v4: vi.fn(), }; }); @@ -90,10 +91,10 @@ type Block = { baseFeePerGas?: string; }; -const createNetworkClientMock = jest.mocked(createNetworkClient); +const createNetworkClientMock = vi.mocked(createNetworkClient); // `v4` is overloaded; naming the signature used here avoids resolving to the // last overload, which returns a `Uint8Array`. -const uuidV4Mock = jest.mocked<() => string>(uuidV4); +const uuidV4Mock = vi.mocked<() => string>(uuidV4); /** * A dummy block that matches the pre-EIP-1559 format (i.e. it doesn't have the @@ -146,6 +147,8 @@ const GENERIC_JSON_RPC_ERROR = rpcErrors.internal( */ const FAKE_DATE_NOW_MS = 1732114339518; +/* eslint-disable vitest/no-conditional-in-test */ + describe('NetworkController', () => { let uuidCounter = 0; @@ -158,11 +161,7 @@ describe('NetworkController', () => { createNetworkClientMock.mockReturnValue(buildFakeClient()); - jest.spyOn(Date, 'now').mockReturnValue(FAKE_DATE_NOW_MS); - }); - - afterEach(() => { - resetAllWhenMocks(); + vi.spyOn(Date, 'now').mockReturnValue(FAKE_DATE_NOW_MS); }); describe('constructor', () => { @@ -378,7 +377,7 @@ describe('NetworkController', () => { it('corrects an invalid selectedNetworkClientId to the default RPC endpoint of the first chain, logging this fact', () => { const messenger = buildRootMessenger(); - const captureExceptionSpy = jest.spyOn(messenger, 'captureException'); + const captureExceptionSpy = vi.spyOn(messenger, 'captureException'); const controllerMessenger = buildNetworkControllerMessenger(messenger); const controller = new NetworkController({ @@ -425,7 +424,7 @@ describe('NetworkController', () => { it('removes invalid network client IDs from networksMetadata, logging this fact', () => { const messenger = buildRootMessenger(); - const captureExceptionSpy = jest.spyOn(messenger, 'captureException'); + const captureExceptionSpy = vi.spyOn(messenger, 'captureException'); const controllerMessenger = buildNetworkControllerMessenger(messenger); const controller = new NetworkController({ @@ -814,18 +813,17 @@ describe('NetworkController', () => { createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; const autoManagedNetworkClients: AutoManagedNetworkClient[] = []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); + vi.spyOn( + createAutoManagedNetworkClientModule, + 'createAutoManagedNetworkClient', + ).mockImplementation((...args) => { + const autoManagedNetworkClient = originalCreateAutoManagedNetworkClient( + ...args, + ); + vi.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); + autoManagedNetworkClients.push(autoManagedNetworkClient); + return autoManagedNetworkClient; + }); await withController( { @@ -884,18 +882,17 @@ describe('NetworkController', () => { createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; const autoManagedNetworkClients: AutoManagedNetworkClient[] = []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); + vi.spyOn( + createAutoManagedNetworkClientModule, + 'createAutoManagedNetworkClient', + ).mockImplementation((...args) => { + const autoManagedNetworkClient = originalCreateAutoManagedNetworkClient( + ...args, + ); + vi.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); + autoManagedNetworkClients.push(autoManagedNetworkClient); + return autoManagedNetworkClient; + }); await withController( { @@ -943,18 +940,17 @@ describe('NetworkController', () => { createAutoManagedNetworkClientModule.createAutoManagedNetworkClient; const autoManagedNetworkClients: AutoManagedNetworkClient[] = []; - jest - .spyOn( - createAutoManagedNetworkClientModule, - 'createAutoManagedNetworkClient', - ) - .mockImplementation((...args) => { - const autoManagedNetworkClient = - originalCreateAutoManagedNetworkClient(...args); - jest.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); - autoManagedNetworkClients.push(autoManagedNetworkClient); - return autoManagedNetworkClient; - }); + vi.spyOn( + createAutoManagedNetworkClientModule, + 'createAutoManagedNetworkClient', + ).mockImplementation((...args) => { + const autoManagedNetworkClient = originalCreateAutoManagedNetworkClient( + ...args, + ); + vi.spyOn(autoManagedNetworkClient, 'setRpcFailoverMode'); + autoManagedNetworkClients.push(autoManagedNetworkClient); + return autoManagedNetworkClient; + }); await withController( { @@ -2160,7 +2156,7 @@ describe('NetworkController', () => { }, }, async ({ controller, messenger }) => { - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -2186,7 +2182,7 @@ describe('NetworkController', () => { }, }, async ({ controller, messenger }) => { - const infuraIsUnblockedListener = jest.fn(); + const infuraIsUnblockedListener = vi.fn(); messenger.subscribe( 'NetworkController:infuraIsUnblocked', infuraIsUnblockedListener, @@ -2212,7 +2208,7 @@ describe('NetworkController', () => { }, }, async ({ controller, messenger }) => { - const infuraIsBlockedListener = jest.fn(); + const infuraIsBlockedListener = vi.fn(); messenger.subscribe( 'NetworkController:infuraIsBlocked', infuraIsBlockedListener, @@ -2595,13 +2591,14 @@ describe('NetworkController', () => { const lookupNetworkPromise = controller.lookupNetwork(); const error = new Error('oops'); - jest - .spyOn(networkControllerMessenger, 'unsubscribe') - .mockImplementation((eventType) => { - if (eventType === 'NetworkController:networkDidChange') { - throw error; - } - }); + vi.spyOn( + networkControllerMessenger, + 'unsubscribe', + ).mockImplementation((eventType) => { + if (eventType === 'NetworkController:networkDidChange') { + throw error; + } + }); await expect(lookupNetworkPromise).rejects.toThrow(error); }, ); @@ -2642,7 +2639,7 @@ describe('NetworkController', () => { }, }, async ({ controller, messenger }) => { - const stateChangeListener = jest.fn(); + const stateChangeListener = vi.fn(); messenger.subscribe( 'NetworkController:stateChange', stateChangeListener, @@ -2674,7 +2671,7 @@ describe('NetworkController', () => { }, }, async ({ controller, messenger }) => { - const infuraIsUnblockedListener = jest.fn(); + const infuraIsUnblockedListener = vi.fn(); messenger.subscribe( 'NetworkController:infuraIsUnblocked', infuraIsUnblockedListener, @@ -2706,7 +2703,7 @@ describe('NetworkController', () => { }, }, async ({ controller, messenger }) => { - const infuraIsBlockedListener = jest.fn(); + const infuraIsBlockedListener = vi.fn(); messenger.subscribe( 'NetworkController:infuraIsBlocked', infuraIsBlockedListener, @@ -3107,13 +3104,14 @@ describe('NetworkController', () => { const lookupNetworkPromise = controller.lookupNetwork(); const error = new Error('oops'); - jest - .spyOn(networkControllerMessenger, 'unsubscribe') - .mockImplementation((eventType) => { - if (eventType === 'NetworkController:networkDidChange') { - throw error; - } - }); + vi.spyOn( + networkControllerMessenger, + 'unsubscribe', + ).mockImplementation((eventType) => { + if (eventType === 'NetworkController:networkDidChange') { + throw error; + } + }); await expect(lookupNetworkPromise).rejects.toThrow(error); }, ); @@ -3982,7 +3980,7 @@ describe('NetworkController', () => { ], ] as const) { // This is a string! - // eslint-disable-next-line jest/valid-title + // eslint-disable-next-line vitest/valid-title describe(name, () => { for (const infuraNetworkType of INFURA_NETWORKS) { const infuraChainId = ChainId[infuraNetworkType]; @@ -4095,7 +4093,7 @@ describe('NetworkController', () => { ], ] as const) { // This is a string! - // eslint-disable-next-line jest/valid-title + // eslint-disable-next-line vitest/valid-title describe(name, () => { for (const infuraNetworkType of INFURA_NETWORKS) { const infuraChainId = ChainId[infuraNetworkType]; @@ -4564,7 +4562,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('BBBB-BBBB-BBBB-BBBB') .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -4736,7 +4734,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('BBBB-BBBB-BBBB-BBBB') .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -4914,7 +4912,7 @@ describe('NetworkController', () => { }), }, ({ controller, messenger }) => { - const networkAddedEventListener = jest.fn(); + const networkAddedEventListener = vi.fn(); messenger.subscribe( 'NetworkController:networkAdded', networkAddedEventListener, @@ -5136,7 +5134,7 @@ describe('NetworkController', () => { uuidV4Mock.mockReturnValueOnce('AAAA-AAAA-AAAA-AAAA'); await withController(({ controller, messenger }) => { - const networkAddedEventListener = jest.fn(); + const networkAddedEventListener = vi.fn(); messenger.subscribe( 'NetworkController:networkAdded', networkAddedEventListener, @@ -5184,7 +5182,7 @@ describe('NetworkController', () => { uuidV4Mock.mockReturnValueOnce('AAAA-AAAA-AAAA-AAAA'); await withController(({ controller, messenger }) => { - const networkAddedEventListener = jest.fn(); + const networkAddedEventListener = vi.fn(); messenger.subscribe( 'NetworkController:networkAdded', networkAddedEventListener, @@ -5232,7 +5230,7 @@ describe('NetworkController', () => { uuidV4Mock.mockReturnValueOnce('AAAA-AAAA-AAAA-AAAA'); await withController(({ messenger }) => { - const networkAddedEventListener = jest.fn(); + const networkAddedEventListener = vi.fn(); messenger.subscribe( 'NetworkController:networkAdded', networkAddedEventListener, @@ -5991,7 +5989,7 @@ describe('NetworkController', () => { describe(`if the existing chain ID is the Infura-supported chain ${infuraChainId} and is not being changed`, () => { describe('when a new Infura RPC endpoint is being added', () => { it('creates and registers a new network client for the RPC endpoint', async () => { - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -6103,7 +6101,7 @@ describe('NetworkController', () => { }); it('overrides the endpoint failover URLs with the chain-level failoverUrls when the controller was initialized with them', async () => { - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -6312,7 +6310,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('AAAA-AAAA-AAAA-AAAA') .mockReturnValueOnce('BBBB-BBBB-BBBB-BBBB'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -6626,7 +6624,7 @@ describe('NetworkController', () => { const existingNetworkClient = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy = jest.spyOn(existingNetworkClient, 'destroy'); + const destroySpy = vi.spyOn(existingNetworkClient, 'destroy'); await controller.updateNetwork(infuraChainId, { ...networkConfigurationToUpdate, @@ -7282,7 +7280,7 @@ describe('NetworkController', () => { const existingNetworkClient = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy = jest.spyOn(existingNetworkClient, 'destroy'); + const destroySpy = vi.spyOn(existingNetworkClient, 'destroy'); await controller.updateNetwork(infuraChainId, { ...networkConfigurationToUpdate, @@ -7306,7 +7304,7 @@ describe('NetworkController', () => { it('creates and registers a network client for the new version of the RPC endpoint', async () => { uuidV4Mock.mockReturnValueOnce('BBBB-BBBB-BBBB-BBBB'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -8174,7 +8172,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('BBBB-BBBB-BBBB-BBBB') .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -8511,7 +8509,7 @@ describe('NetworkController', () => { const existingNetworkClient = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy = jest.spyOn(existingNetworkClient, 'destroy'); + const destroySpy = vi.spyOn(existingNetworkClient, 'destroy'); await controller.updateNetwork('0x1337', { ...networkConfigurationToUpdate, @@ -9149,7 +9147,7 @@ describe('NetworkController', () => { const existingNetworkClient = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy = jest.spyOn(existingNetworkClient, 'destroy'); + const destroySpy = vi.spyOn(existingNetworkClient, 'destroy'); await controller.updateNetwork('0x1337', { ...networkConfigurationToUpdate, @@ -9174,7 +9172,7 @@ describe('NetworkController', () => { it('creates and registers a network client for the new version of the RPC endpoint', async () => { uuidV4Mock.mockReturnValueOnce('BBBB-BBBB-BBBB-BBBB'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -10297,17 +10295,11 @@ describe('NetworkController', () => { const existingNetworkClient1 = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy1 = jest.spyOn( - existingNetworkClient1, - 'destroy', - ); + const destroySpy1 = vi.spyOn(existingNetworkClient1, 'destroy'); const existingNetworkClient2 = controller.getNetworkClientById( 'BBBB-BBBB-BBBB-BBBB', ); - const destroySpy2 = jest.spyOn( - existingNetworkClient2, - 'destroy', - ); + const destroySpy2 = vi.spyOn(existingNetworkClient2, 'destroy'); await controller.updateNetwork('0x1337', { ...networkConfigurationToUpdate, @@ -10332,7 +10324,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC') .mockReturnValueOnce('DDDD-DDDD-DDDD-DDDD'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -10962,17 +10954,11 @@ describe('NetworkController', () => { const existingNetworkClient1 = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy1 = jest.spyOn( - existingNetworkClient1, - 'destroy', - ); + const destroySpy1 = vi.spyOn(existingNetworkClient1, 'destroy'); const existingNetworkClient2 = controller.getNetworkClientById( 'BBBB-BBBB-BBBB-BBBB', ); - const destroySpy2 = jest.spyOn( - existingNetworkClient2, - 'destroy', - ); + const destroySpy2 = vi.spyOn(existingNetworkClient2, 'destroy'); await controller.updateNetwork( infuraChainId, @@ -11005,7 +10991,7 @@ describe('NetworkController', () => { .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC') .mockReturnValueOnce('DDDD-DDDD-DDDD-DDDD'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -11661,17 +11647,11 @@ describe('NetworkController', () => { const existingNetworkClient1 = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy1 = jest.spyOn( - existingNetworkClient1, - 'destroy', - ); + const destroySpy1 = vi.spyOn(existingNetworkClient1, 'destroy'); const existingNetworkClient2 = controller.getNetworkClientById( 'BBBB-BBBB-BBBB-BBBB', ); - const destroySpy2 = jest.spyOn( - existingNetworkClient2, - 'destroy', - ); + const destroySpy2 = vi.spyOn(existingNetworkClient2, 'destroy'); await controller.updateNetwork(infuraChainId, { ...networkConfigurationToUpdate, @@ -11703,7 +11683,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC') .mockReturnValueOnce('DDDD-DDDD-DDDD-DDDD'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -12390,11 +12370,11 @@ describe('NetworkController', () => { const existingNetworkClient1 = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy1 = jest.spyOn(existingNetworkClient1, 'destroy'); + const destroySpy1 = vi.spyOn(existingNetworkClient1, 'destroy'); const existingNetworkClient2 = controller.getNetworkClientById( 'BBBB-BBBB-BBBB-BBBB', ); - const destroySpy2 = jest.spyOn(existingNetworkClient2, 'destroy'); + const destroySpy2 = vi.spyOn(existingNetworkClient2, 'destroy'); await controller.updateNetwork('0x1337', { ...networkConfigurationToUpdate, @@ -12418,7 +12398,7 @@ describe('NetworkController', () => { uuidV4Mock .mockReturnValueOnce('CCCC-CCCC-CCCC-CCCC') .mockReturnValueOnce('DDDD-DDDD-DDDD-DDDD'); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -12908,7 +12888,7 @@ describe('NetworkController', () => { async ({ controller }) => { const existingNetworkClient = controller.getNetworkClientById(infuraNetworkType); - const destroySpy = jest.spyOn(existingNetworkClient, 'destroy'); + const destroySpy = vi.spyOn(existingNetworkClient, 'destroy'); await controller.updateNetwork( infuraChainId, @@ -12924,7 +12904,7 @@ describe('NetworkController', () => { const existingNetworkConfiguration = buildInfuraNetworkConfiguration(infuraNetworkType); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -13035,7 +13015,7 @@ describe('NetworkController', () => { const existingNetworkClient = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy = jest.spyOn(existingNetworkClient, 'destroy'); + const destroySpy = vi.spyOn(existingNetworkClient, 'destroy'); await controller.updateNetwork( '0x1337', @@ -13052,7 +13032,7 @@ describe('NetworkController', () => { chainId: '0x1337', }); - const createAutoManagedNetworkClientSpy = jest.spyOn( + const createAutoManagedNetworkClientSpy = vi.spyOn( createAutoManagedNetworkClientModule, 'createAutoManagedNetworkClient', ); @@ -13292,11 +13272,11 @@ describe('NetworkController', () => { ({ controller }) => { const existingNetworkClient1 = controller.getNetworkClientById(infuraNetworkType); - const destroySpy1 = jest.spyOn(existingNetworkClient1, 'destroy'); + const destroySpy1 = vi.spyOn(existingNetworkClient1, 'destroy'); const existingNetworkClient2 = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy2 = jest.spyOn(existingNetworkClient2, 'destroy'); + const destroySpy2 = vi.spyOn(existingNetworkClient2, 'destroy'); controller.removeNetwork(infuraChainId); @@ -13429,11 +13409,11 @@ describe('NetworkController', () => { const existingNetworkClient1 = controller.getNetworkClientById( 'AAAA-AAAA-AAAA-AAAA', ); - const destroySpy1 = jest.spyOn(existingNetworkClient1, 'destroy'); + const destroySpy1 = vi.spyOn(existingNetworkClient1, 'destroy'); const existingNetworkClient2 = controller.getNetworkClientById( 'BBBB-BBBB-BBBB-BBBB', ); - const destroySpy2 = jest.spyOn(existingNetworkClient2, 'destroy'); + const destroySpy2 = vi.spyOn(existingNetworkClient2, 'destroy'); controller.removeNetwork('0x1337'); @@ -13487,7 +13467,7 @@ describe('NetworkController', () => { }, }, ({ controller, messenger }) => { - const networkRemovedListener = jest.fn(); + const networkRemovedListener = vi.fn(); messenger.subscribe( 'NetworkController:networkRemoved', networkRemovedListener, @@ -15632,15 +15612,14 @@ describe('selectAvailableNetworkClientIds', () => { }); /** - * Creates a mocked version of `createNetworkClient` where multiple mock - * invocations can be specified. A default implementation is provided so that if - * none of the actual invocations of the function match the mock invocations + * Creates a mocked version of `createNetworkClient`. A default implementation + * is provided so that if the caller does not go on to specify a return value * then an error will be thrown. * * @returns The mocked version of `createNetworkClient`. */ -function mockCreateNetworkClient(): WhenMock { - return when(createNetworkClientMock).mockImplementation((options) => { +function mockCreateNetworkClient(): MockedFunction { + return createNetworkClientMock.mockImplementation((options) => { const inspectedOptions = inspect(options, { depth: null, compact: true }); const lines = [ `No fake network client was specified for ${inspectedOptions}.`, @@ -15679,7 +15658,7 @@ function refreshNetworkTests({ initialState?: Partial; operation: (controller: NetworkController) => Promise; }): void { - // eslint-disable-next-line jest/require-top-level-describe + // eslint-disable-next-line vitest/require-top-level-describe it('emits networkWillChange with state payload', async () => { await withController( { @@ -15708,7 +15687,7 @@ function refreshNetworkTests({ ); }); - // eslint-disable-next-line jest/require-top-level-describe + // eslint-disable-next-line vitest/require-top-level-describe it('emits networkDidChange with state payload', async () => { await withController( { @@ -15738,7 +15717,7 @@ function refreshNetworkTests({ }); if (expectedNetworkClientConfiguration.type === NetworkClientType.Custom) { - // eslint-disable-next-line jest/require-top-level-describe + // eslint-disable-next-line vitest/require-top-level-describe it('sets the provider to a custom RPC provider initialized with the RPC target and chain ID', async () => { await withController( { @@ -15778,7 +15757,7 @@ function refreshNetworkTests({ ); }); } else { - // eslint-disable-next-line jest/require-top-level-describe + // eslint-disable-next-line vitest/require-top-level-describe it(`sets the provider to an Infura provider pointed to ${expectedNetworkClientConfiguration.network}`, async () => { await withController( { @@ -15823,7 +15802,7 @@ function refreshNetworkTests({ }); } - // eslint-disable-next-line jest/require-top-level-describe + // eslint-disable-next-line vitest/require-top-level-describe it('replaces the provider object underlying the provider proxy without creating a new instance of the proxy itself', async () => { await withController( { @@ -16940,7 +16919,7 @@ async function setFakeProvider( const fakeProvider = buildFakeProvider(stubs); const fakeNetworkClient = buildFakeClient(fakeProvider); createNetworkClientMock.mockReturnValue(fakeNetworkClient); - const lookupNetworkMock = jest.spyOn(controller, 'lookupNetwork'); + const lookupNetworkMock = vi.spyOn(controller, 'lookupNetwork'); if (stubLookupNetworkWhileSetting) { lookupNetworkMock.mockResolvedValue(undefined); diff --git a/packages/network-controller/tests/helpers.ts b/packages/network-controller/tests/helpers.ts index 07183a199a5..eabe55c780b 100644 --- a/packages/network-controller/tests/helpers.ts +++ b/packages/network-controller/tests/helpers.ts @@ -21,6 +21,8 @@ import type { import { getKnownPropertyNames } from '@metamask/utils'; import type { CaipChainId, Hex } from '@metamask/utils'; import { v4 as uuidV4 } from 'uuid'; +import { vi } from 'vitest'; +import type { Mock } from 'vitest'; import type { AutoManagedNetworkClient } from '../src/create-auto-managed-network-client.js'; import { NetworkController } from '../src/index.js'; @@ -151,20 +153,20 @@ export function buildRootMessenger({ connectivityStatus = CONNECTIVITY_STATUSES.Online, rpcFailoverMode = 'disabled', analyticsId = '11111111-1111-4111-8111-111111111111', - trackEvent = jest.fn(), + trackEvent = vi.fn(), configRegistryNetworkConfigs = [buildMockConfigRegistryControllerNetwork()], }: { connectivityStatus?: ConnectivityStatus; rpcFailoverMode?: RpcFailoverMode; analyticsId?: string; - trackEvent?: jest.Mock; + trackEvent?: Mock; configRegistryNetworkConfigs?: RegistryNetworkConfig[]; } = {}): RootMessenger { const rootMessenger = new Messenger< MockAnyNamespace, MessengerActions, MessengerEvents - >({ namespace: MOCK_ANY_NAMESPACE, captureException: jest.fn() }); + >({ namespace: MOCK_ANY_NAMESPACE, captureException: vi.fn() }); rootMessenger.registerActionHandler( 'ConnectivityController:getState', diff --git a/packages/network-controller/tests/network-client/block-hash-in-response.ts b/packages/network-controller/tests/network-client/block-hash-in-response.ts index aeaa641bbf2..b8f3717c330 100644 --- a/packages/network-controller/tests/network-client/block-hash-in-response.ts +++ b/packages/network-controller/tests/network-client/block-hash-in-response.ts @@ -1,4 +1,5 @@ import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; +import { describe, expect, it, vi } from 'vitest'; import { CUSTOM_RPC_ERRORS } from '../../src/rpc-service/rpc-service.js'; import { NetworkClientType } from '../../src/types.js'; @@ -88,6 +89,7 @@ export function testsForRpcMethodsThatCheckForBlockHashInResponse( // Start the block tracker blockTracker.on('latest', () => { numberOfBlocks += 1; + // eslint-disable-next-line vitest/no-conditional-in-test if (numberOfBlocks === 2) { resolve(); } @@ -97,7 +99,7 @@ export function testsForRpcMethodsThatCheckForBlockHashInResponse( const firstResult = await makeRpcCall(requests[0]); // Proceed to the next iteration of the block tracker so that a new // block is fetched and the current block is updated. - await jest.advanceTimersByTimeAsync(pollingInterval); + await vi.advanceTimersByTimeAsync(pollingInterval); await waitForTwoBlocks; const secondResult = await makeRpcCall(requests[1]); return [firstResult, secondResult]; @@ -326,9 +328,10 @@ export function testsForRpcMethodsThatCheckForBlockHashInResponse( }, ); + // eslint-disable-next-line vitest/no-conditional-in-test if (providerType === NetworkClientType.Infura) { // This is not ideal, but we can refactor this later. - // eslint-disable-next-line jest/no-conditional-expect + // eslint-disable-next-line vitest/no-conditional-expect await expect(promise).rejects.toThrow( rpcErrors.internal({ message: error.message, @@ -337,7 +340,7 @@ export function testsForRpcMethodsThatCheckForBlockHashInResponse( ); } else { // This is not ideal, but we can refactor this later. - // eslint-disable-next-line jest/no-conditional-expect + // eslint-disable-next-line vitest/no-conditional-expect await expect(promise).rejects.toThrow( rpcErrors.internal({ data: error }), ); diff --git a/packages/network-controller/tests/network-client/block-param.ts b/packages/network-controller/tests/network-client/block-param.ts index 5b0e206c7ee..0129dde8666 100644 --- a/packages/network-controller/tests/network-client/block-param.ts +++ b/packages/network-controller/tests/network-client/block-param.ts @@ -1,5 +1,6 @@ import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; import type { Hex } from '@metamask/utils'; +import { describe, expect, it, vi } from 'vitest'; import { CUSTOM_RPC_ERRORS } from '../../src/rpc-service/rpc-service.js'; import { NetworkClientType } from '../../src/types.js'; @@ -188,6 +189,7 @@ export function testsForRpcMethodSupportingBlockParam( // Start the block tracker blockTracker.on('latest', () => { numberOfBlocks += 1; + // eslint-disable-next-line vitest/no-conditional-in-test if (numberOfBlocks === 2) { resolve(); } @@ -197,7 +199,7 @@ export function testsForRpcMethodSupportingBlockParam( const firstResult = await makeRpcCall(requests[0]); // Proceed to the next iteration of the block tracker so that a new // block is fetched and the current block is updated. - await jest.advanceTimersByTimeAsync(pollingInterval); + await vi.advanceTimersByTimeAsync(pollingInterval); await waitForTwoBlocks; const secondResult = await makeRpcCall(requests[1]); return [firstResult, secondResult]; @@ -347,7 +349,7 @@ export function testsForRpcMethodSupportingBlockParam( const firstResult = await resultPromises[0]; // The inflight cache middleware uses setTimeout to run the // handlers, so run them now - jest.runAllTimers(); + vi.runAllTimers(); const remainingResults = await Promise.all(resultPromises.slice(1)); return [firstResult, ...remainingResults]; }, @@ -403,9 +405,10 @@ export function testsForRpcMethodSupportingBlockParam( }, ); + // eslint-disable-next-line vitest/no-conditional-in-test if (providerType === NetworkClientType.Infura) { // This is not ideal, but we can refactor this later. - // eslint-disable-next-line jest/no-conditional-expect + // eslint-disable-next-line vitest/no-conditional-expect await expect(promise).rejects.toThrow( rpcErrors.internal({ message: error.message, @@ -414,7 +417,7 @@ export function testsForRpcMethodSupportingBlockParam( ); } else { // This is not ideal, but we can refactor this later. - // eslint-disable-next-line jest/no-conditional-expect + // eslint-disable-next-line vitest/no-conditional-expect await expect(promise).rejects.toThrow( rpcErrors.internal({ data: error }), ); @@ -1155,7 +1158,7 @@ export function testsForRpcMethodSupportingBlockParam( // first block tracker request occurs because of the first RPC // request. The second block tracker request, however, does not // occur because of the second RPC request, but rather because we - // call `jest.runAllTimers()` below. + // call `vi.runAllTimers()` below. comms.mockNextBlockTrackerRequest({ blockNumber: '0x1' }); comms.mockRpcCall({ request: requests[0], @@ -1173,7 +1176,7 @@ export function testsForRpcMethodSupportingBlockParam( const firstResult = await client.makeRpcCall(requests[0]); // Proceed to the next iteration of the block tracker so that a // new block is fetched and the current block is updated. - jest.runAllTimers(); + vi.runAllTimers(); const secondResult = await client.makeRpcCall(requests[1]); return [firstResult, secondResult]; }, diff --git a/packages/network-controller/tests/network-client/helpers.ts b/packages/network-controller/tests/network-client/helpers.ts index 850026dc21c..9593b327aa7 100644 --- a/packages/network-controller/tests/network-client/helpers.ts +++ b/packages/network-controller/tests/network-client/helpers.ts @@ -9,6 +9,8 @@ import EthQuery from '@metamask/eth-query'; import type { Hex, Json, JsonRpcRequest } from '@metamask/utils'; import nock, { isDone as nockIsDone } from 'nock'; import type { Scope as NockScope } from 'nock'; +import { setTimeout as realSetTimeout } from 'node:timers'; +import { vi } from 'vitest'; import { createNetworkClient } from '../../src/create-network-client.js'; import type { @@ -453,7 +455,12 @@ export async function waitForPromiseToBeFulfilledAfterRunningAllTimers( // `hasPromiseBeenFulfilled` is modified asynchronously. /* eslint-disable-next-line no-unmodified-loop-condition */ while (!hasPromiseBeenFulfilled && numTimesClockHasBeenAdvanced < 30) { - await jest.runAllTimersAsync(); + // Nock schedules its replies on the `node:timers` module, which fake + // timers do not replace, so the macrotask queue has to run for a mocked + // request to settle. `vi.runAllTimersAsync` resolves without yielding to it + // when no fake timer is pending, so yield explicitly first. + await new Promise((resolve) => realSetTimeout(resolve, 0)); + await vi.runAllTimersAsync(); numTimesClockHasBeenAdvanced += 1; } @@ -511,7 +518,7 @@ export async function withNetworkClient( // request the latest block) set up in `eth-json-rpc-middleware` // 2. Halting the retry logic in `@metamask/eth-json-rpc-infura` (which also // depends on `setTimeout`) - jest.useFakeTimers({ doNotFake: ['nextTick', 'queueMicrotask'] }); + vi.useFakeTimers(); const networkControllerMessenger = buildNetworkControllerMessenger(messenger); @@ -589,7 +596,7 @@ export async function withNetworkClient( } finally { await blockTracker.destroy(); - jest.useRealTimers(); + vi.useRealTimers(); } } diff --git a/packages/network-controller/tests/network-client/no-block-param.ts b/packages/network-controller/tests/network-client/no-block-param.ts index 14d5efb8fe2..4fff3070141 100644 --- a/packages/network-controller/tests/network-client/no-block-param.ts +++ b/packages/network-controller/tests/network-client/no-block-param.ts @@ -1,4 +1,5 @@ import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; +import { describe, expect, it, vi } from 'vitest'; import { CUSTOM_RPC_ERRORS } from '../../src/rpc-service/rpc-service.js'; import { NetworkClientType } from '../../src/types.js'; @@ -128,6 +129,7 @@ export function testsForRpcMethodAssumingNoBlockParam( // Start the block tracker blockTracker.on('latest', () => { numberOfBlocks += 1; + // eslint-disable-next-line vitest/no-conditional-in-test if (numberOfBlocks === 2) { resolve(); } @@ -137,7 +139,7 @@ export function testsForRpcMethodAssumingNoBlockParam( const firstResult = await makeRpcCall(requests[0]); // Proceed to the next iteration of the block tracker so that a new // block is fetched and the current block is updated. - await jest.advanceTimersByTimeAsync(pollingInterval); + await vi.advanceTimersByTimeAsync(pollingInterval); await waitForTwoBlocks; const secondResult = await makeRpcCall(requests[1]); return [firstResult, secondResult]; @@ -236,7 +238,7 @@ export function testsForRpcMethodAssumingNoBlockParam( const firstResult = await resultPromises[0]; // The inflight cache middleware uses setTimeout to run the handlers, // so run them now - jest.runAllTimers(); + vi.runAllTimers(); const remainingResults = await Promise.all(resultPromises.slice(1)); return [firstResult, ...remainingResults]; }, @@ -282,9 +284,10 @@ export function testsForRpcMethodAssumingNoBlockParam( }, ); + // eslint-disable-next-line vitest/no-conditional-in-test if (providerType === NetworkClientType.Infura) { // This is not ideal, but we can refactor this later. - // eslint-disable-next-line jest/no-conditional-expect + // eslint-disable-next-line vitest/no-conditional-expect await expect(promise).rejects.toThrow( rpcErrors.internal({ message: error.message, @@ -293,7 +296,7 @@ export function testsForRpcMethodAssumingNoBlockParam( ); } else { // This is not ideal, but we can refactor this later. - // eslint-disable-next-line jest/no-conditional-expect + // eslint-disable-next-line vitest/no-conditional-expect await expect(promise).rejects.toThrow( rpcErrors.internal({ data: error }), ); diff --git a/packages/network-controller/tests/network-client/not-handled-by-middleware.ts b/packages/network-controller/tests/network-client/not-handled-by-middleware.ts index e7af4e0f45e..f773465f420 100644 --- a/packages/network-controller/tests/network-client/not-handled-by-middleware.ts +++ b/packages/network-controller/tests/network-client/not-handled-by-middleware.ts @@ -1,4 +1,5 @@ import { fill } from 'lodash-es'; +import { expect, it } from 'vitest'; import type { ProviderType } from './helpers.js'; import { withMockedCommunications, withNetworkClient } from './helpers.js'; diff --git a/packages/network-controller/tests/network-client/rpc-failover.ts b/packages/network-controller/tests/network-client/rpc-failover.ts index e0eec3c98ef..0267abd4fde 100644 --- a/packages/network-controller/tests/network-client/rpc-failover.ts +++ b/packages/network-controller/tests/network-client/rpc-failover.ts @@ -1,11 +1,18 @@ import { ConstantBackoff } from '@metamask/controller-utils'; import type { Hex } from '@metamask/utils'; +import { describe, expect, it, vi } from 'vitest'; -import { ignoreRejection } from '../../../../tests/helpers.js'; +import { ignoreRejection } from '../../../../tests/vitest/helpers.js'; import { buildRootMessenger } from '../helpers.js'; import type { MockRequest, MockResponse, ProviderType } from './helpers.js'; import { withMockedCommunications, withNetworkClient } from './helpers.js'; +/** + * An error class, as `toThrow` accepts one. Stands in for `jest.Constructable`, + * which Vitest does not re-export. + */ +type Constructable = new (...args: never[]) => unknown; + /** * Tests for RPC failover behavior. * @@ -32,8 +39,8 @@ export function testsForRpcFailoverBehavior({ getRequestToMock: (request: MockRequest, blockNumber: Hex) => MockRequest; failure: MockResponse | Error | string; isRetriableFailure: boolean; - getExpectedError: (url: string) => Error | jest.Constructable; - getExpectedBreakError?: (url: string) => Error | jest.Constructable; + getExpectedError: (url: string) => Error | Constructable; + getExpectedBreakError?: (url: string) => Error | Constructable; }): void { if (providerType === 'custom') { return; @@ -59,6 +66,7 @@ export function testsForRpcFailoverBehavior({ const request = requestToCall; const requestToMock = getRequestToMock(request, blockNumber); const additionalMockRpcCallOptions = + // eslint-disable-next-line vitest/no-conditional-in-test failure instanceof Error || typeof failure === 'string' ? { error: failure } : { response: failure }; @@ -107,7 +115,7 @@ export function testsForRpcFailoverBehavior({ // We also don't need to await this, it just needs to // be added to the promise queue. // eslint-disable-next-line @typescript-eslint/no-floating-promises - jest.advanceTimersByTimeAsync(backoffDuration); + vi.advanceTimersByTimeAsync(backoffDuration); }, ); @@ -127,6 +135,7 @@ export function testsForRpcFailoverBehavior({ it('allows RPC service options to be customized', async () => { const customMaxConsecutiveFailures = 6; const customMaxRetries = 2; + // eslint-disable-next-line vitest/no-conditional-in-test const customNumRequestsToMake = isRetriableFailure ? customMaxConsecutiveFailures / (customMaxRetries + 1) : customMaxConsecutiveFailures; @@ -151,6 +160,7 @@ export function testsForRpcFailoverBehavior({ const request = requestToCall; const requestToMock = getRequestToMock(request, blockNumber); const additionalMockRpcCallOptions = + // eslint-disable-next-line vitest/no-conditional-in-test failure instanceof Error || typeof failure === 'string' ? { error: failure } : { response: failure }; @@ -187,6 +197,7 @@ export function testsForRpcFailoverBehavior({ btoa, isOffline: (): boolean => false, }; + // eslint-disable-next-line vitest/no-conditional-in-test if (rpcEndpointUrl === 'https://failover.endpoint') { const headers: HeadersInit = { 'X-Baz': 'Qux', @@ -223,7 +234,7 @@ export function testsForRpcFailoverBehavior({ // We also don't need to await this, it just needs to // be added to the promise queue. // eslint-disable-next-line @typescript-eslint/no-floating-promises - jest.advanceTimersByTimeAsync(backoffDuration); + vi.advanceTimersByTimeAsync(backoffDuration); }, ); @@ -248,6 +259,7 @@ export function testsForRpcFailoverBehavior({ const request = requestToCall; const requestToMock = getRequestToMock(request, blockNumber); const additionalMockRpcCallOptions = + // eslint-disable-next-line vitest/no-conditional-in-test failure instanceof Error || typeof failure === 'string' ? { error: failure } : { response: failure }; @@ -285,7 +297,7 @@ export function testsForRpcFailoverBehavior({ // We also don't need to await this, it just needs to // be added to the promise queue. // eslint-disable-next-line @typescript-eslint/no-floating-promises - jest.advanceTimersByTimeAsync(backoffDuration); + vi.advanceTimersByTimeAsync(backoffDuration); }); for (let i = 0; i < numRequestsToMake - 1; i++) { diff --git a/packages/network-controller/tsconfig.json b/packages/network-controller/tsconfig.json index b7f3d8f3653..66eb92fc5d9 100644 --- a/packages/network-controller/tsconfig.json +++ b/packages/network-controller/tsconfig.json @@ -1,8 +1,7 @@ { "extends": "../../tsconfig.packages.json", "compilerOptions": { - "rootDir": "../..", - "types": ["jest"] + "rootDir": "../.." }, "references": [ { diff --git a/packages/network-controller/vitest.config.js b/packages/network-controller/vitest.config.js new file mode 100644 index 00000000000..c2f2b025c7e --- /dev/null +++ b/packages/network-controller/vitest.config.js @@ -0,0 +1,27 @@ +import { basename } from 'node:path'; +import { mergeConfig } from 'vitest/config'; + +import baseConfig from '../../vitest.config.packages.js'; + +export default mergeConfig(baseConfig, { + test: { + // oxlint-disable-next-line n/no-unsupported-features/node-builtins + name: basename(import.meta.dirname), + + // Most of this package's tests live in `src`, which the base config + // already covers, but the `NetworkController` suites live in `tests`. + include: ['tests/**/*.test.ts'], + + coverage: { + // Type tests never execute, so they have no coverage to collect. + exclude: ['src/**/*.tst.ts'], + + thresholds: { + branches: 93.17, + functions: 97.94, + lines: 97.47, + statements: 97.36, + }, + }, + }, +}); diff --git a/test-runner-package-map.cjs b/test-runner-package-map.cjs index 1d73ddf240a..955ab7f5c51 100644 --- a/test-runner-package-map.cjs +++ b/test-runner-package-map.cjs @@ -67,7 +67,6 @@ const PACKAGES_ON_JEST = new Set([ '@metamask/multichain-transactions-controller', '@metamask/name-controller', '@metamask/network-connection-banner-controller', - '@metamask/network-controller', '@metamask/network-enablement-controller', '@metamask/notification-services-controller', '@metamask/passkey-controller', diff --git a/tests/vitest/helpers.ts b/tests/vitest/helpers.ts index 724e6092a02..4ec4871df4e 100644 --- a/tests/vitest/helpers.ts +++ b/tests/vitest/helpers.ts @@ -1,5 +1,5 @@ import { setImmediate as realSetImmediate } from 'node:timers'; -import { vi } from 'vitest'; +import { expect, vi } from 'vitest'; /** * Advances the provided fake timer by a specified duration in incremental steps. @@ -45,3 +45,19 @@ export async function advanceTime({ export async function flushPromises(): Promise { await new Promise(realSetImmediate); } + +/** + * Some tests involve a rejected promise that is not necessarily the focus of + * the test. In these cases we don't want to ignore the error in case the + * promise _isn't_ rejected, but we don't want to highlight the assertion, + * either. + * + * @param promiseOrFn - A promise that rejects, or a function that returns a + * promise that rejects. + * @returns A promise that resolves once the rejection has been asserted. + */ +export async function ignoreRejection( + promiseOrFn: Promise | (() => Type | Promise), +): Promise { + await expect(promiseOrFn).rejects.toThrow(expect.any(Error)); +} diff --git a/yarn.lock b/yarn.lock index 2d352fb7be0..ce8cdec052c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8131,22 +8131,19 @@ __metadata: deepmerge: "npm:^4.3.1" fast-deep-equal: "npm:^3.1.3" immer: "npm:^11.1.18" - jest: "npm:^30.5.2" - jest-environment-jsdom: "npm:^30.5.2" - jest-when: "npm:^4.0.3" lodash-es: "npm:^4.18.1" loglevel: "npm:^1.9.2" nock: "npm:^13.5.6" node-fetch: "npm:^2.7.0" reselect: "npm:^5.3.0" rimraf: "npm:^6.1.3" - ts-jest: "npm:^29.4.14" tstyche: "npm:^7.2.5" typedoc: "npm:^0.28.20" typedoc-plugin-missing-exports: "npm:^4.1.4" typescript: "npm:@typescript/typescript6@^6.0.2" uri-js: "npm:^4.4.1" uuid: "npm:^11.1.1" + vitest: "npm:^5.0.0" languageName: unknown linkType: soft From ffac03d1de6149afdc3fc5b5258aa96028854f22 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Fri, 16 Oct 2026 21:54:28 +0200 Subject: [PATCH 2/2] test: run network-controller type tests on Vitest instead of tstyche --- packages/network-controller/package.json | 11 +++-------- ...Controller.tst.ts => NetworkController.test-d.ts} | 6 +++--- packages/network-controller/vitest.config.js | 12 +++++++++++- tsconfig.packages.build.json | 1 + yarn.lock | 1 - 5 files changed, 18 insertions(+), 13 deletions(-) rename packages/network-controller/src/{NetworkController.tst.ts => NetworkController.test-d.ts} (75%) diff --git a/packages/network-controller/package.json b/packages/network-controller/package.json index 7681aac82f1..dd945441cb0 100644 --- a/packages/network-controller/package.json +++ b/packages/network-controller/package.json @@ -44,13 +44,9 @@ "messenger-action-types:check": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --check", "messenger-action-types:generate": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --generate", "since-latest-release": "../../scripts/since-latest-release.sh", - "test": "yarn test:unit && yarn test:types", - "test:clean": "yarn test:unit:clean && yarn test:types", - "test:types": "tstyche", - "test:unit": "vitest", - "test:unit:clean": "vitest --clearCache", - "test:unit:verbose": "vitest --reporter=verbose", - "test:verbose": "yarn test:unit:verbose && yarn test:types", + "test": "vitest", + "test:clean": "vitest --clearCache", + "test:verbose": "vitest --reporter=verbose", "test:watch": "vitest --watch" }, "dependencies": { @@ -93,7 +89,6 @@ "nock": "^13.5.6", "node-fetch": "^2.7.0", "rimraf": "^6.1.3", - "tstyche": "^7.2.5", "typedoc": "^0.28.20", "typedoc-plugin-missing-exports": "^4.1.4", "typescript": "npm:@typescript/typescript6@^6.0.2", diff --git a/packages/network-controller/src/NetworkController.tst.ts b/packages/network-controller/src/NetworkController.test-d.ts similarity index 75% rename from packages/network-controller/src/NetworkController.tst.ts rename to packages/network-controller/src/NetworkController.test-d.ts index f2705ebdddd..4d8d95bd23d 100644 --- a/packages/network-controller/src/NetworkController.tst.ts +++ b/packages/network-controller/src/NetworkController.test-d.ts @@ -1,13 +1,13 @@ import type { Infer } from '@metamask/superstruct'; -import { describe, expect, test } from 'tstyche'; +import { describe, expectTypeOf, test } from 'vitest'; import { NetworkController } from './NetworkController.js'; import type { NetworkState } from './NetworkController.js'; describe('NetworkController.struct', () => { test('is assignable to the NetworkState type', () => { - expect< + expectTypeOf< Infer - >().type.toBeAssignableTo(); + >().toExtend(); }); }); diff --git a/packages/network-controller/vitest.config.js b/packages/network-controller/vitest.config.js index c2f2b025c7e..93d0bf0b667 100644 --- a/packages/network-controller/vitest.config.js +++ b/packages/network-controller/vitest.config.js @@ -12,9 +12,19 @@ export default mergeConfig(baseConfig, { // already covers, but the `NetworkController` suites live in `tests`. include: ['tests/**/*.test.ts'], + typecheck: { + enabled: true, + + // The package's `tsconfig.json` uses project references, which `tsc + // --noEmit` cannot resolve without a prior build, so every import + // outside the type tests reports an error here. `lint:tsc` covers those + // files; this run only has to cover the type tests. + ignoreSourceErrors: true, + }, + coverage: { // Type tests never execute, so they have no coverage to collect. - exclude: ['src/**/*.tst.ts'], + exclude: ['src/**/*.test-d.ts'], thresholds: { branches: 93.17, diff --git a/tsconfig.packages.build.json b/tsconfig.packages.build.json index d938a7429b0..ceac77f8cd7 100644 --- a/tsconfig.packages.build.json +++ b/tsconfig.packages.build.json @@ -14,6 +14,7 @@ "exclude": [ "./jest.config.packages.ts", "**/*.test.ts", + "**/*.test-d.ts", "**/jest.config.ts", "**/*.tst.ts" ] diff --git a/yarn.lock b/yarn.lock index ce8cdec052c..ac30073ec48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8137,7 +8137,6 @@ __metadata: node-fetch: "npm:^2.7.0" reselect: "npm:^5.3.0" rimraf: "npm:^6.1.3" - tstyche: "npm:^7.2.5" typedoc: "npm:^0.28.20" typedoc-plugin-missing-exports: "npm:^4.1.4" typescript: "npm:@typescript/typescript6@^6.0.2"