Coverage page is broken in the html report for @vitest/ui
Describe the bug
When using the 'html' reporter for vitest, with the html coverage reporter also enabled, the UI contains a button to load the coverage report in an iframe, but the iframe fails to load because it is loaded from '/coverage/index.html'.
There are really two problems here.
- By iframing in the url starting with a
/the browser will attempt to loadhttp://my-host.io/coverage/index.htmlregardless of the url route your report is served from. Instead, it might be preferrable to use a relative path:http://my-host.io/report/12345/coverage/index.htmletc. - With the default configured options, the two reports are generated in separate directories:
./coverageand./htmlThis makes it harder to bundle all the report artifacts because they are in separate places.
Regarding #2 it would be nice if the html report directory was automatically copied into the vitest html report directory and just referenced via a relative url if the html coverage reporter is enabled. That would solve both issues at once for me without requiring extra configuration. That is my ideal fix, but I can also see the following being useful:
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['lcov', 'text-summary', ['html', {subdir: '../vitest-report/coverage'}]],
},
reporters: [['html', {outputFile: './vitest-report/index.html'}]],
},
});
The above configuration correctly places the html coverage directory inside of the vitest html report directory, but the paths are not resolved correctly still, Additional work would be needed to support this:
Related Issue: #6144
The above issue appears related in that they are trying to ultimately solve the same problem of the coverage not working depending on how the page is accessed or hosted, but I think the problems are deeper than what is described in the linked issue, so I created this new issue to track fixing some of those fundamental issues as at that point configuring the url to point to an externally hosted service is an orthogonal concern.
Reproduction
Use the following vitest config on a project:
import {defineConfig} from 'vitest/config'
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['html'],
},
reporters: ['html'],
},
});
Execute:
npx vitest run
Follow the instructions for previewing the test results:
npx vite preview --outDir html
Click the coverage button:
Observe that the coverage page fails to load.
System Info
System:
OS: Windows 11 10.0.22631
CPU: (20) x64 12th Gen Intel(R) Core(TM) i7-12800H
Memory: 10.12 GB / 31.64 GB
Binaries:
Node: 22.11.0 - ~\AppData\Local\fnm_multishells\20364_1732206955982\node.EXE
Yarn: 4.5.1 - ~\AppData\Local\fnm_multishells\20364_1732206955982\yarn.CMD
npm: 10.9.0 - ~\AppData\Local\fnm_multishells\20364_1732206955982\npm.CMD
bun: 1.1.29 - ~\.bun\bin\bun.EXE
Browsers:
Edge: Chromium (127.0.2651.74)
Internet Explorer: 11.0.22621.3527
npmPackages:
@vitejs/plugin-vue: ^5.1.4 => 5.1.4
@vitest/coverage-v8: ^2.1.2 => 2.1.3
@vitest/ui: 2.1.3 => 2.1.3
vite: ^5.4.8 => 5.4.10
vitest: ^2.1.2 => 2.1.3
Used Package Manager
yarn
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
Hi, I know this issue was open some time ago but I just encountered this problem and it seems like it's not solved yet.
I managed to solve this problem with a little tweak. Instead of using the subdir field from the html reporter, I changed the whole reportsDirectory to the vitest-report dir. Then, if I want another report out of there, I change it with the file option. In the following example I'm doing that last part for the cobertura report:
import {defineConfig} from 'vitest/config'
export default defineConfig({
test: {
coverage: {
enabled: true,
reporter: ['html', ['cobertura', { file: '../../coverage/cobertura.xml' }]],
reportsDirectory: './vitest-report/coverage',
},
reporters: [['html', {outputFile: './vitest-report/index.html'}]],
},
});
This will put the vitest ui and coverage report inside the <root>/vitest-report folder without modifying the coverage path, and the cobertura report in <root>/coverage/.
It's a bit hacky but hope it helps while they fix the problem! 🙏
Hi, I know this issue was open some time ago but I just encountered this problem and it seems like it's not solved yet.
I managed to solve this problem with a little tweak. Instead of using the
subdirfield from thehtmlreporter, I changed the wholereportsDirectoryto thevitest-reportdir. Then, if I want another report out of there, I change it with thefileoption. In the following example I'm doing that last part for the cobertura report:import {defineConfig} from 'vitest/config'
export default defineConfig({ test: { coverage: { enabled: true, reporter: ['html', ['cobertura', { file: '../../coverage/cobertura.xml' }]], reportsDirectory: './vitest-report/coverage', }, reporters: [['html', {outputFile: './vitest-report/index.html'}]], }, }); This will put the
vitest uiandcoveragereport inside the<root>/vitest-reportfolder without modifying the coverage path, and the cobertura report in<root>/coverage/.It's a bit hacky but hope it helps while they fix the problem! 🙏
Is it possible to enable this in some manner for html coverage reporter.
I've the following config and i need to keep the coverage and test results in once folder to post the results in our service.
test: {
environment: 'jsdom',
globals: true,
setupFiles: './src/setupTests.ts',
include: ['src/**/*.{test,spec}.{ts,tsx}'],
reporters: ['default', 'html'],
outputFile: {
default: 'test-reports/default.html',
html: 'test-reports/index.html',
},
coverage: {
provider: 'v8',
enabled: process.env.VITEST_COVERAGE === 'true',
reportsDirectory: './test-reports/coverage',
reporter: ['html', 'text', 'json'],
exclude: [
'src/main.tsx',
'src/vite-env.d.ts',
'**/index.ts',
'**/*.d.ts',
'*.config.{ts,js}',
'test-reports/**',
],
thresholds: {
lines: 90,
functions: 90,
statements: 90,
branches: 80,
},
},
},
I have this same issue
when clicking on coverage in the ui:
Uncaught ReferenceError: __vite_ssr_exportName__ is not defined
const vitestConfig = defineVitestConfig({
base: "/",
test: {
globals: true,
environment: "jsdom",
setupFiles: "./src/test/setupTests.ts",
coverage: {
reportsDirectory: "./static/coverage",
reporter: ["text", "json", "html"],
include: ["src/**/*.ts", "src/**/*.tsx"],
provider: "v8",
},
},
});
export default ({ mode }: { mode: string }) => {
if (mode === "test") return mergeConfig(shared, vitestConfig);
throw new Error("Mode must be 'client', 'server', or 'test'");
};
"test": "vitest --mode test",
"test:ui": "vitest --ui --mode test --coverage.enabled=true",
"coverage": "vitest run --mode test --coverage",
"reporter:coverage": "vitest run --mode test --reporter=json --coverage || true",
<iframe id="vitest-ui-coverage" src="/coverage/index.html"></iframe>
Update:
i got it to work by adding
reportOnFailure: true,
const vitestConfig = defineVitestConfig({
base: "/",
test: {
globals: true,
environment: "jsdom",
setupFiles: "./src/test/setupTests.ts",
coverage: {
reportOnFailure: true,
enabled: true,
reportsDirectory: "./test-reports/coverage",
reporter: ["text", "json", "html"],
include: ["src/**/*.ts", "src/**/*.tsx"],
provider: "v8",
},
},
});
so what i figured is going on is, coverage wont run unless all tests pass, therefore adding reportOnFailure: true, flags failed/unwritten tests as well. weird but seems to solve it for me.
Hi, I have the first mentioned problem in my CI The vitest report is hosted on e.g. ci.company.com/12345/index.html, I configured coverage according to docs and it tries to load coverage report from ci.company.com/coverage/index.html Is it possible to workaround this issue?
Simply place the coverage report in the same folder as the Vitest UI test report. For example, if the Vitest UI test report’s outputDir is ./html, then the coverage report should be placed in ./html/coverage.
test: {
globals: true,
environment: "jsdom",
reporters: ["default", "html"],
setupFiles: "./vitest.setup.ts",
coverage: {
enabled: true,
reportsDirectory: "./html/coverage"
},
},
Simply place the coverage report in the same folder as the Vitest UI test report. For example, if the Vitest UI test report’s outputDir is ./html, then the coverage report should be placed in ./html/coverage.
test: { globals: true, environment: "jsdom", reporters: ["default", "html"], setupFiles: "./vitest.setup.ts", coverage: { enabled: true, reportsDirectory: "./html/coverage" }, },
That do not work as the link to the coverage report is constructed relative to the host root and in my case vitest report is not hosted in the root. Seems that this line https://github.com/vitest-dev/vitest/blob/075ab35207ce7e6ad6bd3abb02ba6d14d5108c7b/packages/ui/client/composables/navigation.ts#L57 does not take into consideration current location