♻️(docs-app) Switch from Jest tests to Vitest

We have migrated the testing framework from Jest
to Vitest for the Docs application.
This change includes updates to test files,
configuration files, and the addition of new setup
files for Vitest.
This commit is contained in:
Anthony LC
2025-08-06 14:35:43 +02:00
parent ff9e13ca03
commit 1eee24dc19
29 changed files with 325 additions and 277 deletions

View File

@@ -14,6 +14,7 @@ and this project adheres to
### Changed
- ♻️(docs-app) Switch from Jest tests to Vitest #1269
- ⚡️(frontend) improve accessibility:
- #1248
- #1235

View File

@@ -1 +1,2 @@
NEXT_PUBLIC_API_ORIGIN=http://test.jest
NEXT_PUBLIC_PUBLISH_AS_MIT=false

View File

@@ -1,33 +0,0 @@
import type { Config } from 'jest';
import nextJest from 'next/jest.js';
const createJestConfig = nextJest({
dir: './',
});
// Add any custom config to be passed to Jest
const config: Config = {
coverageProvider: 'v8',
moduleNameMapper: {
'^@/docs/(.*)$': '<rootDir>/src/features/docs/$1',
'^@/(.*)$': '<rootDir>/src/$1',
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
testEnvironment: 'jsdom',
};
const jestConfig = async () => {
const nextJestConfig = await createJestConfig(config)();
return {
...nextJestConfig,
moduleNameMapper: {
'\\.svg$': '<rootDir>/jest/mocks/svg.js',
'^.+\\.svg\\?url$': `<rootDir>/jest/mocks/fileMock.js`,
BlockNoteEditor: `<rootDir>/jest/mocks/ComponentMock.js`,
'custom-blocks': `<rootDir>/jest/mocks/ComponentMock.js`,
...nextJestConfig.moduleNameMapper,
},
};
};
export default jestConfig;

View File

@@ -1,4 +0,0 @@
import '@testing-library/jest-dom';
import * as dotenv from 'dotenv';
dotenv.config({ path: './.env.test' });

View File

@@ -1,5 +0,0 @@
import React from 'react';
export const ComponentMock = () => {
return <div>My component mocked</div>;
};

View File

@@ -1,16 +0,0 @@
module.exports = {
src: '/img.jpg',
height: 40,
width: 40,
blurDataURL: 'data:image/png;base64,imagedata',
};
if (
(typeof exports.default === 'function' ||
(typeof exports.default === 'object' && exports.default !== null)) &&
typeof exports.default.__esModule === 'undefined'
) {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}

View File

@@ -1,3 +0,0 @@
const nameMock = 'svg';
export default nameMock;
export const ReactComponent = 'svg';

View File

@@ -11,8 +11,8 @@
"lint": "tsc --noEmit && next lint",
"prettier": "prettier --write .",
"stylelint": "stylelint \"**/*.css\"",
"test": "jest",
"test:watch": "jest --watch"
"test": "vitest",
"test:watch": "vitest --watch"
},
"dependencies": {
"@ag-media/react-pdf-table": "2.0.3",
@@ -67,24 +67,25 @@
"@testing-library/jest-dom": "6.6.4",
"@testing-library/react": "16.3.0",
"@testing-library/user-event": "14.6.1",
"@types/jest": "30.0.0",
"@types/lodash": "4.17.20",
"@types/luxon": "3.7.1",
"@types/node": "*",
"@types/react": "*",
"@types/react-dom": "*",
"@vitejs/plugin-react": "4.7.0",
"cross-env": "10.0.0",
"dotenv": "17.2.1",
"eslint-config-impress": "*",
"fetch-mock": "9.11.0",
"jest": "30.0.5",
"jest-environment-jsdom": "30.0.5",
"jsdom": "26.1.0",
"node-fetch": "2.7.0",
"prettier": "3.6.2",
"stylelint": "16.23.0",
"stylelint-config-standard": "39.0.0",
"stylelint-prettier": "5.0.3",
"typescript": "*",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.2.4",
"webpack": "5.101.0",
"workbox-webpack-plugin": "7.1.0"
}

View File

