From 86fad4b42603a5d1c4935b82656d2fd8d2d44bf1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 15 Feb 2026 12:14:43 +0900 Subject: [PATCH] fix: fix ui mode / html reporter and coverage integration (#9626) Co-authored-by: Claude Sonnet 4.5 --- docs/config/coverage.md | 14 ++++- docs/guide/coverage.md | 6 +- packages/browser/src/node/plugin.ts | 57 ++----------------- packages/ui/client/components/Coverage.vue | 6 +- packages/ui/client/composables/attachments.ts | 2 +- packages/ui/client/composables/navigation.ts | 19 +------ packages/ui/client/pages/index.vue | 3 - packages/ui/node/index.ts | 56 ++---------------- packages/ui/node/reporter.ts | 10 ++++ .../vitest/src/node/config/resolveConfig.ts | 25 ++++++++ .../vitest/src/node/config/serializeConfig.ts | 9 +-- packages/vitest/src/node/core.ts | 10 ++-- packages/vitest/src/node/types/coverage.ts | 8 +++ packages/vitest/src/runtime/config.ts | 4 +- test/config/test/public.test.ts | 55 ++++++++++++++++++ test/ui/test/html-report.spec.ts | 29 +++------- test/ui/test/ui.spec.ts | 2 +- 17 files changed, 145 insertions(+), 170 deletions(-) diff --git a/docs/config/coverage.md b/docs/config/coverage.md index 6765eb0eb..6406a0496 100644 --- a/docs/config/coverage.md +++ b/docs/config/coverage.md @@ -89,8 +89,6 @@ Vitest will delete this directory before running tests if `coverage.clean` is en Directory to write coverage report to. -To preview the coverage report in the output of [HTML reporter](/guide/reporters.html#html-reporter), this option must be set as a sub-directory of the html report directory (for example `./html/coverage`). - ## coverage.reporter - **Type:** `string | string[] | [string, {}][]` @@ -395,3 +393,15 @@ Concurrency limit used when processing the coverage results. - **CLI:** `--coverage.customProviderModule=` Specifies the module name or path for the custom coverage provider module. See [Guide - Custom Coverage Provider](/guide/coverage#custom-coverage-provider) for more information. + +## coverage.htmlDir + +- **Type:** `string` +- **Default:** Automatically inferred from `html`, `html-spa`, or `lcov` coverage reporters +- **CLI:** `--coverage.htmlDir=` + +Directory of HTML coverage output to be served in [Vitest UI](/guide/ui) and [HTML reporter](/guide/reporters.html#html-reporter). + +This is automatically configured when using builtin coverage reporters that produce HTML output (`html`, `html-spa`, and `lcov`). Use this option to override with a custom coverage reporting location when using custom coverage reporters. + +Note that setting this option does not change where coverage HTML report is generated. Configure the `coverage.reporter` option to change the directory instead. diff --git a/docs/guide/coverage.md b/docs/guide/coverage.md index c317853ed..faaa47424 100644 --- a/docs/guide/coverage.md +++ b/docs/guide/coverage.md @@ -499,11 +499,9 @@ If code coverage generation is slow on your project, see [Profiling Test Perform ## Vitest UI -You can check your coverage report in [Vitest UI](/guide/ui). +You can check your coverage report in [Vitest UI](/guide/ui) and [HTML reporter](/guide/reporters.html#html-reporter). -Vitest UI will enable coverage report when it is enabled explicitly and the html coverage reporter is present, otherwise it will not be available: -- enable `coverage.enabled=true` in your configuration file or run Vitest with `--coverage.enabled=true` flag -- add `html` to the `coverage.reporter` list: you can also enable `subdir` option to put coverage report in a subdirectory +This is integrated with builtin coverage reporters with HTML output (`html`, `html-spa`, and `lcov` reporters). `html` reporter is enabled by default and this works out of the box. To integrate with custom reporters, you can configure [`coverage.htmlDir`](/config/coverage#coverage-htmldir). html coverage activation in Vitest UI html coverage activation in Vitest UI diff --git a/packages/browser/src/node/plugin.ts b/packages/browser/src/node/plugin.ts index 5380a76e7..91ed57831 100644 --- a/packages/browser/src/node/plugin.ts +++ b/packages/browser/src/node/plugin.ts @@ -1,15 +1,13 @@ import type { HtmlTagDescriptor } from 'vite' import type { Plugin } from 'vitest/config' -import type { Vitest } from 'vitest/node' import type { ParentBrowserProject } from './projectParent' import { createReadStream, readFileSync } from 'node:fs' import { createRequire } from 'node:module' import { dynamicImportPlugin } from '@vitest/mocker/node' import { toArray } from '@vitest/utils/helpers' import MagicString from 'magic-string' -import { basename, dirname, join, resolve } from 'pathe' +import { dirname, join, resolve } from 'pathe' import sirv from 'sirv' -import { coverageConfigDefaults } from 'vitest/config' import { isFileServingAllowed, isValidApiRequest, @@ -63,18 +61,12 @@ export default (parentServer: ParentBrowserProject, base = '/'): Plugin[] => { }, ) - const coverageFolder = resolveCoverageFolder(parentServer.vitest) - const coveragePath = coverageFolder ? coverageFolder[1] : undefined - if (coveragePath && base === coveragePath) { - throw new Error( - `The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`, - ) - } - - if (coverageFolder) { + // Serve coverage HTML at ./coverage if configured + const coverageHtmlDir = parentServer.vitest.config.coverage?.htmlDir + if (coverageHtmlDir) { server.middlewares.use( - coveragePath!, - sirv(coverageFolder[0], { + '/__vitest_test__/coverage', + sirv(coverageHtmlDir, { single: true, dev: true, setHeaders: (res) => { @@ -604,43 +596,6 @@ function getRequire() { return _require } -function resolveCoverageFolder(vitest: Vitest) { - const options = vitest.config - const coverageOptions = vitest._coverageOptions - const htmlReporter = coverageOptions?.enabled - ? toArray(options.coverage.reporter).find((reporter) => { - if (typeof reporter === 'string') { - return reporter === 'html' - } - - return reporter[0] === 'html' - }) - : undefined - - if (!htmlReporter) { - return undefined - } - - // reportsDirectory not resolved yet - const root = resolve( - options.root || process.cwd(), - coverageOptions.reportsDirectory || coverageConfigDefaults.reportsDirectory, - ) - - const subdir - = Array.isArray(htmlReporter) - && htmlReporter.length > 1 - && 'subdir' in htmlReporter[1] - ? htmlReporter[1].subdir - : undefined - - if (!subdir || typeof subdir !== 'string') { - return [root, `/${basename(root)}/`] - } - - return [resolve(root, subdir), `/${basename(root)}/${subdir}/`] -} - const postfixRE = /[?#].*$/ function cleanUrl(url: string): string { return url.replace(postfixRE, '') diff --git a/packages/ui/client/components/Coverage.vue b/packages/ui/client/components/Coverage.vue index a26a84844..34bc59739 100644 --- a/packages/ui/client/components/Coverage.vue +++ b/packages/ui/client/components/Coverage.vue @@ -1,10 +1,6 @@