From 27adca6701d44cf938ce57ebe4fa50262bfe62b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:03:03 +0000 Subject: [PATCH 01/16] Initial plan From 76655725486253ac28d75a5717d00d25598c0074 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:09:22 +0000 Subject: [PATCH 02/16] feat: add debug-dump example for issue 9290 Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- examples/debug-dump/.gitignore | 3 + examples/debug-dump/README.md | 116 ++++++++++++++++++ examples/debug-dump/package.json | 13 ++ examples/debug-dump/src/utils/currency.ts | 3 + examples/debug-dump/src/utils/index.ts | 6 + examples/debug-dump/src/utils/location.ts | 3 + examples/debug-dump/src/utils/math.ts | 7 ++ examples/debug-dump/src/utils/time.ts | 3 + examples/debug-dump/src/utils/users.ts | 3 + .../debug-dump/test/barrel-import.test.ts | 10 ++ .../debug-dump/test/direct-import.test.ts | 10 ++ examples/debug-dump/vitest.config.ts | 13 ++ pnpm-lock.yaml | 9 ++ 13 files changed, 199 insertions(+) create mode 100644 examples/debug-dump/.gitignore create mode 100644 examples/debug-dump/README.md create mode 100644 examples/debug-dump/package.json create mode 100644 examples/debug-dump/src/utils/currency.ts create mode 100644 examples/debug-dump/src/utils/index.ts create mode 100644 examples/debug-dump/src/utils/location.ts create mode 100644 examples/debug-dump/src/utils/math.ts create mode 100644 examples/debug-dump/src/utils/time.ts create mode 100644 examples/debug-dump/src/utils/users.ts create mode 100644 examples/debug-dump/test/barrel-import.test.ts create mode 100644 examples/debug-dump/test/direct-import.test.ts create mode 100644 examples/debug-dump/vitest.config.ts diff --git a/examples/debug-dump/.gitignore b/examples/debug-dump/.gitignore new file mode 100644 index 000000000000..1a3232fd0a10 --- /dev/null +++ b/examples/debug-dump/.gitignore @@ -0,0 +1,3 @@ +.vitest-dump +node_modules +coverage diff --git a/examples/debug-dump/README.md b/examples/debug-dump/README.md new file mode 100644 index 000000000000..86ffcaf51c69 --- /dev/null +++ b/examples/debug-dump/README.md @@ -0,0 +1,116 @@ +# VITEST_DEBUG_DUMP Example + +This example demonstrates the `VITEST_DEBUG_DUMP` feature for debugging file transformation issues in Vitest. + +Related to: https://github.com/vitest-dev/vitest/discussions/9290 + +## What is VITEST_DEBUG_DUMP? + +`VITEST_DEBUG_DUMP` is an environment variable that enables Vitest to write transformed files to the filesystem. This is useful for: + +- Debugging barrel file issues +- Understanding what files are being transformed +- Inspecting the transformed code +- Identifying unnecessary imports and transformations + +## File Structure + +``` +src/ + utils/ + currency.ts - Currency formatting utility + time.ts - Date formatting utility + math.ts - Math utilities + location.ts - Location utilities + users.ts - User utilities + index.ts - Barrel file (exports all utilities) +test/ + barrel-import.test.ts - Test that imports from barrel file + direct-import.test.ts - Test that imports directly +``` + +## Usage + +### Enable dump via environment variable (recommended): + +```bash +VITEST_DEBUG_DUMP=true pnpm test +``` + +This will create a `.vitest-dump` folder in the project root with all transformed files. + +### Enable dump via config: + +Uncomment the configuration in `vitest.config.ts`: + +```ts +export default defineConfig({ + test: { + server: { + debug: { + dump: true, + }, + }, + }, +}) +``` + +### Custom dump directory: + +```bash +VITEST_DEBUG_DUMP=my-custom-dir pnpm test +``` + +Or in config: + +```ts +export default defineConfig({ + test: { + server: { + debug: { + dump: 'my-custom-dir', + }, + }, + }, +}) +``` + +## Observing the Difference + +### Test with barrel import: + +```bash +VITEST_DEBUG_DUMP=true pnpm test barrel-import +``` + +Check `.vitest-dump/` - you'll see that ALL files in the utils directory are transformed, even though only `currency.ts` is needed. + +### Test with direct import: + +```bash +VITEST_DEBUG_DUMP=true pnpm test direct-import +``` + +Check `.vitest-dump/` - you'll see that ONLY the necessary files are transformed. + +## Expected Behavior + +When using the barrel file import (`../src/utils`): +- ✅ All utility files are transformed (currency, time, math, location, users) +- ⚠️ This causes unnecessary overhead + +When using direct imports (`../src/utils/currency`): +- ✅ Only the needed file is transformed (currency) +- ✅ Better performance + +## Debugging Tips + +1. Run tests with `VITEST_DEBUG_DUMP=true` +2. Inspect the `.vitest-dump` directory +3. Look for files that shouldn't be there +4. Refactor imports to avoid barrel files where possible +5. Use the Vitest UI to visualize the module graph + +## Clean Up + +The `.vitest-dump` directory is temporary and can be safely deleted or added to `.gitignore`. diff --git a/examples/debug-dump/package.json b/examples/debug-dump/package.json new file mode 100644 index 000000000000..e0a80596bd04 --- /dev/null +++ b/examples/debug-dump/package.json @@ -0,0 +1,13 @@ +{ + "name": "@vitest/example-debug-dump", + "type": "module", + "private": true, + "license": "MIT", + "scripts": { + "test": "vitest" + }, + "devDependencies": { + "vite": "latest", + "vitest": "latest" + } +} diff --git a/examples/debug-dump/src/utils/currency.ts b/examples/debug-dump/src/utils/currency.ts new file mode 100644 index 000000000000..45b18b592c54 --- /dev/null +++ b/examples/debug-dump/src/utils/currency.ts @@ -0,0 +1,3 @@ +export function formatCurrency(value: number): string { + return `$${value.toFixed(2)}` +} diff --git a/examples/debug-dump/src/utils/index.ts b/examples/debug-dump/src/utils/index.ts new file mode 100644 index 000000000000..0c4159c261de --- /dev/null +++ b/examples/debug-dump/src/utils/index.ts @@ -0,0 +1,6 @@ +// Barrel file - exports all utilities +export * from './currency' +export * from './time' +export * from './math' +export * from './location' +export * from './users' diff --git a/examples/debug-dump/src/utils/location.ts b/examples/debug-dump/src/utils/location.ts new file mode 100644 index 000000000000..a76054adcbf4 --- /dev/null +++ b/examples/debug-dump/src/utils/location.ts @@ -0,0 +1,3 @@ +export function getLocation(lat: number, lng: number): string { + return `${lat},${lng}` +} diff --git a/examples/debug-dump/src/utils/math.ts b/examples/debug-dump/src/utils/math.ts new file mode 100644 index 000000000000..fb76720eab58 --- /dev/null +++ b/examples/debug-dump/src/utils/math.ts @@ -0,0 +1,7 @@ +export function add(a: number, b: number): number { + return a + b +} + +export function multiply(a: number, b: number): number { + return a * b +} diff --git a/examples/debug-dump/src/utils/time.ts b/examples/debug-dump/src/utils/time.ts new file mode 100644 index 000000000000..8829a69e2cf2 --- /dev/null +++ b/examples/debug-dump/src/utils/time.ts @@ -0,0 +1,3 @@ +export function formatDate(date: Date): string { + return date.toISOString().split('T')[0] +} diff --git a/examples/debug-dump/src/utils/users.ts b/examples/debug-dump/src/utils/users.ts new file mode 100644 index 000000000000..b52a49fb8254 --- /dev/null +++ b/examples/debug-dump/src/utils/users.ts @@ -0,0 +1,3 @@ +export function getUserName(firstName: string, lastName: string): string { + return `${firstName} ${lastName}` +} diff --git a/examples/debug-dump/test/barrel-import.test.ts b/examples/debug-dump/test/barrel-import.test.ts new file mode 100644 index 000000000000..7fc832f714d9 --- /dev/null +++ b/examples/debug-dump/test/barrel-import.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, test } from 'vitest' +// Importing from barrel file - causes unnecessary transformations +import { formatCurrency } from '../src/utils' + +describe('currency formatter (barrel import)', () => { + test('formats currency correctly', () => { + expect(formatCurrency(42.5)).toBe('$42.50') + expect(formatCurrency(100)).toBe('$100.00') + }) +}) diff --git a/examples/debug-dump/test/direct-import.test.ts b/examples/debug-dump/test/direct-import.test.ts new file mode 100644 index 000000000000..95d61b7fa0b9 --- /dev/null +++ b/examples/debug-dump/test/direct-import.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, test } from 'vitest' +// Direct import - only transforms what's needed +import { formatCurrency } from '../src/utils/currency' + +describe('currency formatter (direct import)', () => { + test('formats currency correctly', () => { + expect(formatCurrency(42.5)).toBe('$42.50') + expect(formatCurrency(100)).toBe('$100.00') + }) +}) diff --git a/examples/debug-dump/vitest.config.ts b/examples/debug-dump/vitest.config.ts new file mode 100644 index 000000000000..b55e6fd4c229 --- /dev/null +++ b/examples/debug-dump/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + // Enable dump to see transformed files + // You can also use VITEST_DEBUG_DUMP=true environment variable + // server: { + // debug: { + // dump: true, + // }, + // }, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2103754dbee4..1bf9c6ccf1ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -318,6 +318,15 @@ importers: specifier: workspace:* version: link:../../packages/vitest + examples/debug-dump: + devDependencies: + vite: + specifier: ^7.1.5 + version: 7.1.5(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: + specifier: workspace:* + version: link:../../packages/vitest + examples/fastify: devDependencies: '@vitest/ui': From 0d4abad49ddd482c73b73242a5332294ef345e06 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:10:34 +0000 Subject: [PATCH 03/16] docs: update debug-dump README with actual test results Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- examples/debug-dump/README.md | 60 +++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/examples/debug-dump/README.md b/examples/debug-dump/README.md index 86ffcaf51c69..0636a07633a5 100644 --- a/examples/debug-dump/README.md +++ b/examples/debug-dump/README.md @@ -34,10 +34,10 @@ test/ ### Enable dump via environment variable (recommended): ```bash -VITEST_DEBUG_DUMP=true pnpm test +VITEST_DEBUG_DUMP=true pnpm test --run ``` -This will create a `.vitest-dump` folder in the project root with all transformed files. +This will create a `.vitest-dump/root/` folder in the project root with a `vitest-metadata.json` file containing information about transformed files. ### Enable dump via config: @@ -80,36 +80,68 @@ export default defineConfig({ ### Test with barrel import: ```bash -VITEST_DEBUG_DUMP=true pnpm test barrel-import +VITEST_DEBUG_DUMP=true pnpm test --run barrel-import ``` -Check `.vitest-dump/` - you'll see that ALL files in the utils directory are transformed, even though only `currency.ts` is needed. +Check `.vitest-dump/root/vitest-metadata.json` - you'll see that ALL files in the utils directory are transformed: + +```json +{ + "outline": { + "externalized": 0, + "inlined": 7 // 7 files transformed! + }, + "duration": { + "/test/barrel-import.test.ts": [...], + "/src/utils/index.ts": [...], // barrel file + "/src/utils/currency.ts": [...], // ✓ needed + "/src/utils/time.ts": [...], // ✗ not needed + "/src/utils/math.ts": [...], // ✗ not needed + "/src/utils/location.ts": [...], // ✗ not needed + "/src/utils/users.ts": [...] // ✗ not needed + } +} +``` ### Test with direct import: ```bash -VITEST_DEBUG_DUMP=true pnpm test direct-import +VITEST_DEBUG_DUMP=true pnpm test --run direct-import ``` -Check `.vitest-dump/` - you'll see that ONLY the necessary files are transformed. +Check `.vitest-dump/root/vitest-metadata.json` - you'll see that ONLY the necessary files are transformed: + +```json +{ + "outline": { + "externalized": 0, + "inlined": 2 // Only 2 files! + }, + "duration": { + "/test/direct-import.test.ts": [...], + "/src/utils/currency.ts": [...] // ✓ only what's needed + } +} +``` ## Expected Behavior When using the barrel file import (`../src/utils`): -- ✅ All utility files are transformed (currency, time, math, location, users) -- ⚠️ This causes unnecessary overhead +- ⚠️ All utility files are transformed (currency, time, math, location, users) = **7 files** +- ⚠️ This causes unnecessary overhead and slower test execution When using direct imports (`../src/utils/currency`): -- ✅ Only the needed file is transformed (currency) -- ✅ Better performance +- ✅ Only the needed file is transformed (currency) = **2 files** (test + source) +- ✅ Better performance (71% fewer files transformed in this example) ## Debugging Tips 1. Run tests with `VITEST_DEBUG_DUMP=true` -2. Inspect the `.vitest-dump` directory -3. Look for files that shouldn't be there -4. Refactor imports to avoid barrel files where possible -5. Use the Vitest UI to visualize the module graph +2. Inspect the `.vitest-dump/root/vitest-metadata.json` file +3. Look at the `"inlined"` count and `"duration"` entries to see which files were transformed +4. Files that shouldn't be there indicate barrel file issues +5. Refactor imports to avoid barrel files where possible +6. Use the Vitest UI to visualize the module graph ## Clean Up From 8ff96e7422691bf75b01705b6d6cdaf208778281 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:12:36 +0000 Subject: [PATCH 04/16] style: apply linting fixes to debug-dump example Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- examples/debug-dump/README.md | 22 +++++++++++----------- examples/debug-dump/src/utils/index.ts | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/debug-dump/README.md b/examples/debug-dump/README.md index 0636a07633a5..fd5a3bec4279 100644 --- a/examples/debug-dump/README.md +++ b/examples/debug-dump/README.md @@ -89,16 +89,16 @@ Check `.vitest-dump/root/vitest-metadata.json` - you'll see that ALL files in th { "outline": { "externalized": 0, - "inlined": 7 // 7 files transformed! + "inlined": 7 // 7 files transformed! }, "duration": { - "/test/barrel-import.test.ts": [...], - "/src/utils/index.ts": [...], // barrel file - "/src/utils/currency.ts": [...], // ✓ needed - "/src/utils/time.ts": [...], // ✗ not needed - "/src/utils/math.ts": [...], // ✗ not needed - "/src/utils/location.ts": [...], // ✗ not needed - "/src/utils/users.ts": [...] // ✗ not needed + "/test/barrel-import.test.ts": [/* timing data */], + "/src/utils/index.ts": [/* timing data */], // barrel file + "/src/utils/currency.ts": [/* timing data */], // ✓ needed + "/src/utils/time.ts": [/* timing data */], // ✗ not needed + "/src/utils/math.ts": [/* timing data */], // ✗ not needed + "/src/utils/location.ts": [/* timing data */], // ✗ not needed + "/src/utils/users.ts": [/* timing data */] // ✗ not needed } } ``` @@ -115,11 +115,11 @@ Check `.vitest-dump/root/vitest-metadata.json` - you'll see that ONLY the necess { "outline": { "externalized": 0, - "inlined": 2 // Only 2 files! + "inlined": 2 // Only 2 files! }, "duration": { - "/test/direct-import.test.ts": [...], - "/src/utils/currency.ts": [...] // ✓ only what's needed + "/test/direct-import.test.ts": [/* timing data */], + "/src/utils/currency.ts": [/* timing data */] // ✓ only what's needed } } ``` diff --git a/examples/debug-dump/src/utils/index.ts b/examples/debug-dump/src/utils/index.ts index 0c4159c261de..1832528d56a6 100644 --- a/examples/debug-dump/src/utils/index.ts +++ b/examples/debug-dump/src/utils/index.ts @@ -1,6 +1,6 @@ // Barrel file - exports all utilities export * from './currency' -export * from './time' -export * from './math' export * from './location' +export * from './math' +export * from './time' export * from './users' From f75d236756dacdf1eeffa82eb367c0e7a7a359f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:19:41 +0000 Subject: [PATCH 05/16] feat: add repro for issue 9290 - spy mock coverage issue Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- examples/debug-dump/README.md | 148 ------------------ examples/debug-dump/package.json | 13 -- examples/debug-dump/src/utils/currency.ts | 3 - examples/debug-dump/src/utils/index.ts | 6 - examples/debug-dump/src/utils/location.ts | 3 - examples/debug-dump/src/utils/math.ts | 7 - examples/debug-dump/src/utils/time.ts | 3 - examples/debug-dump/src/utils/users.ts | 3 - .../debug-dump/test/barrel-import.test.ts | 10 -- .../debug-dump/test/direct-import.test.ts | 10 -- examples/debug-dump/vitest.config.ts | 13 -- .../{debug-dump => repro-9290}/.gitignore | 2 +- examples/repro-9290/README.md | 101 ++++++++++++ examples/repro-9290/package.json | 18 +++ examples/repro-9290/src/module.ts | 7 + examples/repro-9290/test/short.test.ts | 11 ++ examples/repro-9290/test/verbose.test.ts | 17 ++ examples/repro-9290/vitest.config.ts | 11 ++ pnpm-lock.yaml | 21 +-- 19 files changed, 178 insertions(+), 229 deletions(-) delete mode 100644 examples/debug-dump/README.md delete mode 100644 examples/debug-dump/package.json delete mode 100644 examples/debug-dump/src/utils/currency.ts delete mode 100644 examples/debug-dump/src/utils/index.ts delete mode 100644 examples/debug-dump/src/utils/location.ts delete mode 100644 examples/debug-dump/src/utils/math.ts delete mode 100644 examples/debug-dump/src/utils/time.ts delete mode 100644 examples/debug-dump/src/utils/users.ts delete mode 100644 examples/debug-dump/test/barrel-import.test.ts delete mode 100644 examples/debug-dump/test/direct-import.test.ts delete mode 100644 examples/debug-dump/vitest.config.ts rename examples/{debug-dump => repro-9290}/.gitignore (100%) create mode 100644 examples/repro-9290/README.md create mode 100644 examples/repro-9290/package.json create mode 100644 examples/repro-9290/src/module.ts create mode 100644 examples/repro-9290/test/short.test.ts create mode 100644 examples/repro-9290/test/verbose.test.ts create mode 100644 examples/repro-9290/vitest.config.ts diff --git a/examples/debug-dump/README.md b/examples/debug-dump/README.md deleted file mode 100644 index fd5a3bec4279..000000000000 --- a/examples/debug-dump/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# VITEST_DEBUG_DUMP Example - -This example demonstrates the `VITEST_DEBUG_DUMP` feature for debugging file transformation issues in Vitest. - -Related to: https://github.com/vitest-dev/vitest/discussions/9290 - -## What is VITEST_DEBUG_DUMP? - -`VITEST_DEBUG_DUMP` is an environment variable that enables Vitest to write transformed files to the filesystem. This is useful for: - -- Debugging barrel file issues -- Understanding what files are being transformed -- Inspecting the transformed code -- Identifying unnecessary imports and transformations - -## File Structure - -``` -src/ - utils/ - currency.ts - Currency formatting utility - time.ts - Date formatting utility - math.ts - Math utilities - location.ts - Location utilities - users.ts - User utilities - index.ts - Barrel file (exports all utilities) -test/ - barrel-import.test.ts - Test that imports from barrel file - direct-import.test.ts - Test that imports directly -``` - -## Usage - -### Enable dump via environment variable (recommended): - -```bash -VITEST_DEBUG_DUMP=true pnpm test --run -``` - -This will create a `.vitest-dump/root/` folder in the project root with a `vitest-metadata.json` file containing information about transformed files. - -### Enable dump via config: - -Uncomment the configuration in `vitest.config.ts`: - -```ts -export default defineConfig({ - test: { - server: { - debug: { - dump: true, - }, - }, - }, -}) -``` - -### Custom dump directory: - -```bash -VITEST_DEBUG_DUMP=my-custom-dir pnpm test -``` - -Or in config: - -```ts -export default defineConfig({ - test: { - server: { - debug: { - dump: 'my-custom-dir', - }, - }, - }, -}) -``` - -## Observing the Difference - -### Test with barrel import: - -```bash -VITEST_DEBUG_DUMP=true pnpm test --run barrel-import -``` - -Check `.vitest-dump/root/vitest-metadata.json` - you'll see that ALL files in the utils directory are transformed: - -```json -{ - "outline": { - "externalized": 0, - "inlined": 7 // 7 files transformed! - }, - "duration": { - "/test/barrel-import.test.ts": [/* timing data */], - "/src/utils/index.ts": [/* timing data */], // barrel file - "/src/utils/currency.ts": [/* timing data */], // ✓ needed - "/src/utils/time.ts": [/* timing data */], // ✗ not needed - "/src/utils/math.ts": [/* timing data */], // ✗ not needed - "/src/utils/location.ts": [/* timing data */], // ✗ not needed - "/src/utils/users.ts": [/* timing data */] // ✗ not needed - } -} -``` - -### Test with direct import: - -```bash -VITEST_DEBUG_DUMP=true pnpm test --run direct-import -``` - -Check `.vitest-dump/root/vitest-metadata.json` - you'll see that ONLY the necessary files are transformed: - -```json -{ - "outline": { - "externalized": 0, - "inlined": 2 // Only 2 files! - }, - "duration": { - "/test/direct-import.test.ts": [/* timing data */], - "/src/utils/currency.ts": [/* timing data */] // ✓ only what's needed - } -} -``` - -## Expected Behavior - -When using the barrel file import (`../src/utils`): -- ⚠️ All utility files are transformed (currency, time, math, location, users) = **7 files** -- ⚠️ This causes unnecessary overhead and slower test execution - -When using direct imports (`../src/utils/currency`): -- ✅ Only the needed file is transformed (currency) = **2 files** (test + source) -- ✅ Better performance (71% fewer files transformed in this example) - -## Debugging Tips - -1. Run tests with `VITEST_DEBUG_DUMP=true` -2. Inspect the `.vitest-dump/root/vitest-metadata.json` file -3. Look at the `"inlined"` count and `"duration"` entries to see which files were transformed -4. Files that shouldn't be there indicate barrel file issues -5. Refactor imports to avoid barrel files where possible -6. Use the Vitest UI to visualize the module graph - -## Clean Up - -The `.vitest-dump` directory is temporary and can be safely deleted or added to `.gitignore`. diff --git a/examples/debug-dump/package.json b/examples/debug-dump/package.json deleted file mode 100644 index e0a80596bd04..000000000000 --- a/examples/debug-dump/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "@vitest/example-debug-dump", - "type": "module", - "private": true, - "license": "MIT", - "scripts": { - "test": "vitest" - }, - "devDependencies": { - "vite": "latest", - "vitest": "latest" - } -} diff --git a/examples/debug-dump/src/utils/currency.ts b/examples/debug-dump/src/utils/currency.ts deleted file mode 100644 index 45b18b592c54..000000000000 --- a/examples/debug-dump/src/utils/currency.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function formatCurrency(value: number): string { - return `$${value.toFixed(2)}` -} diff --git a/examples/debug-dump/src/utils/index.ts b/examples/debug-dump/src/utils/index.ts deleted file mode 100644 index 1832528d56a6..000000000000 --- a/examples/debug-dump/src/utils/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Barrel file - exports all utilities -export * from './currency' -export * from './location' -export * from './math' -export * from './time' -export * from './users' diff --git a/examples/debug-dump/src/utils/location.ts b/examples/debug-dump/src/utils/location.ts deleted file mode 100644 index a76054adcbf4..000000000000 --- a/examples/debug-dump/src/utils/location.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function getLocation(lat: number, lng: number): string { - return `${lat},${lng}` -} diff --git a/examples/debug-dump/src/utils/math.ts b/examples/debug-dump/src/utils/math.ts deleted file mode 100644 index fb76720eab58..000000000000 --- a/examples/debug-dump/src/utils/math.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function add(a: number, b: number): number { - return a + b -} - -export function multiply(a: number, b: number): number { - return a * b -} diff --git a/examples/debug-dump/src/utils/time.ts b/examples/debug-dump/src/utils/time.ts deleted file mode 100644 index 8829a69e2cf2..000000000000 --- a/examples/debug-dump/src/utils/time.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function formatDate(date: Date): string { - return date.toISOString().split('T')[0] -} diff --git a/examples/debug-dump/src/utils/users.ts b/examples/debug-dump/src/utils/users.ts deleted file mode 100644 index b52a49fb8254..000000000000 --- a/examples/debug-dump/src/utils/users.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function getUserName(firstName: string, lastName: string): string { - return `${firstName} ${lastName}` -} diff --git a/examples/debug-dump/test/barrel-import.test.ts b/examples/debug-dump/test/barrel-import.test.ts deleted file mode 100644 index 7fc832f714d9..000000000000 --- a/examples/debug-dump/test/barrel-import.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { describe, expect, test } from 'vitest' -// Importing from barrel file - causes unnecessary transformations -import { formatCurrency } from '../src/utils' - -describe('currency formatter (barrel import)', () => { - test('formats currency correctly', () => { - expect(formatCurrency(42.5)).toBe('$42.50') - expect(formatCurrency(100)).toBe('$100.00') - }) -}) diff --git a/examples/debug-dump/test/direct-import.test.ts b/examples/debug-dump/test/direct-import.test.ts deleted file mode 100644 index 95d61b7fa0b9..000000000000 --- a/examples/debug-dump/test/direct-import.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { describe, expect, test } from 'vitest' -// Direct import - only transforms what's needed -import { formatCurrency } from '../src/utils/currency' - -describe('currency formatter (direct import)', () => { - test('formats currency correctly', () => { - expect(formatCurrency(42.5)).toBe('$42.50') - expect(formatCurrency(100)).toBe('$100.00') - }) -}) diff --git a/examples/debug-dump/vitest.config.ts b/examples/debug-dump/vitest.config.ts deleted file mode 100644 index b55e6fd4c229..000000000000 --- a/examples/debug-dump/vitest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - // Enable dump to see transformed files - // You can also use VITEST_DEBUG_DUMP=true environment variable - // server: { - // debug: { - // dump: true, - // }, - // }, - }, -}) diff --git a/examples/debug-dump/.gitignore b/examples/repro-9290/.gitignore similarity index 100% rename from examples/debug-dump/.gitignore rename to examples/repro-9290/.gitignore index 1a3232fd0a10..e9d5142493d1 100644 --- a/examples/debug-dump/.gitignore +++ b/examples/repro-9290/.gitignore @@ -1,3 +1,3 @@ +coverage .vitest-dump node_modules -coverage diff --git a/examples/repro-9290/README.md b/examples/repro-9290/README.md new file mode 100644 index 000000000000..326476df5f55 --- /dev/null +++ b/examples/repro-9290/README.md @@ -0,0 +1,101 @@ +# Reproduction for Issue 9290 + +Related to: https://github.com/vitest-dev/vitest/discussions/9290 + +## Problem + +When using `vi.mock('./module', { spy: true })`, coverage information is **not collected** for the mocked module. However, using the more verbose manual spy approach **does collect** coverage. + +This is problematic because both approaches execute the actual module code, so they should both collect coverage information. + +## File Structure + +``` +src/ + module.ts - Module to be mocked and tested +test/ + short.test.ts - Uses { spy: true } (DOES NOT collect coverage ❌) + verbose.test.ts - Uses manual spy setup (DOES collect coverage ✅) +``` + +## Reproduction Steps + +### 1. Run test with `{ spy: true }` approach + +```bash +pnpm run coverage:short +``` + +**Expected**: Coverage should be collected for `src/module.ts` +**Actual**: No coverage is collected ❌ + +### 2. Run test with manual spy approach + +```bash +pnpm run coverage:verbose +``` + +**Expected**: Coverage should be collected for `src/module.ts` +**Actual**: Coverage IS collected ✅ + +## The Issue + +### Approach 1: Using `{ spy: true }` (doesn't collect coverage) + +```ts +import something from './module' + +vi.mock('./module', { spy: true }) + +test('test', () => { + something(5) // Code executes but coverage is not collected ❌ +}) +``` + +### Approach 2: Manual spy (does collect coverage) + +```ts +import something from './module' + +vi.mock('./module', async () => { + const actual = await vi.importActual('./module') + return { + ...actual, + default: vi.spyOn(actual, 'default'), + } +}) + +test('test', () => { + something(5) // Code executes and coverage IS collected ✅ +}) +``` + +## Expected Behavior + +Both approaches should collect coverage information since they both: +1. Execute the actual module code +2. Spy on the module's exports +3. Allow the test to verify the code was called + +The `{ spy: true }` option is supposed to be a convenient shorthand for the manual spy approach, so they should behave identically with respect to coverage collection. + +## Impact + +This inconsistency means developers have to: +- Use the more verbose approach to get accurate coverage reports +- This defeats the purpose of the convenient `{ spy: true }` option +- Coverage reports are inaccurate when using `{ spy: true }` +- Developers may not realize their coverage is incomplete + +## Running the Example + +```bash +# Run both tests normally +pnpm test + +# Run with coverage to see the difference +pnpm run coverage:short # No coverage collected +pnpm run coverage:verbose # Coverage collected +``` + +Look at the coverage output to see that `src/module.ts` is only covered when using the verbose approach. diff --git a/examples/repro-9290/package.json b/examples/repro-9290/package.json new file mode 100644 index 000000000000..63dde445a2b9 --- /dev/null +++ b/examples/repro-9290/package.json @@ -0,0 +1,18 @@ +{ + "name": "@vitest/example-repro-9290", + "type": "module", + "private": true, + "license": "MIT", + "scripts": { + "test": "vitest", + "test:short": "vitest run short", + "test:verbose": "vitest run verbose", + "coverage:short": "vitest run short --coverage", + "coverage:verbose": "vitest run verbose --coverage" + }, + "devDependencies": { + "@vitest/coverage-v8": "workspace:*", + "vite": "latest", + "vitest": "workspace:*" + } +} diff --git a/examples/repro-9290/src/module.ts b/examples/repro-9290/src/module.ts new file mode 100644 index 000000000000..15bc974cef89 --- /dev/null +++ b/examples/repro-9290/src/module.ts @@ -0,0 +1,7 @@ +export default function myModule(value: number): number { + return value * 2 +} + +export function anotherFunction(value: number): number { + return value + 10 +} diff --git a/examples/repro-9290/test/short.test.ts b/examples/repro-9290/test/short.test.ts new file mode 100644 index 000000000000..f27147a51e90 --- /dev/null +++ b/examples/repro-9290/test/short.test.ts @@ -0,0 +1,11 @@ +import { expect, test, vi } from 'vitest' +import something from '../src/module' + +// Using { spy: true } - this should collect coverage but currently doesn't +vi.mock('../src/module', { spy: true }) + +test('should spy on module and collect coverage with spy: true', () => { + const result = something(5) + expect(result).toBe(10) + expect(something).toHaveBeenCalledWith(5) +}) diff --git a/examples/repro-9290/test/verbose.test.ts b/examples/repro-9290/test/verbose.test.ts new file mode 100644 index 000000000000..415c620bf3fa --- /dev/null +++ b/examples/repro-9290/test/verbose.test.ts @@ -0,0 +1,17 @@ +import { expect, test, vi } from 'vitest' +import something from '../src/module' + +// Manual spy approach - this DOES collect coverage +vi.mock('../src/module', async () => { + const actual = await vi.importActual('../src/module') + return { + ...actual, + default: vi.spyOn(actual, 'default'), + } +}) + +test('should spy on module and collect coverage with manual spy', () => { + const result = something(5) + expect(result).toBe(10) + expect(something).toHaveBeenCalledWith(5) +}) diff --git a/examples/repro-9290/vitest.config.ts b/examples/repro-9290/vitest.config.ts new file mode 100644 index 000000000000..de24dd39c727 --- /dev/null +++ b/examples/repro-9290/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + coverage: { + provider: 'v8', + reporter: ['text', 'html', 'json'], + include: ['src/**/*.ts'], + }, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bf9c6ccf1ed..70b4c4356576 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -318,15 +318,6 @@ importers: specifier: workspace:* version: link:../../packages/vitest - examples/debug-dump: - devDependencies: - vite: - specifier: ^7.1.5 - version: 7.1.5(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2) - vitest: - specifier: workspace:* - version: link:../../packages/vitest - examples/fastify: devDependencies: '@vitest/ui': @@ -445,6 +436,18 @@ importers: specifier: workspace:* version: link:../../packages/vitest + examples/repro-9290: + devDependencies: + '@vitest/coverage-v8': + specifier: workspace:* + version: link:../../packages/coverage-v8 + vite: + specifier: ^7.1.5 + version: 7.1.5(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: + specifier: workspace:* + version: link:../../packages/vitest + examples/typecheck: devDependencies: '@types/node': From 8507ab09f59ef5e96342f45e4e4b6fa15984f869 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:24:20 +0000 Subject: [PATCH 06/16] docs: fix reference from issue to discussion in README Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- examples/repro-9290/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/repro-9290/README.md b/examples/repro-9290/README.md index 326476df5f55..1c30746e2bb7 100644 --- a/examples/repro-9290/README.md +++ b/examples/repro-9290/README.md @@ -1,4 +1,4 @@ -# Reproduction for Issue 9290 +# Reproduction for Discussion 9290 Related to: https://github.com/vitest-dev/vitest/discussions/9290 From a5b671b7e5a207cbb0693b827b39f781e148292a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 28 Jan 2026 17:16:24 +0900 Subject: [PATCH 07/16] chore: lockfile --- pnpm-lock.yaml | 87 ++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16f066b3aa94..332f512172b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -476,8 +476,8 @@ importers: specifier: workspace:* version: link:../../packages/coverage-v8 vite: - specifier: ^7.1.5 - version: 7.1.5(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.2) + specifier: 7.1.5 + version: 7.1.5(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: workspace:* version: link:../../packages/vitest @@ -10218,7 +10218,7 @@ snapshots: '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -10244,7 +10244,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.6) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -10259,7 +10259,7 @@ snapshots: '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.3 lodash.debounce: 4.0.8 @@ -10271,8 +10271,8 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -10299,15 +10299,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.6)': - dependencies: - '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 @@ -10319,7 +10310,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 '@babel/helper-plugin-utils@7.27.1': {} @@ -10328,7 +10319,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10337,14 +10328,14 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -10358,9 +10349,9 @@ snapshots: '@babel/helper-wrap-function@7.28.3': dependencies: - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/template': 7.28.6 + '@babel/traverse': 7.28.6 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -10394,7 +10385,7 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10421,7 +10412,7 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10455,14 +10446,14 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.6) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.27.1 + '@babel/helper-module-imports': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.6) transitivePeerDependencies: @@ -10498,11 +10489,11 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.6) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10510,13 +10501,13 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/template': 7.27.2 + '@babel/template': 7.28.6 '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10571,9 +10562,9 @@ snapshots: '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10600,7 +10591,7 @@ snapshots: '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.6) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -10608,7 +10599,7 @@ snapshots: '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.6) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -10616,17 +10607,17 @@ snapshots: '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.6) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.6) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -10655,11 +10646,11 @@ snapshots: '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.6)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.6) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.6) - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.28.6 transitivePeerDependencies: - supports-color @@ -10790,9 +10781,9 @@ snapshots: '@babel/preset-env@7.28.5(@babel/core@7.28.6)': dependencies: - '@babel/compat-data': 7.28.5 + '@babel/compat-data': 7.28.6 '@babel/core': 7.28.6 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.6) @@ -10868,7 +10859,7 @@ snapshots: dependencies: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.5 + '@babel/types': 7.28.6 esutils: 2.0.3 '@babel/runtime@7.26.0': @@ -12411,7 +12402,7 @@ snapshots: '@rollup/plugin-babel@5.3.1(@babel/core@7.28.6)(@types/babel__core@7.20.5)(rollup@4.56.0)': dependencies: '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.27.1 + '@babel/helper-module-imports': 7.28.6 '@rollup/pluginutils': 3.1.0(rollup@4.56.0) rollup: 4.56.0 optionalDependencies: @@ -13826,7 +13817,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.6): dependencies: - '@babel/compat-data': 7.28.5 + '@babel/compat-data': 7.28.6 '@babel/core': 7.28.6 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.6) semver: 6.3.1 @@ -16258,8 +16249,8 @@ snapshots: magicast@0.3.5: dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 source-map-js: 1.2.1 optional: true From e1730f0c3ed1c4e721ef8f904eef75a7d8cf9744 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 28 Jan 2026 17:56:47 +0900 Subject: [PATCH 08/16] fix: fix coverage of `vi.mock({ spy: true })` module --- packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts b/packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts index f5e45ea53306..76fb1ed4c1d6 100644 --- a/packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts +++ b/packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts @@ -315,7 +315,8 @@ export class VitestModuleEvaluator implements ModuleEvaluator { )})=>{{` const wrappedCode = `${codeDefinition}${code}\n}}` const options = { - filename: module.id, + // use original id for auto spy module (vi.mock(..., { spy: true })) + filename: module.id.startsWith('mock:') ? module.id.slice(5) : module.id, lineOffset: 0, columnOffset: -codeDefinition.length, } From 44de34f186f0e37a93f988a29abb51b47293f34e Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 28 Jan 2026 19:23:02 +0900 Subject: [PATCH 09/16] test(coverage): add test for `vi.mock({ spy: true })` coverage Verifies that coverage is properly collected for modules mocked with `vi.mock({ spy: true })` (#9290). Co-Authored-By: Claude Opus 4.5 --- .../fixtures/src/autospy-target.ts | 7 ++++ .../fixtures/test/autospy-fixture.test.ts | 10 +++++ test/coverage-test/test/autospy.test.ts | 41 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 test/coverage-test/fixtures/src/autospy-target.ts create mode 100644 test/coverage-test/fixtures/test/autospy-fixture.test.ts create mode 100644 test/coverage-test/test/autospy.test.ts diff --git a/test/coverage-test/fixtures/src/autospy-target.ts b/test/coverage-test/fixtures/src/autospy-target.ts new file mode 100644 index 000000000000..bf28b98258e0 --- /dev/null +++ b/test/coverage-test/fixtures/src/autospy-target.ts @@ -0,0 +1,7 @@ +export function double(value: number): number { + return value * 2 +} + +export function triple(value: number): number { + return value * 3 +} diff --git a/test/coverage-test/fixtures/test/autospy-fixture.test.ts b/test/coverage-test/fixtures/test/autospy-fixture.test.ts new file mode 100644 index 000000000000..510563759a79 --- /dev/null +++ b/test/coverage-test/fixtures/test/autospy-fixture.test.ts @@ -0,0 +1,10 @@ +import { expect, test, vi } from 'vitest' +import { double, triple } from '../src/autospy-target' + +vi.mock('../src/autospy-target', { spy: true }) + +test('autospy calls original and can be spied on', () => { + expect(double(5)).toBe(10) + expect(double).toHaveBeenCalledWith(5) + expect(triple).not.toHaveBeenCalled() +}) diff --git a/test/coverage-test/test/autospy.test.ts b/test/coverage-test/test/autospy.test.ts new file mode 100644 index 000000000000..e9a6c79d73c5 --- /dev/null +++ b/test/coverage-test/test/autospy.test.ts @@ -0,0 +1,41 @@ +import { expect } from 'vitest' +import { readCoverageMap, runVitest, test } from '../utils' + +// TODO: browser mode? +test('vi.mock({ spy: true }) collects coverage (#9290)', async () => { + await runVitest({ + include: ['fixtures/test/autospy-fixture.test.ts'], + coverage: { + reporter: 'json', + include: ['fixtures/src/autospy-target.ts'], + }, + }) + + const coverageMap = await readCoverageMap() + expect(coverageMap).toMatchInlineSnapshot(` + { + "branches": "0/0 (100%)", + "functions": "1/2 (50%)", + "lines": "1/2 (50%)", + "statements": "1/2 (50%)", + } + `) + + const coverage = coverageMap.fileCoverageFor('/fixtures/src/autospy-target.ts') + const functionCoverage = Object.keys(coverage.fnMap) + .map(index => ({ name: coverage.fnMap[index].name, hits: coverage.f[index] })) + .sort((a, b) => a.name.localeCompare(b.name)) + + expect(functionCoverage).toMatchInlineSnapshot(` + [ + { + "hits": 1, + "name": "double", + }, + { + "hits": 0, + "name": "triple", + }, + ] + `) +}) From 0a0043b2b7dd27e28a993b05471b02f2823adc13 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:03:39 +0900 Subject: [PATCH 10/16] test: tweak --- .../src/{autospy-target.ts => mock-autospy-target.ts} | 0 ...autospy-fixture.test.ts => mock-autospy-fixture.test.ts} | 4 ++-- .../test/{autospy.test.ts => mock-autospy.test.ts} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename test/coverage-test/fixtures/src/{autospy-target.ts => mock-autospy-target.ts} (100%) rename test/coverage-test/fixtures/test/{autospy-fixture.test.ts => mock-autospy-fixture.test.ts} (64%) rename test/coverage-test/test/{autospy.test.ts => mock-autospy.test.ts} (86%) diff --git a/test/coverage-test/fixtures/src/autospy-target.ts b/test/coverage-test/fixtures/src/mock-autospy-target.ts similarity index 100% rename from test/coverage-test/fixtures/src/autospy-target.ts rename to test/coverage-test/fixtures/src/mock-autospy-target.ts diff --git a/test/coverage-test/fixtures/test/autospy-fixture.test.ts b/test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts similarity index 64% rename from test/coverage-test/fixtures/test/autospy-fixture.test.ts rename to test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts index 510563759a79..340e10d796c5 100644 --- a/test/coverage-test/fixtures/test/autospy-fixture.test.ts +++ b/test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts @@ -1,7 +1,7 @@ import { expect, test, vi } from 'vitest' -import { double, triple } from '../src/autospy-target' +import { double, triple } from '../src/mock-autospy-target' -vi.mock('../src/autospy-target', { spy: true }) +vi.mock(import('../src/mock-autospy-target'), { spy: true }) test('autospy calls original and can be spied on', () => { expect(double(5)).toBe(10) diff --git a/test/coverage-test/test/autospy.test.ts b/test/coverage-test/test/mock-autospy.test.ts similarity index 86% rename from test/coverage-test/test/autospy.test.ts rename to test/coverage-test/test/mock-autospy.test.ts index e9a6c79d73c5..3388d61fca77 100644 --- a/test/coverage-test/test/autospy.test.ts +++ b/test/coverage-test/test/mock-autospy.test.ts @@ -4,10 +4,10 @@ import { readCoverageMap, runVitest, test } from '../utils' // TODO: browser mode? test('vi.mock({ spy: true }) collects coverage (#9290)', async () => { await runVitest({ - include: ['fixtures/test/autospy-fixture.test.ts'], + include: ['fixtures/test/mock-autospy-fixture.test.ts'], coverage: { reporter: 'json', - include: ['fixtures/src/autospy-target.ts'], + include: ['fixtures/src/mock-autospy-target.ts'], }, }) @@ -21,7 +21,7 @@ test('vi.mock({ spy: true }) collects coverage (#9290)', async () => { } `) - const coverage = coverageMap.fileCoverageFor('/fixtures/src/autospy-target.ts') + const coverage = coverageMap.fileCoverageFor('/fixtures/src/mock-autospy-target.ts') const functionCoverage = Object.keys(coverage.fnMap) .map(index => ({ name: coverage.fnMap[index].name, hits: coverage.f[index] })) .sort((a, b) => a.name.localeCompare(b.name)) From 49f1589f7d06e9efca23914a2ca396324ab98350 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:06:31 +0900 Subject: [PATCH 11/16] test: browser mode --- test/coverage-test/test/mock-autospy.test.ts | 3 +-- test/coverage-test/vitest.config.ts | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/coverage-test/test/mock-autospy.test.ts b/test/coverage-test/test/mock-autospy.test.ts index 3388d61fca77..a9dcc640f417 100644 --- a/test/coverage-test/test/mock-autospy.test.ts +++ b/test/coverage-test/test/mock-autospy.test.ts @@ -1,8 +1,7 @@ import { expect } from 'vitest' import { readCoverageMap, runVitest, test } from '../utils' -// TODO: browser mode? -test('vi.mock({ spy: true }) collects coverage (#9290)', async () => { +test('vi.mock({ spy: true }) collects coverage of original module', async () => { await runVitest({ include: ['fixtures/test/mock-autospy-fixture.test.ts'], coverage: { diff --git a/test/coverage-test/vitest.config.ts b/test/coverage-test/vitest.config.ts index f75455f42e3b..62ff870256ef 100644 --- a/test/coverage-test/vitest.config.ts +++ b/test/coverage-test/vitest.config.ts @@ -90,6 +90,7 @@ export default defineConfig({ '**/query-param-transforms.test.ts', '**/test/cjs-dependency.test.ts', '**/test/source-maps.test.ts', + '**/test/mock-autospy.test.ts', ], exclude: [FIXTURES], }, @@ -122,6 +123,7 @@ export default defineConfig({ '**/query-param-transforms.test.ts', '**/test/cjs-dependency.test.ts', '**/test/source-maps.test.ts', + '**/test/mock-autospy.test.ts', ], exclude: [FIXTURES], }, From 7715581595533ed86f1c7269bfa6256777913c85 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:36:28 +0900 Subject: [PATCH 12/16] test(coverage): add mock-importActual test for coverage verification Add a parallel test using vi.mock with importOriginal callback to verify coverage collection works similarly to vi.mock({ spy: true }). Co-Authored-By: Claude Opus 4.5 --- ...{mock-autospy-target.ts => mock-target.ts} | 0 .../test/mock-autospy-fixture.test.ts | 4 +- .../test/mock-importActual-fixture.test.ts | 17 ++++++++ test/coverage-test/test/mock-autospy.test.ts | 4 +- .../test/mock-importActual.test.ts | 40 +++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) rename test/coverage-test/fixtures/src/{mock-autospy-target.ts => mock-target.ts} (100%) create mode 100644 test/coverage-test/fixtures/test/mock-importActual-fixture.test.ts create mode 100644 test/coverage-test/test/mock-importActual.test.ts diff --git a/test/coverage-test/fixtures/src/mock-autospy-target.ts b/test/coverage-test/fixtures/src/mock-target.ts similarity index 100% rename from test/coverage-test/fixtures/src/mock-autospy-target.ts rename to test/coverage-test/fixtures/src/mock-target.ts diff --git a/test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts b/test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts index 340e10d796c5..9a0c310f3331 100644 --- a/test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts +++ b/test/coverage-test/fixtures/test/mock-autospy-fixture.test.ts @@ -1,7 +1,7 @@ import { expect, test, vi } from 'vitest' -import { double, triple } from '../src/mock-autospy-target' +import { double, triple } from '../src/mock-target' -vi.mock(import('../src/mock-autospy-target'), { spy: true }) +vi.mock(import('../src/mock-target'), { spy: true }) test('autospy calls original and can be spied on', () => { expect(double(5)).toBe(10) diff --git a/test/coverage-test/fixtures/test/mock-importActual-fixture.test.ts b/test/coverage-test/fixtures/test/mock-importActual-fixture.test.ts new file mode 100644 index 000000000000..8f00072ba67e --- /dev/null +++ b/test/coverage-test/fixtures/test/mock-importActual-fixture.test.ts @@ -0,0 +1,17 @@ +import { expect, test, vi } from 'vitest' +import { double, triple } from '../src/mock-target' + +// Manual spy approach using importOriginal callback - this collects coverage +vi.mock(import('../src/mock-target'), async (importOriginal) => { + const actual = await importOriginal() + return { + double: vi.fn(actual.double), + triple: vi.fn(actual.triple), + } +}) + +test('importActual calls original and can be spied on', () => { + expect(double(5)).toBe(10) + expect(double).toHaveBeenCalledWith(5) + expect(triple).not.toHaveBeenCalled() +}) diff --git a/test/coverage-test/test/mock-autospy.test.ts b/test/coverage-test/test/mock-autospy.test.ts index a9dcc640f417..2c96964dbec7 100644 --- a/test/coverage-test/test/mock-autospy.test.ts +++ b/test/coverage-test/test/mock-autospy.test.ts @@ -6,7 +6,7 @@ test('vi.mock({ spy: true }) collects coverage of original module', async () => include: ['fixtures/test/mock-autospy-fixture.test.ts'], coverage: { reporter: 'json', - include: ['fixtures/src/mock-autospy-target.ts'], + include: ['fixtures/src/mock-target.ts'], }, }) @@ -20,7 +20,7 @@ test('vi.mock({ spy: true }) collects coverage of original module', async () => } `) - const coverage = coverageMap.fileCoverageFor('/fixtures/src/mock-autospy-target.ts') + const coverage = coverageMap.fileCoverageFor('/fixtures/src/mock-target.ts') const functionCoverage = Object.keys(coverage.fnMap) .map(index => ({ name: coverage.fnMap[index].name, hits: coverage.f[index] })) .sort((a, b) => a.name.localeCompare(b.name)) diff --git a/test/coverage-test/test/mock-importActual.test.ts b/test/coverage-test/test/mock-importActual.test.ts new file mode 100644 index 000000000000..2c7d27c3acbf --- /dev/null +++ b/test/coverage-test/test/mock-importActual.test.ts @@ -0,0 +1,40 @@ +import { expect } from 'vitest' +import { readCoverageMap, runVitest, test } from '../utils' + +test('vi.importActual() collects coverage of original module', async () => { + await runVitest({ + include: ['fixtures/test/mock-importActual-fixture.test.ts'], + coverage: { + reporter: 'json', + include: ['fixtures/src/mock-target.ts'], + }, + }) + + const coverageMap = await readCoverageMap() + expect(coverageMap).toMatchInlineSnapshot(` + { + "branches": "0/0 (100%)", + "functions": "1/2 (50%)", + "lines": "1/2 (50%)", + "statements": "1/2 (50%)", + } + `) + + const coverage = coverageMap.fileCoverageFor('/fixtures/src/mock-target.ts') + const functionCoverage = Object.keys(coverage.fnMap) + .map(index => ({ name: coverage.fnMap[index].name, hits: coverage.f[index] })) + .sort((a, b) => a.name.localeCompare(b.name)) + + expect(functionCoverage).toMatchInlineSnapshot(` + [ + { + "hits": 1, + "name": "double", + }, + { + "hits": 0, + "name": "triple", + }, + ] + `) +}) From 7320efa512c78d4026ea00d068d37b47b288141c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:44:37 +0900 Subject: [PATCH 13/16] chore: repro --- examples/repro-9290/test/short.test.ts | 2 +- examples/repro-9290/test/verbose.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/repro-9290/test/short.test.ts b/examples/repro-9290/test/short.test.ts index f27147a51e90..11a667c80a9f 100644 --- a/examples/repro-9290/test/short.test.ts +++ b/examples/repro-9290/test/short.test.ts @@ -2,7 +2,7 @@ import { expect, test, vi } from 'vitest' import something from '../src/module' // Using { spy: true } - this should collect coverage but currently doesn't -vi.mock('../src/module', { spy: true }) +vi.mock(import('../src/module'), { spy: true }) test('should spy on module and collect coverage with spy: true', () => { const result = something(5) diff --git a/examples/repro-9290/test/verbose.test.ts b/examples/repro-9290/test/verbose.test.ts index 415c620bf3fa..96ac2e6576d4 100644 --- a/examples/repro-9290/test/verbose.test.ts +++ b/examples/repro-9290/test/verbose.test.ts @@ -2,8 +2,8 @@ import { expect, test, vi } from 'vitest' import something from '../src/module' // Manual spy approach - this DOES collect coverage -vi.mock('../src/module', async () => { - const actual = await vi.importActual('../src/module') +vi.mock(import('../src/module'), async (importOriginal) => { + const actual = await importOriginal() return { ...actual, default: vi.spyOn(actual, 'default'), From dedaa93a3ad76c3418a842c6764e5f2ab90032e1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:45:00 +0900 Subject: [PATCH 14/16] chore: remove examples/repro-9290 --- examples/repro-9290/.gitignore | 3 - examples/repro-9290/README.md | 101 ----------------------- examples/repro-9290/package.json | 18 ---- examples/repro-9290/src/module.ts | 7 -- examples/repro-9290/test/short.test.ts | 11 --- examples/repro-9290/test/verbose.test.ts | 17 ---- examples/repro-9290/vitest.config.ts | 11 --- pnpm-lock.yaml | 12 --- 8 files changed, 180 deletions(-) delete mode 100644 examples/repro-9290/.gitignore delete mode 100644 examples/repro-9290/README.md delete mode 100644 examples/repro-9290/package.json delete mode 100644 examples/repro-9290/src/module.ts delete mode 100644 examples/repro-9290/test/short.test.ts delete mode 100644 examples/repro-9290/test/verbose.test.ts delete mode 100644 examples/repro-9290/vitest.config.ts diff --git a/examples/repro-9290/.gitignore b/examples/repro-9290/.gitignore deleted file mode 100644 index e9d5142493d1..000000000000 --- a/examples/repro-9290/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -coverage -.vitest-dump -node_modules diff --git a/examples/repro-9290/README.md b/examples/repro-9290/README.md deleted file mode 100644 index 1c30746e2bb7..000000000000 --- a/examples/repro-9290/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Reproduction for Discussion 9290 - -Related to: https://github.com/vitest-dev/vitest/discussions/9290 - -## Problem - -When using `vi.mock('./module', { spy: true })`, coverage information is **not collected** for the mocked module. However, using the more verbose manual spy approach **does collect** coverage. - -This is problematic because both approaches execute the actual module code, so they should both collect coverage information. - -## File Structure - -``` -src/ - module.ts - Module to be mocked and tested -test/ - short.test.ts - Uses { spy: true } (DOES NOT collect coverage ❌) - verbose.test.ts - Uses manual spy setup (DOES collect coverage ✅) -``` - -## Reproduction Steps - -### 1. Run test with `{ spy: true }` approach - -```bash -pnpm run coverage:short -``` - -**Expected**: Coverage should be collected for `src/module.ts` -**Actual**: No coverage is collected ❌ - -### 2. Run test with manual spy approach - -```bash -pnpm run coverage:verbose -``` - -**Expected**: Coverage should be collected for `src/module.ts` -**Actual**: Coverage IS collected ✅ - -## The Issue - -### Approach 1: Using `{ spy: true }` (doesn't collect coverage) - -```ts -import something from './module' - -vi.mock('./module', { spy: true }) - -test('test', () => { - something(5) // Code executes but coverage is not collected ❌ -}) -``` - -### Approach 2: Manual spy (does collect coverage) - -```ts -import something from './module' - -vi.mock('./module', async () => { - const actual = await vi.importActual('./module') - return { - ...actual, - default: vi.spyOn(actual, 'default'), - } -}) - -test('test', () => { - something(5) // Code executes and coverage IS collected ✅ -}) -``` - -## Expected Behavior - -Both approaches should collect coverage information since they both: -1. Execute the actual module code -2. Spy on the module's exports -3. Allow the test to verify the code was called - -The `{ spy: true }` option is supposed to be a convenient shorthand for the manual spy approach, so they should behave identically with respect to coverage collection. - -## Impact - -This inconsistency means developers have to: -- Use the more verbose approach to get accurate coverage reports -- This defeats the purpose of the convenient `{ spy: true }` option -- Coverage reports are inaccurate when using `{ spy: true }` -- Developers may not realize their coverage is incomplete - -## Running the Example - -```bash -# Run both tests normally -pnpm test - -# Run with coverage to see the difference -pnpm run coverage:short # No coverage collected -pnpm run coverage:verbose # Coverage collected -``` - -Look at the coverage output to see that `src/module.ts` is only covered when using the verbose approach. diff --git a/examples/repro-9290/package.json b/examples/repro-9290/package.json deleted file mode 100644 index 63dde445a2b9..000000000000 --- a/examples/repro-9290/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@vitest/example-repro-9290", - "type": "module", - "private": true, - "license": "MIT", - "scripts": { - "test": "vitest", - "test:short": "vitest run short", - "test:verbose": "vitest run verbose", - "coverage:short": "vitest run short --coverage", - "coverage:verbose": "vitest run verbose --coverage" - }, - "devDependencies": { - "@vitest/coverage-v8": "workspace:*", - "vite": "latest", - "vitest": "workspace:*" - } -} diff --git a/examples/repro-9290/src/module.ts b/examples/repro-9290/src/module.ts deleted file mode 100644 index 15bc974cef89..000000000000 --- a/examples/repro-9290/src/module.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default function myModule(value: number): number { - return value * 2 -} - -export function anotherFunction(value: number): number { - return value + 10 -} diff --git a/examples/repro-9290/test/short.test.ts b/examples/repro-9290/test/short.test.ts deleted file mode 100644 index 11a667c80a9f..000000000000 --- a/examples/repro-9290/test/short.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { expect, test, vi } from 'vitest' -import something from '../src/module' - -// Using { spy: true } - this should collect coverage but currently doesn't -vi.mock(import('../src/module'), { spy: true }) - -test('should spy on module and collect coverage with spy: true', () => { - const result = something(5) - expect(result).toBe(10) - expect(something).toHaveBeenCalledWith(5) -}) diff --git a/examples/repro-9290/test/verbose.test.ts b/examples/repro-9290/test/verbose.test.ts deleted file mode 100644 index 96ac2e6576d4..000000000000 --- a/examples/repro-9290/test/verbose.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { expect, test, vi } from 'vitest' -import something from '../src/module' - -// Manual spy approach - this DOES collect coverage -vi.mock(import('../src/module'), async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - default: vi.spyOn(actual, 'default'), - } -}) - -test('should spy on module and collect coverage with manual spy', () => { - const result = something(5) - expect(result).toBe(10) - expect(something).toHaveBeenCalledWith(5) -}) diff --git a/examples/repro-9290/vitest.config.ts b/examples/repro-9290/vitest.config.ts deleted file mode 100644 index de24dd39c727..000000000000 --- a/examples/repro-9290/vitest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - coverage: { - provider: 'v8', - reporter: ['text', 'html', 'json'], - include: ['src/**/*.ts'], - }, - }, -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 332f512172b1..1a840b66ebe9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -470,18 +470,6 @@ importers: specifier: workspace:* version: link:../../packages/vitest - examples/repro-9290: - devDependencies: - '@vitest/coverage-v8': - specifier: workspace:* - version: link:../../packages/coverage-v8 - vite: - specifier: 7.1.5 - version: 7.1.5(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - vitest: - specifier: workspace:* - version: link:../../packages/vitest - examples/typecheck: devDependencies: '@types/node': From 493e5c6725987077bc40dca3ec94429b7ca5122b Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:50:01 +0900 Subject: [PATCH 15/16] test(coverage): handle browser mode difference in mock-importActual test Browser mode reports 100% coverage due to different collection behavior. Co-Authored-By: Claude Opus 4.5 --- .../test/mock-importActual.test.ts | 62 ++++++++++++------- test/coverage-test/vitest.config.ts | 2 + 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/test/coverage-test/test/mock-importActual.test.ts b/test/coverage-test/test/mock-importActual.test.ts index 2c7d27c3acbf..7dd10cd56227 100644 --- a/test/coverage-test/test/mock-importActual.test.ts +++ b/test/coverage-test/test/mock-importActual.test.ts @@ -1,5 +1,5 @@ import { expect } from 'vitest' -import { readCoverageMap, runVitest, test } from '../utils' +import { isBrowser, readCoverageMap, runVitest, test } from '../utils' test('vi.importActual() collects coverage of original module', async () => { await runVitest({ @@ -11,30 +11,44 @@ test('vi.importActual() collects coverage of original module', async () => { }) const coverageMap = await readCoverageMap() - expect(coverageMap).toMatchInlineSnapshot(` - { - "branches": "0/0 (100%)", - "functions": "1/2 (50%)", - "lines": "1/2 (50%)", - "statements": "1/2 (50%)", - } - `) - const coverage = coverageMap.fileCoverageFor('/fixtures/src/mock-target.ts') - const functionCoverage = Object.keys(coverage.fnMap) - .map(index => ({ name: coverage.fnMap[index].name, hits: coverage.f[index] })) - .sort((a, b) => a.name.localeCompare(b.name)) - - expect(functionCoverage).toMatchInlineSnapshot(` - [ + if (isBrowser()) { + // Browser mode reports 100% due to different coverage collection behavior + expect(coverageMap).toMatchInlineSnapshot(` { - "hits": 1, - "name": "double", - }, + "branches": "0/0 (100%)", + "functions": "2/2 (100%)", + "lines": "2/2 (100%)", + "statements": "2/2 (100%)", + } + `) + } + else { + expect(coverageMap).toMatchInlineSnapshot(` { - "hits": 0, - "name": "triple", - }, - ] - `) + "branches": "0/0 (100%)", + "functions": "1/2 (50%)", + "lines": "1/2 (50%)", + "statements": "1/2 (50%)", + } + `) + + const coverage = coverageMap.fileCoverageFor('/fixtures/src/mock-target.ts') + const functionCoverage = Object.keys(coverage.fnMap) + .map(index => ({ name: coverage.fnMap[index].name, hits: coverage.f[index] })) + .sort((a, b) => a.name.localeCompare(b.name)) + + expect(functionCoverage).toMatchInlineSnapshot(` + [ + { + "hits": 1, + "name": "double", + }, + { + "hits": 0, + "name": "triple", + }, + ] + `) + } }) diff --git a/test/coverage-test/vitest.config.ts b/test/coverage-test/vitest.config.ts index 62ff870256ef..2afd3dcfa879 100644 --- a/test/coverage-test/vitest.config.ts +++ b/test/coverage-test/vitest.config.ts @@ -91,6 +91,7 @@ export default defineConfig({ '**/test/cjs-dependency.test.ts', '**/test/source-maps.test.ts', '**/test/mock-autospy.test.ts', + '**/test/mock-importActual.test.ts', ], exclude: [FIXTURES], }, @@ -124,6 +125,7 @@ export default defineConfig({ '**/test/cjs-dependency.test.ts', '**/test/source-maps.test.ts', '**/test/mock-autospy.test.ts', + '**/test/mock-importActual.test.ts', ], exclude: [FIXTURES], }, From 60c785485e531fca09548e7b6344a411eda5a0b8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 29 Jan 2026 10:51:17 +0900 Subject: [PATCH 16/16] test(coverage): fix mock-importActual for all runners Handle different coverage behavior across runners: - v8-browser and native: 100% - v8, istanbul, istanbul-browser: 50% Co-Authored-By: Claude Opus 4.5 --- test/coverage-test/test/mock-importActual.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/coverage-test/test/mock-importActual.test.ts b/test/coverage-test/test/mock-importActual.test.ts index 7dd10cd56227..0ad1d2b7b972 100644 --- a/test/coverage-test/test/mock-importActual.test.ts +++ b/test/coverage-test/test/mock-importActual.test.ts @@ -1,5 +1,5 @@ import { expect } from 'vitest' -import { isBrowser, readCoverageMap, runVitest, test } from '../utils' +import { isBrowser, isNativeRunner, isV8Provider, readCoverageMap, runVitest, test } from '../utils' test('vi.importActual() collects coverage of original module', async () => { await runVitest({ @@ -12,8 +12,8 @@ test('vi.importActual() collects coverage of original module', async () => { const coverageMap = await readCoverageMap() - if (isBrowser()) { - // Browser mode reports 100% due to different coverage collection behavior + // v8-browser and native runner report 100% due to different coverage collection behavior + if ((isBrowser() && isV8Provider()) || isNativeRunner()) { expect(coverageMap).toMatchInlineSnapshot(` { "branches": "0/0 (100%)",