@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';
import { APIError, isAPIError } from '@/api';
describe('APIError', () => {

View File

@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';
import { baseApiUrl } from '@/api';
describe('config', () => {

View File

@@ -1,4 +1,5 @@
import fetchMock from 'fetch-mock';
import { beforeEach, describe, expect, it } from 'vitest';
import { fetchAPI } from '@/api';

View File

@@ -1,5 +1,6 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { useAPIInfiniteQuery } from '@/api';
@@ -21,8 +22,8 @@ const createWrapper = () => {
describe('helpers', () => {
it('fetches and paginates correctly', async () => {
const mockAPI = jest
.fn<Promise<DummyResponse>, [{ page: number; query: string }]>()
const mockAPI = vi
.fn<(params: { page: number; query: string }) => Promise<DummyResponse>>()
.mockResolvedValueOnce({
results: [{ id: 1 }],
next: 'url?page=2',

View File

@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';
import { errorCauses, getCSRFToken } from '@/api';
describe('utils', () => {

View File

@@ -1,3 +1,4 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { Box } from '../Box';

View File

@@ -1,42 +1,28 @@
import { Crisp } from 'crisp-sdk-web';
import fetchMock from 'fetch-mock';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { gotoLogout } from '../utils';
jest.mock('crisp-sdk-web', () => ({
...jest.requireActual('crisp-sdk-web'),
Crisp: {
isCrispInjected: jest.fn().mockReturnValue(true),
setTokenId: jest.fn(),
user: {
setEmail: jest.fn(),
},
session: {
reset: jest.fn(),
},
},
// Mock the Crisp service
vi.mock('@/services/Crisp', () => ({
terminateCrispSession: vi.fn(),
}));
describe('utils', () => {
afterEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
fetchMock.restore();
});
it('checks support session is terminated when logout', () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
it('checks support session is terminated when logout', async () => {
const { terminateCrispSession } = await import('@/services/Crisp');
window.$crisp = true;
const propertyDescriptors = Object.getOwnPropertyDescriptors(window);
for (const key in propertyDescriptors) {
propertyDescriptors[key].configurable = true;
}
const clonedWindow = Object.defineProperties({}, propertyDescriptors);
Object.defineProperty(clonedWindow, 'location', {
// Mock window.location.replace
const mockReplace = vi.fn();
Object.defineProperty(window, 'location', {
value: {
...window.location,
replace: jest.fn(),
replace: mockReplace,
},
writable: true,
configurable: true,
@@ -44,6 +30,9 @@ describe('utils', () => {
gotoLogout();
expect(Crisp.session.reset).toHaveBeenCalled();
expect(terminateCrispSession).toHaveBeenCalled();
expect(mockReplace).toHaveBeenCalledWith(
'http://test.jest/api/v1.0/logout/',
);
});
});

View File

@@ -1,13 +1,14 @@
import { renderHook, waitFor } from '@testing-library/react';
import fetchMock from 'fetch-mock';
import { Fragment } from 'react';
import { beforeEach, describe, expect, vi } from 'vitest';
import { AbstractAnalytic } from '@/libs';
import { AppWrapper } from '@/tests/utils';
import { useAuth } from '../useAuth';
const trackEventMock = jest.fn();
const trackEventMock = vi.fn();
const flag = true;
class TestAnalytic extends AbstractAnalytic {
public constructor() {
@@ -31,11 +32,11 @@ class TestAnalytic extends AbstractAnalytic {
}
}
jest.mock('next/router', () => ({
...jest.requireActual('next/router'),
vi.mock('next/router', async () => ({
...(await vi.importActual('next/router')),
useRouter: () => ({
pathname: '/dashboard',
replace: jest.fn(),
replace: vi.fn(),
}),
}));
@@ -43,7 +44,7 @@ const dummyUser = { id: '123', email: 'test@example.com' };
describe('useAuth hook - trackEvent effect', () => {
beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
fetchMock.restore();
});

View File

@@ -1,36 +1,38 @@
import { act, renderHook, waitFor } from '@testing-library/react';
import fetchMock from 'fetch-mock';
import { useRouter } from 'next/router';
import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
import * as Y from 'yjs';
import { AppWrapper } from '@/tests/utils';
import { useSaveDoc } from '../useSaveDoc';
jest.mock('next/router', () => ({
useRouter: jest.fn(),
vi.mock('next/router', () => ({
useRouter: vi.fn(),
}));
jest.mock('@/docs/doc-versioning', () => ({
vi.mock('@/docs/doc-versioning', () => ({
KEY_LIST_DOC_VERSIONS: 'test-key-list-doc-versions',
}));
jest.mock('@/docs/doc-management', () => ({
useUpdateDoc: jest.requireActual('@/docs/doc-management/api/useUpdateDoc')
.useUpdateDoc,
vi.mock('@/docs/doc-management', async () => ({
useUpdateDoc: (
await vi.importActual('@/docs/doc-management/api/useUpdateDoc')
).useUpdateDoc,
}));
describe('useSaveDoc', () => {
const mockRouterEvents = {
on: jest.fn(),
off: jest.fn(),
on: vi.fn(),
off: vi.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
fetchMock.restore();
(useRouter as jest.Mock).mockReturnValue({
(useRouter as Mock).mockReturnValue({
events: mockRouterEvents,
});
});
@@ -39,7 +41,7 @@ describe('useSaveDoc', () => {
const yDoc = new Y.Doc();
const docId = 'test-doc-id';
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
const addEventListenerSpy = vi.spyOn(window, 'addEventListener');
renderHook(() => useSaveDoc(docId, yDoc, true, true), {
wrapper: AppWrapper,
@@ -60,8 +62,8 @@ describe('useSaveDoc', () => {
addEventListenerSpy.mockRestore();
});
it('should not save when canSave is false', async () => {
jest.useFakeTimers();
it('should not save when canSave is false', () => {
vi.useFakeTimers();
const yDoc = new Y.Doc();
const docId = 'test-doc-id';
@@ -80,22 +82,19 @@ describe('useSaveDoc', () => {
act(() => {
// Trigger a local update
yDoc.getMap('test').set('key', 'value');
// Advance timers to trigger the save interval
vi.advanceTimersByTime(61000);
});
act(() => {
// Now advance timers after state has updated
jest.advanceTimersByTime(61000);
});
// Since canSave is false, no API call should be made
expect(fetchMock.calls().length).toBe(0);
await waitFor(() => {
expect(fetchMock.calls().length).toBe(0);
});
jest.useRealTimers();
vi.useRealTimers();
});
it('should save when there are local changes', async () => {
jest.useFakeTimers();
vi.useFakeTimers();
const yDoc = new Y.Doc();
const docId = 'test-doc-id';
@@ -117,21 +116,22 @@ describe('useSaveDoc', () => {
});
act(() => {
// Now advance timers after state has updated
jest.advanceTimersByTime(61000);
// Advance timers to trigger the save interval
vi.advanceTimersByTime(61000);
});
// Switch to real timers to allow the mutation promise to resolve
vi.useRealTimers();
await waitFor(() => {
expect(fetchMock.lastCall()?.[0]).toBe(
'http://test.jest/api/v1.0/documents/test-doc-id/',
);
});
jest.useRealTimers();
});
it('should not save when there are no local changes', async () => {
jest.useFakeTimers();
it('should not save when there are no local changes', () => {
vi.useFakeTimers();
const yDoc = new Y.Doc();
const docId = 'test-doc-id';
@@ -148,21 +148,20 @@ describe('useSaveDoc', () => {
});
act(() => {
// Now advance timers after state has updated
jest.advanceTimersByTime(61000);
// Advance timers without triggering any local updates
vi.advanceTimersByTime(61000);
});
await waitFor(() => {
expect(fetchMock.calls().length).toBe(0);
});
// Since there are no local changes, no API call should be made
expect(fetchMock.calls().length).toBe(0);
jest.useRealTimers();
vi.useRealTimers();
});
it('should cleanup event listeners on unmount', () => {
const yDoc = new Y.Doc();
const docId = 'test-doc-id';
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener');
const { unmount } = renderHook(() => useSaveDoc(docId, yDoc, true, true), {
wrapper: AppWrapper,

View File

@@ -1,9 +1,10 @@
import { afterAll, afterEach, describe, expect, it, vi } from 'vitest';
const originalEnv = process.env.NEXT_PUBLIC_PUBLISH_AS_MIT;
jest.mock('@/features/docs/doc-export/utils', () => ({
vi.mock('@/features/docs/doc-export/utils', () => ({
anything: true,
}));
jest.mock('@/features/docs/doc-export/components/ModalExport', () => ({
vi.mock('@/features/docs/doc-export/components/ModalExport', () => ({
ModalExport: () => <span>ModalExport</span>,
}));
@@ -13,8 +14,8 @@ describe('useModuleExport', () => {
});
afterEach(() => {
jest.clearAllMocks();
jest.resetModules();
vi.clearAllMocks();
vi.resetModules();
});
it('should return undefined when NEXT_PUBLIC_PUBLISH_AS_MIT is true', async () => {

View File

@@ -1,6 +1,7 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { Fragment } from 'react';
import { beforeEach, describe, expect, vi } from 'vitest';
import { AbstractAnalytic, Analytics } from '@/libs';
import { AppWrapper } from '@/tests/utils';
@@ -28,14 +29,10 @@ class TestAnalytic extends AbstractAnalytic {
}
}
jest.mock('@/features/docs/doc-export/', () => ({
ModalExport: () => <span>ModalExport</span>,
}));
jest.mock('next/router', () => ({
...jest.requireActual('next/router'),
vi.mock('next/router', async () => ({
...(await vi.importActual('next/router')),
useRouter: () => ({
push: jest.fn(),
push: vi.fn(),
}),
}));

View File

@@ -1,34 +1,30 @@
/**
* @jest-environment node
*/
import '@testing-library/jest-dom';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { RequestSerializer } from '../RequestSerializer';
import { ApiPlugin } from '../plugins/ApiPlugin';
const mockedGet = jest.fn().mockResolvedValue({});
const mockedGetAllKeys = jest.fn().mockResolvedValue([]);
const mockedPut = jest.fn().mockResolvedValue({});
const mockedDelete = jest.fn().mockResolvedValue({});
const mockedClose = jest.fn().mockResolvedValue({});
const mockedOpendDB = jest.fn().mockResolvedValue({
const mockedGet = vi.fn().mockResolvedValue({});
const mockedGetAllKeys = vi.fn().mockResolvedValue([]);
const mockedPut = vi.fn().mockResolvedValue({});
const mockedDelete = vi.fn().mockResolvedValue({});
const mockedClose = vi.fn().mockResolvedValue({});
const mockedOpendDB = vi.fn().mockResolvedValue({
get: mockedGet,
getAllKeys: mockedGetAllKeys,
getAll: jest.fn().mockResolvedValue([]),
getAll: vi.fn().mockResolvedValue([]),
put: mockedPut,
delete: mockedDelete,
clear: jest.fn().mockResolvedValue({}),
clear: vi.fn().mockResolvedValue({}),
close: mockedClose,
});
jest.mock('idb', () => ({
...jest.requireActual('idb'),
vi.mock('idb', async () => ({
...(await vi.importActual('idb')),
openDB: () => mockedOpendDB(),
}));
describe('ApiPlugin', () => {
afterEach(() => jest.clearAllMocks());
afterEach(() => vi.clearAllMocks());
[
{ type: 'item', table: 'doc-item' },
@@ -36,7 +32,7 @@ describe('ApiPlugin', () => {
{ type: 'update', table: 'doc-item' },
].forEach(({ type, table }) => {
it(`calls fetchDidSucceed with type ${type} and status 200`, async () => {
const mockedSync = jest.fn().mockResolvedValue({});
const mockedSync = vi.fn().mockResolvedValue({});
const apiPlugin = new ApiPlugin({
tableName: table as any,
type: type as any,
@@ -55,7 +51,7 @@ describe('ApiPlugin', () => {
json: () => body,
} as unknown as Request,
} as any;
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
const mockedClone = vi.fn().mockReturnValue(requestInit.request);
await apiPlugin.requestWillFetch?.(requestInit);
const response = await apiPlugin.fetchDidSucceed?.({
@@ -81,7 +77,7 @@ describe('ApiPlugin', () => {
const apiPlugin = new ApiPlugin({
tableName: table as any,
type: type as any,
syncManager: jest.fn() as any,
syncManager: vi.fn() as any,
});
const body = { lastName: 'Doe' };
@@ -114,7 +110,7 @@ describe('ApiPlugin', () => {
{ type: 'item', withClone: false },
].forEach(({ type, withClone }) => {
it(`calls requestWillFetch with type ${type}`, async () => {
const mockedSync = jest.fn().mockResolvedValue({});
const mockedSync = vi.fn().mockResolvedValue({});
const apiPlugin = new ApiPlugin({
type: 'update',
@@ -123,7 +119,7 @@ describe('ApiPlugin', () => {
} as any,
});
const mockedClone = jest.fn().mockResolvedValue({});
const mockedClone = vi.fn().mockResolvedValue({});
const requestInit = {
request: {
url: 'test-url',
@@ -189,9 +185,9 @@ describe('ApiPlugin', () => {
} as unknown as Request,
} as any;
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
const mockedClone = vi.fn().mockReturnValue(requestInit.request);
const mockedSync = jest.fn().mockResolvedValue({});
const mockedSync = vi.fn().mockResolvedValue({});
const apiPlugin = new ApiPlugin({
type: 'update',
syncManager: {
@@ -265,9 +261,9 @@ describe('ApiPlugin', () => {
} as unknown as Request,
} as any;
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
const mockedClone = vi.fn().mockReturnValue(requestInit.request);
const mockedSync = jest.fn().mockResolvedValue({});
const mockedSync = vi.fn().mockResolvedValue({});
const apiPlugin = new ApiPlugin({
type: 'delete',
syncManager: {
@@ -334,7 +330,7 @@ describe('ApiPlugin', () => {
Object.defineProperty(global, 'self', {
value: {
crypto: {
randomUUID: jest.fn().mockReturnValue('444555'),
randomUUID: vi.fn().mockReturnValue('444555'),
},
},
});
@@ -351,9 +347,9 @@ describe('ApiPlugin', () => {
} as unknown as Request,
} as any;
const mockedClone = jest.fn().mockReturnValue(requestInit.request);
const mockedClone = vi.fn().mockReturnValue(requestInit.request);
const mockedSync = jest.fn().mockResolvedValue({});
const mockedSync = vi.fn().mockResolvedValue({});
const apiPlugin = new ApiPlugin({
type: 'create',
syncManager: {

View File

@@ -1,15 +1,14 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
/**
* @jest-environment node
* @vitest-environment node
*/
import '@testing-library/jest-dom';
import { MESSAGE_TYPE } from '../conf';
import { OfflinePlugin } from '../plugins/OfflinePlugin';
const mockServiceWorkerScope = {
clients: {
matchAll: jest.fn().mockResolvedValue([]),
matchAll: vi.fn().mockResolvedValue([]),
},
} as unknown as ServiceWorkerGlobalScope;
@@ -19,11 +18,11 @@ const mockServiceWorkerScope = {
} as unknown as ServiceWorkerGlobalScope;
describe('OfflinePlugin', () => {
afterEach(() => jest.clearAllMocks());
afterEach(() => vi.clearAllMocks());
it(`calls fetchDidSucceed`, async () => {
const apiPlugin = new OfflinePlugin();
const postMessageSpy = jest.spyOn(apiPlugin, 'postMessage');
const postMessageSpy = vi.spyOn(apiPlugin, 'postMessage');
await apiPlugin.fetchDidSucceed?.({
response: new Response(),
@@ -34,7 +33,7 @@ describe('OfflinePlugin', () => {
it(`calls fetchDidFail`, async () => {
const apiPlugin = new OfflinePlugin();
const postMessageSpy = jest.spyOn(apiPlugin, 'postMessage');
const postMessageSpy = vi.spyOn(apiPlugin, 'postMessage');
await apiPlugin.fetchDidFail?.({} as any);
@@ -43,12 +42,9 @@ describe('OfflinePlugin', () => {
it(`calls postMessage`, async () => {
const apiPlugin = new OfflinePlugin();
const mockClients = [
{ postMessage: jest.fn() },
{ postMessage: jest.fn() },
];
const mockClients = [{ postMessage: vi.fn() }, { postMessage: vi.fn() }];
mockServiceWorkerScope.clients.matchAll = jest
mockServiceWorkerScope.clients.matchAll = vi
.fn()
.mockResolvedValue(mockClients);

View File

@@ -1,24 +1,23 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
/**
* @jest-environment node
*/
import '@testing-library/jest-dom';
import { SyncManager } from '../SyncManager';
const mockedSleep = jest.fn();
jest.mock('@/utils/system', () => ({
sleep: jest.fn().mockImplementation((ms) => mockedSleep(ms)),
const mockedSleep = vi.fn();
vi.mock('@/utils/system', () => ({
sleep: vi.fn().mockImplementation((ms) => mockedSleep(ms)),
}));
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
describe('SyncManager', () => {
afterEach(() => jest.clearAllMocks());
afterEach(() => vi.clearAllMocks());
it('checks SyncManager no sync to do', async () => {
const toSync = jest.fn();
const hasSyncToDo = jest.fn().mockResolvedValue(false);
const toSync = vi.fn();
const hasSyncToDo = vi.fn().mockResolvedValue(false);
new SyncManager(toSync, hasSyncToDo);
await delay(100);
@@ -28,8 +27,8 @@ describe('SyncManager', () => {
});
it('checks SyncManager sync to do', async () => {
const toSync = jest.fn();
const hasSyncToDo = jest.fn().mockResolvedValue(true);
const toSync = vi.fn();
const hasSyncToDo = vi.fn().mockResolvedValue(true);
new SyncManager(toSync, hasSyncToDo);
await delay(100);
@@ -39,10 +38,10 @@ describe('SyncManager', () => {
});
it('checks SyncManager sync to do trigger error', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
vi.spyOn(console, 'error').mockImplementation(() => {});
const toSync = jest.fn().mockRejectedValue(new Error('error'));
const hasSyncToDo = jest.fn().mockResolvedValue(true);
const toSync = vi.fn().mockRejectedValue(new Error('error'));
const hasSyncToDo = vi.fn().mockResolvedValue(true);
new SyncManager(toSync, hasSyncToDo);
await delay(100);
@@ -56,8 +55,8 @@ describe('SyncManager', () => {
});
it('checks SyncManager multiple sync to do', async () => {
const toSync = jest.fn().mockReturnValue(delay(200));
const hasSyncToDo = jest.fn().mockResolvedValue(true);
const toSync = vi.fn().mockReturnValue(delay(200));
const hasSyncToDo = vi.fn().mockResolvedValue(true);
const syncManager = new SyncManager(toSync, hasSyncToDo);
await syncManager.sync();

View File

@@ -1,11 +1,11 @@
import '@testing-library/jest-dom';
import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { MESSAGE_TYPE } from '../conf';
import { useIsOffline, useOffline } from '../hooks/useOffline';
const mockAddEventListener = jest.fn();
const mockRemoveEventListener = jest.fn();
const mockAddEventListener = vi.fn();
const mockRemoveEventListener = vi.fn();
Object.defineProperty(navigator, 'serviceWorker', {
value: {
addEventListener: mockAddEventListener,
@@ -16,7 +16,7 @@ Object.defineProperty(navigator, 'serviceWorker', {
describe('useOffline', () => {
beforeEach(() => {
jest.clearAllMocks();
vi.clearAllMocks();
});
it('should set isOffline to true when receiving an offline message', () => {

View File

@@ -1,5 +1,5 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { useSWRegister } from '../hooks/useSWRegister';
@@ -12,9 +12,9 @@ const TestComponent = () => {
describe('useSWRegister', () => {
it('checks service-worker is register', () => {
process.env.NEXT_PUBLIC_BUILD_ID = '123456';
jest.spyOn(console, 'error').mockImplementation(() => {});
vi.spyOn(console, 'error').mockImplementation(() => {});
const registerSpy = jest.fn();
const registerSpy = vi.fn();
registerSpy.mockImplementation(
() =>
new Promise((reject) => {
@@ -22,11 +22,13 @@ describe('useSWRegister', () => {
}),
);
const addEventListenerSpy = vi.fn();
Object.defineProperty(navigator, 'serviceWorker', {
value: {
register: registerSpy,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
addEventListener: addEventListenerSpy,
removeEventListener: vi.fn(),
},
writable: true,
});
@@ -34,7 +36,7 @@ describe('useSWRegister', () => {
render(<TestComponent />);
expect(registerSpy).toHaveBeenCalledWith('/service-worker.js?v=123456');
expect(navigator.serviceWorker.addEventListener).toHaveBeenCalledWith(
expect(addEventListenerSpy).toHaveBeenCalledWith(
'controllerchange',
expect.any(Function),
);
@@ -44,7 +46,7 @@ describe('useSWRegister', () => {
process.env.NEXT_PUBLIC_SW_DEACTIVATED = 'true';
process.env.NEXT_PUBLIC_BUILD_ID = '123456';
const registerSpy = jest.fn();
const registerSpy = vi.fn();
registerSpy.mockImplementation(
() =>
new Promise((reject) => {

View File

@@ -1,4 +1,4 @@
import '@testing-library/jest-dom';
import { describe, expect, it } from 'vitest';
import { isValidEmail } from '../string';

View File

@@ -1,3 +1,5 @@
import { describe, expect, it } from 'vitest';
import { isSafeUrl } from '@/utils/url';
describe('isSafeUrl', () => {

View File

@@ -0,0 +1,25 @@
/// <reference types="vitest" />
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [
react(),
tsconfigPaths({
root: '.',
projects: ['./tsconfig.json'],
}),
],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './vitest.setup.ts',
coverage: {
provider: 'v8',
},
},
define: {
'process.env.NODE_ENV': 'test',
},
});

View File

@@ -0,0 +1,4 @@
import '@testing-library/jest-dom/vitest';
import * as dotenv from 'dotenv';
dotenv.config({ path: './.env.test', quiet: true });

View File

@@ -75,6 +75,27 @@
json5 "^2.2.3"
semver "^6.3.1"
"@babel/core@^7.28.0":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.3.tgz#aceddde69c5d1def69b839d09efa3e3ff59c97cb"
integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.27.1"
"@babel/generator" "^7.28.3"
"@babel/helper-compilation-targets" "^7.27.2"
"@babel/helper-module-transforms" "^7.28.3"
"@babel/helpers" "^7.28.3"
"@babel/parser" "^7.28.3"
"@babel/template" "^7.27.2"
"@babel/traverse" "^7.28.3"
"@babel/types" "^7.28.2"
convert-source-map "^2.0.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.3"
semver "^6.3.1"
"@babel/generator@^7.27.5", "@babel/generator@^7.28.0":
version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.0.tgz#9cc2f7bd6eb054d77dc66c2664148a0c5118acd2"
@@ -86,6 +107,17 @@
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
"@babel/generator@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e"
integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==
dependencies:
"@babel/parser" "^7.28.3"
"@babel/types" "^7.28.2"
"@jridgewell/gen-mapping" "^0.3.12"
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
"@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3":
version "7.27.3"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5"
@@ -167,6 +199,15 @@
"@babel/helper-validator-identifier" "^7.27.1"
"@babel/traverse" "^7.27.3"
"@babel/helper-module-transforms@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6"
integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==
dependencies:
"@babel/helper-module-imports" "^7.27.1"
"@babel/helper-validator-identifier" "^7.27.1"
"@babel/traverse" "^7.28.3"
"@babel/helper-optimise-call-expression@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200"
@@ -237,6 +278,14 @@
"@babel/template" "^7.27.2"
"@babel/types" "^7.28.2"
"@babel/helpers@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.3.tgz#b83156c0a2232c133d1b535dd5d3452119c7e441"
integrity sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==
dependencies:
"@babel/template" "^7.27.2"
"@babel/types" "^7.28.2"
"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.0":
version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e"
@@ -244,6 +293,13 @@
dependencies:
"@babel/types" "^7.28.0"
"@babel/parser@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71"
integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==
dependencies:
"@babel/types" "^7.28.2"
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz#61dd8a8e61f7eb568268d1b5f129da3eee364bf9"
@@ -751,6 +807,20 @@
dependencies:
"@babel/plugin-transform-react-jsx" "^7.27.1"
"@babel/plugin-transform-react-jsx-self@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92"
integrity sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-react-jsx-source@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz#dcfe2c24094bb757bf73960374e7c55e434f19f0"
integrity sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-react-jsx@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz#1023bc94b78b0a2d68c82b5e96aed573bcfb9db0"
@@ -1005,6 +1075,19 @@
"@babel/types" "^7.28.0"
debug "^4.3.1"
"@babel/traverse@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434"
integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==
dependencies:
"@babel/code-frame" "^7.27.1"
"@babel/generator" "^7.28.3"
"@babel/helper-globals" "^7.28.0"
"@babel/parser" "^7.28.3"
"@babel/template" "^7.27.2"
"@babel/types" "^7.28.2"
debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.0", "@babel/types@^7.28.2", "@babel/types@^7.4.4":
version "7.28.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b"
@@ -1969,19 +2052,6 @@
resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be"
integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==
"@jest/environment-jsdom-abstract@30.0.5":
version "30.0.5"
resolved "https://registry.yarnpkg.com/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.0.5.tgz#7299cca59b3e84547ca3d1bbd4e7d36b4b44d426"
integrity sha512-gpWwiVxZunkoglP8DCnT3As9x5O8H6gveAOpvaJd2ATAoSh7ZSSCWbr9LQtUMvr8WD3VjG9YnDhsmkCK5WN1rQ==
dependencies:
"@jest/environment" "30.0.5"
"@jest/fake-timers" "30.0.5"
"@jest/types" "30.0.5"
"@types/jsdom" "^21.1.7"
"@types/node" "*"
jest-mock "30.0.5"
jest-util "30.0.5"
"@jest/environment@30.0.5":
version "30.0.5"
resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.0.5.tgz#eaaae0403c7d3f8414053c2224acc3011e1c3a1b"
@@ -4300,6 +4370,11 @@
resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-3.0.0.tgz#96fdb89d25c62e7b6a5d08caf0ce5114370e3b8f"
integrity sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==
"@rolldown/pluginutils@1.0.0-beta.27":
version "1.0.0-beta.27"
resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz#47d2bf4cef6d470b22f5831b420f8964e0bf755f"
integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@@ -5346,15 +5421,6 @@
expect "^30.0.0"
pretty-format "^30.0.0"
"@types/jsdom@^21.1.7":
version "21.1.7"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.7.tgz#9edcb09e0b07ce876e7833922d3274149c898cfa"
integrity sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==
dependencies:
"@types/node" "*"
"@types/tough-cookie" "*"
parse5 "^7.0.0"
"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
@@ -5577,11 +5643,6 @@
dependencies:
"@types/node" "*"
"@types/tough-cookie@*":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
"@types/trusted-types@^2.0.2":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
@@ -5816,6 +5877,18 @@
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777"
integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==
"@vitejs/plugin-react@4.7.0":
version "4.7.0"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz#647af4e7bb75ad3add578e762ad984b90f4a24b9"
integrity sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==
dependencies:
"@babel/core" "^7.28.0"
"@babel/plugin-transform-react-jsx-self" "^7.27.1"
"@babel/plugin-transform-react-jsx-source" "^7.27.1"
"@rolldown/pluginutils" "1.0.0-beta.27"
"@types/babel__core" "^7.20.5"
react-refresh "^0.17.0"
"@vitest/expect@3.2.4":
version "3.2.4"
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433"
@@ -8734,6 +8807,11 @@ globjoin@^0.1.4:
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==
globrex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
gopd@^1.0.1, gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
@@ -9788,17 +9866,6 @@ jest-each@30.0.5:
jest-util "30.0.5"
pretty-format "30.0.5"
jest-environment-jsdom@30.0.5:
version "30.0.5"
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-30.0.5.tgz#36351cc8a14fcd54945da0beb029af493d7d5764"
integrity sha512-BmnDEoAH+jEjkPrvE9DTKS2r3jYSJWlN/r46h0/DBUxKrkgt2jAZ5Nj4wXLAcV1KWkRpcFqA5zri9SWzJZ1cCg==
dependencies:
"@jest/environment" "30.0.5"
"@jest/environment-jsdom-abstract" "30.0.5"
"@types/jsdom" "^21.1.7"
"@types/node" "*"
jsdom "^26.1.0"
jest-environment-node@30.0.5:
version "30.0.5"
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.0.5.tgz#6a98dd80e0384ead67ed05643381395f6cda93c9"
@@ -10080,6 +10147,32 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
jsdom@26.1.0:
version "26.1.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3"
integrity sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==
dependencies:
cssstyle "^4.2.1"
data-urls "^5.0.0"
decimal.js "^10.5.0"
html-encoding-sniffer "^4.0.0"
http-proxy-agent "^7.0.2"
https-proxy-agent "^7.0.6"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.16"
parse5 "^7.2.1"
rrweb-cssom "^0.8.0"
saxes "^6.0.0"
symbol-tree "^3.2.4"
tough-cookie "^5.1.1"
w3c-xmlserializer "^5.0.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^3.1.1"
whatwg-mimetype "^4.0.0"
whatwg-url "^14.1.1"
ws "^8.18.0"
xml-name-validator "^5.0.0"
jsdom@^25.0.1:
version "25.0.1"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef"
@@ -10107,32 +10200,6 @@ jsdom@^25.0.1:
ws "^8.18.0"
xml-name-validator "^5.0.0"
jsdom@^26.1.0:
version "26.1.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3"
integrity sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==
dependencies:
cssstyle "^4.2.1"
data-urls "^5.0.0"
decimal.js "^10.5.0"
html-encoding-sniffer "^4.0.0"
http-proxy-agent "^7.0.2"
https-proxy-agent "^7.0.6"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.16"
parse5 "^7.2.1"
rrweb-cssom "^0.8.0"
saxes "^6.0.0"
symbol-tree "^3.2.4"
tough-cookie "^5.1.1"
w3c-xmlserializer "^5.0.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^3.1.1"
whatwg-mimetype "^4.0.0"
whatwg-url "^14.1.1"
ws "^8.18.0"
xml-name-validator "^5.0.0"
jsesc@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
@@ -12284,6 +12351,11 @@ react-number-format@^5.4.3:
resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.4.4.tgz#d31f0e260609431500c8d3f81bbd3ae1fb7cacad"
integrity sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==
react-refresh@^0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.17.0.tgz#b7e579c3657f23d04eccbe4ad2e58a8ed51e7e53"
integrity sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==
react-remove-scroll-bar@^2.3.7:
version "2.3.8"
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223"
@@ -13952,6 +14024,11 @@ tsc-alias@1.8.16:
normalize-path "^3.0.0"
plimit-lit "^1.2.6"
tsconfck@^3.0.3:
version "3.1.6"
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.6.tgz#da1f0b10d82237ac23422374b3fce1edb23c3ead"
integrity sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==
tsconfig-paths@^3.15.0:
version "3.15.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
@@ -14468,6 +14545,15 @@ vite-node@3.2.4:
pathe "^2.0.3"
vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
vite-tsconfig-paths@5.1.4:
version "5.1.4"
resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz#d9a71106a7ff2c1c840c6f1708042f76a9212ed4"
integrity sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==
dependencies:
debug "^4.1.1"
globrex "^0.1.2"
tsconfck "^3.0.3"
"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.0.tgz#6fb13c74c13cfdd0e200ee61d6ea6e8fafc2e8b5"