Skip to content

Commit 5517da9

Browse files
jorgenclaude
andcommitted
feat: migrate from Jest to Vitest
Migrate test runner from Jest to Vitest for significantly improved performance: - Frontend tests: 77.9s → 2.1s (37x faster) - API tests: 84.4s → 7.6s (11x faster) Changes: - Add vitest.frontend.config.ts and vitest.api.config.ts - Use node environment by default, jsdom only for DOM-requiring tests - Convert jest.mock/fn/spyOn to vi.mock/fn/spyOn across 17 test files - Add setup.vitest.ts with TextEncoder polyfill - Inline @across-protocol/* packages to resolve ESM issues - Update package.json test scripts to use vitest Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 916eb80 commit 5517da9

24 files changed

+535
-221
lines changed

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@
7575
"remote-env": "tsx ./scripts/fetch-remote-env.ts",
7676
"cache-swap-tokens": "tsx scripts/cache-swap-tokens.ts",
7777
"analyze": "yarn build && rollup-plugin-visualizer --open ./bundle-size-analysis.json",
78-
"test": "export REACT_APP_GIT_COMMIT_HASH=$(git rev-parse HEAD) && jest --config jest.frontend.config.cjs",
78+
"test": "export REACT_APP_GIT_COMMIT_HASH=$(git rev-parse HEAD) && vitest run --config vitest.frontend.config.ts",
7979
"serve": "vite preview --port 3000",
80-
"test-api": "yarn remote-config && jest --config jest.api.config.cjs ./test/api",
80+
"test-api": "yarn remote-config && vitest run --config vitest.api.config.ts --dir ./test/api",
8181
"pretest:e2e": "npm pkg set 'type'='module'",
8282
"test:e2e:headful": "yarn pretest:e2e && playwright test",
8383
"test:e2e:headless": "yarn pretest:e2e && HEADLESS=true playwright test",
8484
"test:e2e:headless:ui": "yarn pretest:e2e && HEADLESS=true playwright test --ui",
85-
"test:e2e-api": "export NODE_OPTIONS='--max-old-space-size=8192' && yarn remote-config && jest --config jest.api.config.cjs ./e2e-api",
85+
"test:e2e-api": "export NODE_OPTIONS='--max-old-space-size=8192' && yarn remote-config && vitest run --config vitest.api.config.ts --dir ./e2e-api",
8686
"eject": "react-scripts eject",
8787
"format": "prettier --check src api e2e test",
8888
"format:fix": "prettier --write src api e2e test",
@@ -164,6 +164,8 @@
164164
"@types/react-router-dom": "5.3.3",
165165
"@vercel/node": "^5.0.2",
166166
"@vitejs/plugin-react": "^4.3.4",
167+
"@vitest/browser": "^4.0.15",
168+
"@vitest/ui": "^4.0.15",
167169
"axios-mock-adapter": "^1.21.2",
168170
"buffer": "^6.0.3",
169171
"canvas": "^3.1.2",
@@ -175,6 +177,7 @@
175177
"eslint-plugin-react": "^7.37.4",
176178
"eslint-plugin-react-hooks": "^4.6.2",
177179
"eslint-plugin-storybook": "^0.6.15",
180+
"happy-dom": "^20.0.11",
178181
"husky": "^8.0.0",
179182
"jest": "^29.5.0",
180183
"jest-environment-jsdom": "^29.5.0",
@@ -195,7 +198,8 @@
195198
"vite-plugin-eslint": "^1.8.1",
196199
"vite-plugin-node-polyfills": "^0.23.0",
197200
"vite-plugin-svgr": "^3.2.0",
198-
"vite-tsconfig-paths": "^4.2.0"
201+
"vite-tsconfig-paths": "^4.2.0",
202+
"vitest": "^4.0.15"
199203
},
200204
"babel": {
201205
"env": {

setup.vitest.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { TextEncoder, TextDecoder } from "util";
2+
3+
global.TextEncoder = TextEncoder;
4+
// @ts-expect-error - The types are incompatible but the implementation works correctly
5+
global.TextDecoder = TextDecoder;

src/hooks/tests/usePrevious.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @vitest-environment jsdom
12
import { renderHook } from "@testing-library/react";
23
import { usePrevious } from "../usePrevious";
34
const setUp = () =>

src/hooks/tests/useScrollPosition.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @vitest-environment jsdom
12
import { renderHook } from "@testing-library/react";
23
import useScrollPosition from "../useScrollPosition";
34

src/hooks/tests/useWindowSize.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @vitest-environment jsdom
12
import { renderHook, act } from "@testing-library/react";
23
import useWindowSize from "../useWindowSize";
34

src/utils/tests/rewards.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { BigNumber } from "ethers";
22
import { parseEtherLike } from "utils/format";
3+
import { vi } from "vitest";
34

45
import { getBaseRewardsApr } from "../rewards";
56

6-
// Enums break ts-jest
7-
// https://github.com/kulshekhar/ts-jest/issues/3397
8-
jest.mock("../providers.ts", () => ({
7+
vi.mock("../providers.ts", () => ({
98
ChainId: {
109
MAINNET: 1,
1110
},

test/api/_bridges/bridges-utils.test.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { vi } from "vitest";
12
import { BigNumber } from "ethers";
23
import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../../../api/_constants";
34
import {
@@ -9,15 +10,18 @@ import { LimitsResponse } from "../../../api/_types";
910
import { getCachedLimits } from "../../../api/_utils";
1011
import { getTransferMode } from "../../../api/_bridges/cctp/utils/fill-times";
1112

12-
jest.mock("../../../api/_utils", () => ({
13-
...jest.requireActual("../../../api/_utils"),
14-
getCachedLimits: jest.fn(),
13+
vi.mock("../../../api/_utils", async (importOriginal) => ({
14+
...(await importOriginal()),
15+
getCachedLimits: vi.fn(),
1516
}));
1617

17-
jest.mock("../../../api/_bridges/cctp/utils/fill-times", () => ({
18-
...jest.requireActual("../../../api/_bridges/cctp/utils/fill-times"),
19-
getTransferMode: jest.fn(),
20-
}));
18+
vi.mock(
19+
"../../../api/_bridges/cctp/utils/fill-times",
20+
async (importOriginal) => ({
21+
...(await importOriginal()),
22+
getTransferMode: vi.fn(),
23+
})
24+
);
2125

2226
// Helper function to create a token on a specific chain
2327
const createToken = (
@@ -86,8 +90,10 @@ describe("#getBridgeStrategyData()", () => {
8690

8791
beforeEach(() => {
8892
// Reset and setup mock before each test
89-
(getCachedLimits as jest.Mock).mockReset();
90-
(getCachedLimits as jest.Mock).mockResolvedValue(mockLimitsResponse());
93+
(getCachedLimits as ReturnType<typeof vi.fn>).mockReset();
94+
(getCachedLimits as ReturnType<typeof vi.fn>).mockResolvedValue(
95+
mockLimitsResponse()
96+
);
9197
});
9298

9399
describe("Utilization checks", () => {
@@ -218,7 +224,7 @@ describe("#getBridgeStrategyData()", () => {
218224
const limits = mockLimitsResponse({
219225
maxDepositInstant: "100000000", // 100 USDC
220226
});
221-
(getCachedLimits as jest.Mock).mockResolvedValue(limits);
227+
(getCachedLimits as ReturnType<typeof vi.fn>).mockResolvedValue(limits);
222228

223229
const result = await getBridgeStrategyData({
224230
...baseParams,
@@ -235,7 +241,7 @@ describe("#getBridgeStrategyData()", () => {
235241
const limits = mockLimitsResponse({
236242
maxDepositInstant: "100000000", // 100 USDC
237243
});
238-
(getCachedLimits as jest.Mock).mockResolvedValue(limits);
244+
(getCachedLimits as ReturnType<typeof vi.fn>).mockResolvedValue(limits);
239245

240246
const result = await getBridgeStrategyData({
241247
...baseParams,
@@ -257,7 +263,7 @@ describe("#getBridgeStrategyData()", () => {
257263
utilizedReserves: "500000000000", // 83.3% utilization
258264
},
259265
});
260-
(getCachedLimits as jest.Mock).mockResolvedValue(limits);
266+
(getCachedLimits as ReturnType<typeof vi.fn>).mockResolvedValue(limits);
261267

262268
const result = await getBridgeStrategyData({
263269
...baseParams,
@@ -277,7 +283,7 @@ describe("#getBridgeStrategyData()", () => {
277283
utilizedReserves: "100000000000", // 33.3% utilization
278284
},
279285
});
280-
(getCachedLimits as jest.Mock).mockResolvedValue(limits);
286+
(getCachedLimits as ReturnType<typeof vi.fn>).mockResolvedValue(limits);
281287

282288
const result = await getBridgeStrategyData({
283289
...baseParams,
@@ -385,13 +391,15 @@ describe("#getBridgeStrategyData()", () => {
385391
describe("Linea fast mode eligibility", () => {
386392
beforeEach(() => {
387393
// Reset getTransferMode mock before each test
388-
(getTransferMode as jest.Mock).mockReset();
394+
(getTransferMode as ReturnType<typeof vi.fn>).mockReset();
389395
});
390396

391397
test("should mark Linea as Fast CCTP eligible when fast mode is available", async () => {
392398
const amount = BigNumber.from("15000000000"); // 15,000 USDC
393399
// Mock getTransferMode to return "fast" mode
394-
(getTransferMode as jest.Mock).mockResolvedValue("fast");
400+
(getTransferMode as ReturnType<typeof vi.fn>).mockResolvedValue(
401+
"fast"
402+
);
395403

396404
const result = await getBridgeStrategyData({
397405
...baseParams,
@@ -412,7 +420,9 @@ describe("#getBridgeStrategyData()", () => {
412420
test("should not mark Linea as Fast CCTP eligible when fast mode is unavailable", async () => {
413421
const amount = BigNumber.from("15000000000"); // 15,000 USDC
414422
// Mock getTransferMode to return "standard" mode (fast mode not available)
415-
(getTransferMode as jest.Mock).mockResolvedValue("standard");
423+
(getTransferMode as ReturnType<typeof vi.fn>).mockResolvedValue(
424+
"standard"
425+
);
416426

417427
const result = await getBridgeStrategyData({
418428
...baseParams,
@@ -484,7 +494,7 @@ describe("#getBridgeStrategyData()", () => {
484494
const limits = mockLimitsResponse({
485495
maxDepositInstant: "1000000", // Should match converted amount
486496
});
487-
(getCachedLimits as jest.Mock).mockResolvedValue(limits);
497+
(getCachedLimits as ReturnType<typeof vi.fn>).mockResolvedValue(limits);
488498

489499
const result = await getBridgeStrategyData({
490500
...baseParams,
@@ -516,7 +526,7 @@ describe("#getBridgeStrategyData()", () => {
516526
describe("Error handling", () => {
517527
test("should return undefined when getCachedLimits throws error", async () => {
518528
const amount = BigNumber.from("1000000");
519-
(getCachedLimits as jest.Mock).mockRejectedValue(
529+
(getCachedLimits as ReturnType<typeof vi.fn>).mockRejectedValue(
520530
new Error("API error")
521531
);
522532

0 commit comments

Comments
 (0)