From 40cf0a88d9f0871cd16769cd15689f4d4a5567aa Mon Sep 17 00:00:00 2001 From: nowgnuesLee <192685612+nowgnuesLee@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:12:28 +0000 Subject: [PATCH] feat(FR-2609): Vitest migration for react/ (100% pass, 856/856 tests) (#6874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #6812(FR-2609) ## Summary Migrates `react/`'s test suite from Jest to Vitest. **856/856 tests passing** under Vitest. (Commit title still reads `96.9% pass rate, 822/848 tests` — that was the baseline before the last-mile fixes landed in-tree via `gt modify`. Current state is 100%; see test-plan counts below.) ### New files - `react/vitest.config.ts` — reuses `@vitejs/plugin-react` + `babel-plugin-react-compiler` + `babel-plugin-relay` so tests and the running app exercise identical transformed code. - `react/__test__/vitest.jest-compat.ts` — exposes `vi` as `jest` globally, so existing `jest.fn()` / `jest.spyOn()` calls in test files continue to work without a sweeping rename. ### Vitest 4 pitfalls fixed inline - **`process.env.NODE_ENV` is non-configurable** in Node 20+. Replaced `Object.defineProperty(process.env, 'NODE_ENV', ...)` with `vi.stubEnv('NODE_ENV', ...)` + `vi.unstubAllEnvs()` in `customThemeConfig.test.ts`. - **Default-export mock shape**: `vi.mock(path, () => Component)` must return `{ default: Component }` for files imported via `import X from ...`. Fixed in `MyResourceWithinResourceGroup.test.tsx`. - **Arrow-function `mockImplementation` called with `new`**: `new TabCount()` throws because arrow functions aren't constructors. Swapped all `MockedTabCount.mockImplementation(() => ({...}))` to `function(this: any) {...}` in `useLoginOrchestration.test.ts`. - **Factory-created vi.fn() mock history** isn't cleared by `vi.restoreAllMocks()` (that only touches `spyOn` spies). Added `vi.clearAllMocks()` in `afterEach` to prevent call-count leakage across tests. ## Test plan - [x] `pnpm --prefix ./react run vitest` → 856/856 pass - [x] Same React Compiler / Relay transforms as prod build (`'use memo'` optimisations active in tests) - [x] `scripts/verify.sh` passes for Lint + Format + Relay checks ## Stack Builds on FR-2608. The broader Jest → Vitest cutover continues in the next PR (BUI + root `/src`). --- pnpm-lock.yaml | 572 +++++++++++++++++- react/VITE_POC_NOTES.md | 44 +- react/__test__/vitest.jest-compat.ts | 27 + react/package.json | 4 + .../MyResourceWithinResourceGroup.test.tsx | 32 +- .../rules/__tests__/configRules.test.ts | 1 - .../rules/__tests__/cspRules.test.ts | 1 - .../rules/__tests__/endpointRules.test.ts | 1 - .../rules/__tests__/storageProxyRules.test.ts | 1 - react/src/global-stores.test.ts | 10 +- react/src/helper/big-number.test.ts | 1 - react/src/helper/customThemeConfig.test.ts | 68 +-- react/src/helper/index.test.tsx | 8 +- react/src/helper/resultTypes.test.ts | 1 - .../useMultiStepNotification.test.tsx | 52 +- .../hooks/useBackendAIImageMetaData.test.tsx | 2 +- react/src/hooks/useControllableState.test.ts | 2 +- react/src/hooks/useHighlight.test.tsx | 47 +- react/src/hooks/useKeyboardShortcut.test.ts | 11 +- react/src/hooks/useLoginOrchestration.test.ts | 82 ++- react/src/hooks/useLogout.test.ts | 18 +- react/src/hooks/useMemoWithPrevious.test.tsx | 8 +- react/src/hooks/usePrimaryColors.test.tsx | 4 +- react/src/hooks/useScrollBreackPoint.test.tsx | 13 +- react/src/hooks/useTokenizer.test.ts | 7 +- .../src/hooks/useValidateServiceName.test.tsx | 2 +- .../src/hooks/useValidateSessionName.test.tsx | 2 +- react/src/hooks/useVariantConfigs.test.ts | 11 +- react/src/hooks/useWebUIConfig.test.ts | 8 +- react/src/lib/TabCounter.test.ts | 20 +- react/vitest.config.ts | 101 ++++ 31 files changed, 928 insertions(+), 233 deletions(-) create mode 100644 react/__test__/vitest.jest-compat.ts create mode 100644 react/vitest.config.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46f5a739d8..cd070df6d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -605,7 +605,7 @@ importers: version: link:../eslint-config-bai eslint-plugin-import: specifier: 'catalog:' - version: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.4(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4))(eslint@9.39.4(jiti@1.21.7)) eslint-plugin-json-schema-validator: specifier: 'catalog:' version: 5.5.1(eslint@9.39.4(jiti@1.21.7)) @@ -692,7 +692,7 @@ importers: version: 7.0.1(eslint@9.39.4(jiti@1.21.7)) typescript-eslint: specifier: 'catalog:' - version: 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3) + version: 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) react: dependencies: @@ -1038,7 +1038,7 @@ importers: version: link:../packages/eslint-config-bai eslint-plugin-import: specifier: 'catalog:' - version: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7)) eslint-plugin-react: specifier: 'catalog:' version: 7.37.5(eslint@9.39.4(jiti@1.21.7)) @@ -1066,6 +1066,9 @@ importers: jest-environment-jsdom: specifier: 'catalog:' version: 30.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + jsdom: + specifier: ^29.0.2 + version: 29.0.2 nodemon: specifier: ^3.1.14 version: 3.1.14 @@ -1108,6 +1111,9 @@ importers: vite-plugin-svgr: specifier: ^4.5.0 version: 4.5.0(rollup@2.80.0)(typescript@5.7.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3)) + vitest: + specifier: ^4.1.4 + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jsdom@29.0.2)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3)) webpack: specifier: 'catalog:' version: 5.105.4(esbuild@0.27.3)(webpack-cli@6.0.1(webpack@5.105.4)) @@ -1372,6 +1378,17 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@asamuzakjp/css-color@5.1.10': + resolution: {integrity: sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.0.9': + resolution: {integrity: sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} @@ -2130,6 +2147,10 @@ packages: '@braintree/sanitize-url@7.1.2': resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} @@ -2319,6 +2340,10 @@ packages: resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + '@csstools/css-calc@2.1.4': resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} engines: {node: '>=18'} @@ -2326,6 +2351,13 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-color-parser@3.1.0': resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} engines: {node: '>=18'} @@ -2333,16 +2365,41 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-parser-algorithms@3.0.5': resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} engines: {node: '>=18'} peerDependencies: '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + '@csstools/normalize.css@12.1.1': resolution: {integrity: sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==} @@ -3128,6 +3185,15 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -5802,15 +5868,44 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.1.4': + resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==} + + '@vitest/mocker@4.1.4': + resolution: {integrity: sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.1.4': + resolution: {integrity: sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==} + + '@vitest/runner@4.1.4': + resolution: {integrity: sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==} + + '@vitest/snapshot@4.1.4': + resolution: {integrity: sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==} + '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.1.4': + resolution: {integrity: sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==} + '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.1.4': + resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==} + '@volar/language-core@2.4.14': resolution: {integrity: sha512-X6beusV0DvuVseaOEy7GoagS4rYHgDHnTrdOj5jeUb49fW5ceQyP9Ej5rBhqgz2wJggl+2fDbbojq1XKaxDi6w==} @@ -6508,6 +6603,9 @@ packages: resolution: {integrity: sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==} engines: {node: '>= 8.0.0'} + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -6743,6 +6841,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk-template@0.4.0: resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} engines: {node: '>=12'} @@ -7287,6 +7389,10 @@ packages: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@3.4.2: resolution: {integrity: sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==} engines: {node: '>= 6'} @@ -7547,6 +7653,10 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -8404,6 +8514,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + expect@27.5.1: resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -9042,6 +9156,10 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-entities@2.5.2: resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} @@ -9989,6 +10107,15 @@ packages: canvas: optional: true + jsdom@29.0.2: + resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -10306,6 +10433,10 @@ packages: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -10471,6 +10602,9 @@ packages: mdn-data@2.0.4: resolution: {integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -11002,6 +11136,9 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + octokit@5.0.5: resolution: {integrity: sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==} engines: {node: '>= 20'} @@ -11168,6 +11305,9 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -12884,6 +13024,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -13033,6 +13176,9 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} @@ -13050,6 +13196,9 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -13446,6 +13595,9 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -13458,6 +13610,10 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -13465,10 +13621,17 @@ packages: tldts-core@6.1.86: resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + tldts-core@7.0.28: + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} + tldts@6.1.86: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true + tldts@7.0.28: + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} + hasBin: true + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -13514,6 +13677,10 @@ packages: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -13528,6 +13695,10 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -13768,6 +13939,10 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -14078,6 +14253,47 @@ packages: yaml: optional: true + vitest@4.1.4: + resolution: {integrity: sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.4 + '@vitest/browser-preview': 4.1.4 + '@vitest/browser-webdriverio': 4.1.4 + '@vitest/coverage-istanbul': 4.1.4 + '@vitest/coverage-v8': 4.1.4 + '@vitest/ui': 4.1.4 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} @@ -14160,6 +14376,10 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + webpack-cli@6.0.1: resolution: {integrity: sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==} engines: {node: '>=18.12.0'} @@ -14267,10 +14487,18 @@ packages: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + whatwg-url@14.2.0: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -14306,6 +14534,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + widest-line@4.0.1: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} @@ -15076,6 +15309,22 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 + '@asamuzakjp/css-color@5.1.10': + dependencies: + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@asamuzakjp/dom-selector@7.0.9': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + + '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 @@ -16034,6 +16283,10 @@ snapshots: '@braintree/sanitize-url@7.1.2': {} + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + '@chevrotain/cst-dts-gen@11.0.3': dependencies: '@chevrotain/gast': 11.0.3 @@ -16443,11 +16696,18 @@ snapshots: '@csstools/color-helpers@5.1.0': {} + '@csstools/color-helpers@6.0.2': {} + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/color-helpers': 5.1.0 @@ -16455,12 +16715,29 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + '@csstools/css-tokenizer@3.0.4': {} + '@csstools/css-tokenizer@4.0.0': {} + '@csstools/normalize.css@12.1.1': {} '@csstools/postcss-cascade-layers@1.1.1(postcss@8.4.49)': @@ -17158,6 +17435,8 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.15.0': {} + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -20260,10 +20539,10 @@ snapshots: '@types/node': 25.3.5 optional: true - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/type-utils': 5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) '@typescript-eslint/utils': 5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) @@ -20297,6 +20576,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4))(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.57.1 + '@typescript-eslint/type-utils': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.57.1 + eslint: 9.39.4(jiti@1.21.7) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -20362,6 +20657,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/scope-manager': 8.57.1 + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.57.1 + debug: 4.4.3(supports-color@5.5.0) + eslint: 9.39.4(jiti@1.21.7) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.57.1 @@ -20404,6 +20711,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.57.1(typescript@5.5.4)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.57.1(typescript@5.5.4) + '@typescript-eslint/types': 8.57.1 + debug: 4.4.3(supports-color@5.5.0) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.57.1(typescript@5.7.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.57.1(typescript@5.7.3) @@ -20450,6 +20766,10 @@ snapshots: dependencies: typescript: 5.9.3 + '@typescript-eslint/tsconfig-utils@8.57.1(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + '@typescript-eslint/tsconfig-utils@8.57.1(typescript@5.7.3)': dependencies: typescript: 5.7.3 @@ -20482,6 +20802,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.5.4) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + debug: 4.4.3(supports-color@5.5.0) + eslint: 9.39.4(jiti@1.21.7) + ts-api-utils: 2.4.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/type-utils@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3)': dependencies: '@typescript-eslint/types': 8.57.1 @@ -20573,6 +20905,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.57.1(typescript@5.5.4)': + dependencies: + '@typescript-eslint/project-service': 8.57.1(typescript@5.5.4) + '@typescript-eslint/tsconfig-utils': 8.57.1(typescript@5.5.4) + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/visitor-keys': 8.57.1 + debug: 4.4.3(supports-color@5.5.0) + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@8.57.1(typescript@5.7.3)': dependencies: '@typescript-eslint/project-service': 8.57.1(typescript@5.7.3) @@ -20651,6 +20998,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.57.1 + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.5.4) + eslint: 9.39.4(jiti@1.21.7) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@1.21.7)) @@ -20859,20 +21217,61 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/expect@4.1.4': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.4(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3))': + dependencies: + '@vitest/spy': 4.1.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3) + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 + '@vitest/pretty-format@4.1.4': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.4': + dependencies: + '@vitest/utils': 4.1.4 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.4': + dependencies: + '@vitest/pretty-format': 4.1.4 + '@vitest/utils': 4.1.4 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 + '@vitest/spy@4.1.4': {} + '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 loupe: 3.2.1 tinyrainbow: 2.0.0 + '@vitest/utils@4.1.4': + dependencies: + '@vitest/pretty-format': 4.1.4 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + '@volar/language-core@2.4.14': dependencies: '@volar/source-map': 2.4.14 @@ -21823,6 +22222,10 @@ snapshots: jsonpath: 1.3.0 tryer: 1.0.1 + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + big.js@5.2.2: {} big.js@7.0.1: {} @@ -22109,6 +22512,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.2: {} + chalk-template@0.4.0: dependencies: chalk: 4.1.2 @@ -22697,6 +23102,11 @@ snapshots: mdn-data: 2.0.30 source-map-js: 1.2.1 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + css-what@3.4.2: {} css-what@6.2.2: {} @@ -23005,6 +23415,13 @@ snapshots: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 + data-urls@7.0.0: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -23625,7 +24042,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/eslint-parser': 7.25.9(@babel/core@7.29.0)(eslint@9.39.4(jiti@1.21.7)) '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) '@typescript-eslint/parser': 5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) babel-preset-react-app: 10.1.0 confusing-browser-globals: 1.0.11 @@ -23681,11 +24098,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@1.21.7)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) eslint: 9.39.4(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -23757,7 +24174,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.4(jiti@1.21.7)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -23768,7 +24185,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@1.21.7)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -23780,7 +24197,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -23791,7 +24208,7 @@ snapshots: '@typescript-eslint/experimental-utils': 5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) eslint: 9.39.4(jiti@1.21.7) optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) jest: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.7.3))(utf-8-validate@6.0.6) transitivePeerDependencies: - supports-color @@ -24108,6 +24525,8 @@ snapshots: expand-template@2.0.3: {} + expect-type@1.3.0: {} + expect@27.5.1: dependencies: '@jest/types': 27.5.1 @@ -24929,6 +25348,12 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.15.0 + transitivePeerDependencies: + - '@noble/hashes' + html-entities@2.5.2: {} html-entities@2.6.0: {} @@ -26553,6 +26978,32 @@ snapshots: - supports-color - utf-8-validate + jsdom@29.0.2: + dependencies: + '@asamuzakjp/css-color': 5.1.10 + '@asamuzakjp/dom-selector': 7.0.9 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) + '@exodus/bytes': 1.15.0 + css-tree: 3.2.1 + data-urls: 7.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.3.5 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + undici: 7.25.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -26893,6 +27344,8 @@ snapshots: lru-cache@11.2.6: {} + lru-cache@11.3.5: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -27162,6 +27615,8 @@ snapshots: mdn-data@2.0.4: {} + mdn-data@2.27.1: {} + media-typer@0.3.0: {} memfs@3.5.3: @@ -27905,6 +28360,8 @@ snapshots: obuf@1.1.2: {} + obug@2.1.1: {} + octokit@5.0.5: dependencies: '@octokit/app': 16.1.2 @@ -28108,6 +28565,10 @@ snapshots: dependencies: entities: 6.0.1 + parse5@8.0.0: + dependencies: + entities: 6.0.1 + parseurl@1.3.3: {} pascal-case@3.1.2: @@ -30170,6 +30631,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -30320,6 +30783,8 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + stackback@0.0.2: {} + stackframe@1.3.4: {} state-local@1.0.7: {} @@ -30332,6 +30797,8 @@ snapshots: statuses@2.0.2: {} + std-env@4.0.0: {} + stdin-discarder@0.2.2: {} stop-iteration-iterator@1.1.0: @@ -30850,6 +31317,8 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: @@ -30859,14 +31328,22 @@ snapshots: tinyrainbow@2.0.0: {} + tinyrainbow@3.1.0: {} + tinyspy@4.0.4: {} tldts-core@6.1.86: {} + tldts-core@7.0.28: {} + tldts@6.1.86: dependencies: tldts-core: 6.1.86 + tldts@7.0.28: + dependencies: + tldts-core: 7.0.28 + tmpl@1.0.5: {} to-buffer@1.2.2: @@ -30913,6 +31390,10 @@ snapshots: dependencies: tldts: 6.1.86 + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.28 + tr46@0.0.3: {} tr46@1.0.1: @@ -30927,6 +31408,10 @@ snapshots: dependencies: punycode: 2.3.1 + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + tree-kill@1.2.2: {} trim-lines@3.0.1: {} @@ -30949,6 +31434,10 @@ snapshots: dependencies: typescript: 5.5.4 + ts-api-utils@2.4.0(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + ts-api-utils@2.4.0(typescript@5.7.3): dependencies: typescript: 5.7.3 @@ -31169,6 +31658,17 @@ snapshots: dependencies: is-typedarray: 1.0.0 + typescript-eslint@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4): + dependencies: + '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4))(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.5.4) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.5.4) + eslint: 9.39.4(jiti@1.21.7) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + typescript-eslint@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3): dependencies: '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3))(eslint@9.39.4(jiti@1.21.7))(typescript@5.7.3) @@ -31223,6 +31723,8 @@ snapshots: undici-types@7.18.2: {} + undici@7.25.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -31639,6 +32141,35 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 + vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jsdom@29.0.2)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + '@vitest/expect': 4.1.4 + '@vitest/mocker': 4.1.4(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.4 + '@vitest/runner': 4.1.4 + '@vitest/snapshot': 4.1.4 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 22.19.15 + jsdom: 29.0.2 + transitivePeerDependencies: + - msw + vm-browserify@1.1.2: {} void-elements@3.1.0: {} @@ -31709,6 +32240,8 @@ snapshots: webidl-conversions@7.0.0: {} + webidl-conversions@8.0.1: {} + webpack-cli@6.0.1(webpack@5.105.4): dependencies: '@discoveryjs/json-ext': 0.6.3 @@ -31875,11 +32408,21 @@ snapshots: whatwg-mimetype@4.0.0: {} + whatwg-mimetype@5.0.0: {} + whatwg-url@14.2.0: dependencies: tr46: 5.1.1 webidl-conversions: 7.0.0 + whatwg-url@16.0.1: + dependencies: + '@exodus/bytes': 1.15.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -31946,6 +32489,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + widest-line@4.0.1: dependencies: string-width: 5.1.2 diff --git a/react/VITE_POC_NOTES.md b/react/VITE_POC_NOTES.md index 5eec7a9925..9a2935bce4 100644 --- a/react/VITE_POC_NOTES.md +++ b/react/VITE_POC_NOTES.md @@ -77,9 +77,51 @@ Fix: (Each of these gets its own sub-issue under FR-2605.) -- Jest → Vitest migration (FR-2609) - CI pipeline updates (FR-2611) +## Vitest migration (react/ only) — complete (FR-2609) + +`pnpm --prefix ./react run vitest` now passes **856/856 tests** across the migrated suite. The Vitest-semantic differences from Jest noted below have been resolved as part of this PR, so this note no longer implies outstanding failures. + +### What was added + +- `react/vitest.config.ts` — dedicated Vitest config, separate from `vite.config.ts`. Shares the transform pipeline (`@vitejs/plugin-react` + babel-plugin-relay with per-directory `artifactDirectory` + babel-plugin-react-compiler) so tests exercise the same transforms as dev/prod. +- `react/__test__/vitest.jest-compat.ts` — a setup file that aliases `globalThis.jest = vi` so legacy `jest.fn()` / `jest.clearAllMocks()` call sites continue to work. Migration aid only; new tests should use `vi.*` directly. +- `vitest` / `vitest:watch` scripts in `react/package.json`. + +### Bulk migrations applied + +Mechanical `jest.` → `vi.` rewrites across 39 test files (perl one-liner in the commit message): + +- `jest.mock|fn|spyOn|clearAllMocks|resetAllMocks|restoreAllMocks|useFakeTimers|useRealTimers|advanceTimersByTime|runOnlyPendingTimers|runAllTimers|doMock` → `vi.*` +- `jest.Mock` (type cast) → `Mock`, with `import type { Mock } from 'vitest'` added +- Removed `@jest/globals` import lines (Vitest's `globals: true` provides them) + +Without this, Vitest's `vi.mock` hoisting does NOT apply to `jest.mock(...)` calls (Vitest only recognises literal `vi.mock` for hoisting). The rewrites restore mock correctness across 14+ files. + +### Module resolution + +- `src/` baseUrl via regex alias `{ find: /^src\//, replacement: reactSrc + '/' }`. +- `backend.ai-ui/*`, `backend.ai-client-esm` mapped to same mocks Jest used. +- `.svg` plain imports → `__test__/svg.mock.js`; `.svg?react` → `vite-plugin-svgr`. +- `.css` / `.css?raw` → `__test__/rawCss.mock.js` (regex anchored with `^.+` so the entire specifier is replaced; array-form aliases replace the matched portion). + +### Vitest-semantic differences from Jest (resolved in this PR) + +- `react/src/helper/customThemeConfig.test.ts` — was using `Object.defineProperty(process.env, 'NODE_ENV', ...)` to toggle dev/prod; Vitest's `process.env.NODE_ENV` has an immutable descriptor. Migrated to `vi.stubEnv('NODE_ENV', ...)` + `vi.unstubAllEnvs()` in `afterEach`, plus `vi.restoreAllMocks()` between nested describes to avoid event-dispatcher accumulation. +- `react/src/components/MyResourceWithinResourceGroup.test.tsx` and `react/src/hooks/useResourceLimitAndRemaining.test.ts` — bare `vi.mock(path)` without a factory does not produce a `default` export for ESM under Vitest. Fixed by passing an explicit `() => ({ default: vi.fn() })` factory. + +### Performance + +- Vitest run: ~20s wall clock for 848 tests (transform 71s, tests 6s — tests themselves are very fast; the time is transform + import cost, paid only once per file). +- Jest equivalent on the same tree has not been measured in this session; prior expectation was 60-120s. Confidence level: "materially faster" but exact multiplier needs a controlled benchmark. + +### Still open + +- BUI (`packages/backend.ai-ui`) Jest → Vitest migration +- Root `/src` Jest → Vitest migration +- `transformIgnorePatterns` regex in existing `react/jest.config.cjs` can be deleted once the Jest pipeline is fully retired. + ## Production `vite build` + Workbox PWA — landed (FR-2608) `pnpm --prefix ./react run vite:build` now produces a working web build with a generated service worker. Output goes to `react/build/`, same directory the craco pipeline uses. diff --git a/react/__test__/vitest.jest-compat.ts b/react/__test__/vitest.jest-compat.ts new file mode 100644 index 0000000000..d6713e5899 --- /dev/null +++ b/react/__test__/vitest.jest-compat.ts @@ -0,0 +1,27 @@ +// Vitest ↔ Jest compatibility shim for the FR-2609 migration. +// +// Instead of renaming every `jest.fn()`, `jest.mock()`, `jest.spyOn()` etc. +// call across ~39 test files in react/src, we expose `vi` under the name +// `jest` so existing tests run as-is. Newly authored tests should use `vi.*` +// directly — this shim is a migration aid, not a long-term convention. +// +// Vitest's `vi` object is mostly a drop-in for Jest: +// - `jest.fn` → `vi.fn` +// - `jest.mock` → `vi.mock` (behaviour is equivalent; hoisting rules differ +// in corner cases around using captured variables in the factory) +// - `jest.spyOn` → `vi.spyOn` +// - `jest.useFakeTimers` / `jest.useRealTimers` → `vi.useFakeTimers` / +// `vi.useRealTimers` (defaults differ slightly; see vitest docs) +// - `jest.resetAllMocks` / `jest.clearAllMocks` / `jest.restoreAllMocks` → +// `vi.resetAllMocks` / `vi.clearAllMocks` / `vi.restoreAllMocks` +// +// For APIs without a direct `vi` equivalent (e.g. `jest.requireActual`), +// the offending call will throw at test time and we fix it inline there. +import { vi } from 'vitest'; + +// `globals: true` in vitest.config.ts already exposes `vi` as a global. +// The line below ALSO exposes it as `jest` so prior Jest-style calls still +// resolve. Both globals co-exist; no name collision since `jest` is not +// otherwise defined under Vitest. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).jest = vi; diff --git a/react/package.json b/react/package.json index 2ea2238e80..e917449de6 100644 --- a/react/package.json +++ b/react/package.json @@ -90,6 +90,8 @@ "start": "NODE_OPTIONS='--max-old-space-size=4096' craco start", "vite:dev": "vite", "vite:build": "vite build", + "vitest": "vitest run", + "vitest:watch": "vitest", "build": "pnpm run build:only && cp -r ./build/* ../build/web/", "build:only": "NODE_OPTIONS='--max-old-space-size=4096' pnpm run relay && NODE_OPTIONS='--max-old-space-size=4096' craco build", "test": "NODE_OPTIONS='$NODE_OPTIONS --no-deprecation --experimental-vm-modules' jest", @@ -155,6 +157,7 @@ "jest": "catalog:", "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "catalog:", + "jsdom": "^29.0.2", "nodemon": "^3.1.14", "prop-types": "^15.8.1", "react-dev-utils": "^12.0.1", @@ -169,6 +172,7 @@ "vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-pwa": "^1.2.0", "vite-plugin-svgr": "^4.5.0", + "vitest": "^4.1.4", "webpack": "catalog:", "workbox-webpack-plugin": "^7.4.0" } diff --git a/react/src/components/MyResourceWithinResourceGroup.test.tsx b/react/src/components/MyResourceWithinResourceGroup.test.tsx index 6ec5c3fc96..27e4446cd1 100644 --- a/react/src/components/MyResourceWithinResourceGroup.test.tsx +++ b/react/src/components/MyResourceWithinResourceGroup.test.tsx @@ -11,13 +11,13 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; // Mock all the required hooks and dependencies -jest.mock('react-i18next', () => ({ +vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key, }), })); -jest.mock('antd', () => ({ +vi.mock('antd', () => ({ Segmented: ({ children }: any) => (
highlighted');
+ vi.clearAllMocks();
+ (useBAILogger as Mock).mockReturnValue({ logger: mockLogger });
+ (useThemeMode as Mock).mockReturnValue({ isDarkMode: false });
+ (codeToHtml as Mock).mockResolvedValue('highlighted');
});
describe('Basic Functionality', () => {
@@ -71,7 +72,7 @@ describe('useHighlight', () => {
describe('Theme Mode Support', () => {
it('should use dark theme when dark mode is enabled', async () => {
- (useThemeMode as jest.Mock).mockReturnValue({ isDarkMode: true });
+ (useThemeMode as Mock).mockReturnValue({ isDarkMode: true });
const { result } = renderHook(() =>
useHighlight('const x = 1;', 'javascript'),
@@ -103,8 +104,8 @@ describe('useHighlight', () => {
const testLanguages = ['javascript', 'typescript', 'python', 'java'];
for (const lang of testLanguages) {
- jest.clearAllMocks();
- (codeToHtml as jest.Mock).mockResolvedValue('highlighted');
+ vi.clearAllMocks();
+ (codeToHtml as Mock).mockResolvedValue('highlighted');
const { result } = renderHook(() => useHighlight('test code', lang));
@@ -121,8 +122,8 @@ describe('useHighlight', () => {
const testLanguages = ['html', 'xml', 'markdown', 'yaml'];
for (const lang of testLanguages) {
- jest.clearAllMocks();
- (codeToHtml as jest.Mock).mockResolvedValue('highlighted');
+ vi.clearAllMocks();
+ (codeToHtml as Mock).mockResolvedValue('highlighted');
const { result } = renderHook(() => useHighlight('test markup', lang));
@@ -139,8 +140,8 @@ describe('useHighlight', () => {
const testLanguages = ['bash', 'shell', 'sh'];
for (const lang of testLanguages) {
- jest.clearAllMocks();
- (codeToHtml as jest.Mock).mockResolvedValue('highlighted');
+ vi.clearAllMocks();
+ (codeToHtml as Mock).mockResolvedValue('highlighted');
const { result } = renderHook(() => useHighlight('echo "test"', lang));
@@ -161,8 +162,8 @@ describe('useHighlight', () => {
];
for (const { alias, expected } of testAliases) {
- jest.clearAllMocks();
- (codeToHtml as jest.Mock).mockResolvedValue('highlighted');
+ vi.clearAllMocks();
+ (codeToHtml as Mock).mockResolvedValue('highlighted');
const { result } = renderHook(() => useHighlight('test code', alias));
diff --git a/react/src/hooks/useKeyboardShortcut.test.ts b/react/src/hooks/useKeyboardShortcut.test.ts
index 53153779e3..6b7bdccd29 100644
--- a/react/src/hooks/useKeyboardShortcut.test.ts
+++ b/react/src/hooks/useKeyboardShortcut.test.ts
@@ -1,9 +1,10 @@
import useKeyboardShortcut from './useKeyboardShortcut';
import { renderHook } from '@testing-library/react';
+import type { Mock } from 'vitest';
// Mock ahooks useEventListener
-jest.mock('ahooks', () => ({
- useEventListener: jest.fn((event, handler) => {
+vi.mock('ahooks', () => ({
+ useEventListener: vi.fn((event, handler) => {
// Store handler for testing
(global as any).__eventListeners = (global as any).__eventListeners || {};
(global as any).__eventListeners[event] = handler;
@@ -11,10 +12,10 @@ jest.mock('ahooks', () => ({
}));
describe('useKeyboardShortcut', () => {
- let mockHandler: jest.Mock;
+ let mockHandler: Mock;
beforeEach(() => {
- mockHandler = jest.fn();
+ mockHandler = vi.fn();
// Clear stored event listeners
(global as any).__eventListeners = {};
// Clear DOM
@@ -22,7 +23,7 @@ describe('useKeyboardShortcut', () => {
});
afterEach(() => {
- jest.clearAllMocks();
+ vi.clearAllMocks();
});
const triggerKeydown = (options: Partial