-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v8 coverage provider report 100% statement coverage for uncovered v-if inside template #4993
Comments
This usually happens when source maps are missing. Does this happen with latest version of |
@AriPerkkio Yes, it is same with latest version <template v-if="hasMessage">
<h1 class="green">{{ msg }}</h1>
</template>
<template v-else>
<h1 class="yellow">Default message</h1>
</template> and tests... describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, {props:{msg:'Yes did it!'}})
expect(wrapper.find('.greetings').exists()).toBe(true)
})
}) coverage... |
Vue parser was updated in the latest minor version - maybe it's a regression on Vue's part? |
Source maps are present in this case. If you add some unreached code into The reason why line 17's The line 24 of generated code is executed by Node as it's in top level scope. It's source maps points it to line 17 in sources -> it's covered. You can see the line mapped here by clicking line 24 on "Generated" side: https://ariperkkio.github.io/source-map-debugger/?s=N4Ig5gpg... In the V8 report this line is marked as covered as it's executed. If Vue excluded that line from source maps, or added This is similar issue as Svelte has: sveltejs/svelte#7824. So to summarize: Issue is in Vue compiler. It's generated code does not work with code coverage tools. |
@AriPerkkio Thank you for explanation. Just summarize to be more clear to me. Coverage tool marks those lines as covered because it is hoisted in top level scope after compilation. Am I right? And I have another question. Do you find any solution? I think that it is not minor issue. For example, process on my project is based on code coverage, among other tools. |
Yep, but it looks like there is also another issue. Even if the hoisted variable was dropped out of the source maps, this line would still mark the Link https://evanw.github.io/source-map-visualization/#MjQ5....
There are no good work-arounds for this. Vue compiler needs to adjust their source maps. |
It's clear to me. I am ready to go further. We have two issues here:
I would start with resolving second issue... @AriPerkkio Is it ok to link your noted links, if I open issue to another repo? |
I think this is an issue of Vue compiler, not the vite-plugin-vue itself. While their source maps are correct right now, they are not compatible with code coverage tools. It's similar issue as Svelte has: sveltejs/svelte#7824. Feel free to link this issue in other ones. |
Downgrading from Vue 3.4 to 3.3 fixes these issues. |
Thanks for answer, but unfortunately, doesn't work for me. @boboldehampsink Can you send me your lock file? Maybe it solves issue with correct version from another dependency. |
I used
and ran |
Nope, same issue... |
The reasons could be: We found this issue previously but have been unable to solve it until we implemented our own converter. You could try it simply using vitest import { fileURLToPath } from 'node:url'
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(
viteConfig,
// @ts-ignore
defineConfig({
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/*'],
root: fileURLToPath(new URL('./', import.meta.url)),
coverage: {
// @ts-ignore
reporter: ['v8', 'lcov'],
provider: 'custom',
customProviderModule: 'vitest-monocart-coverage'
}
}
})
) |
There's link to the source maps above #4993 (comment). It is accurate.
What do you mean? The root cause is that this line is actually covered. In the transpiled code it's in two positions: once on top level and once inside the conditional call. The top level code is always executed. I'm not sure what of rule could be used to exclude the top level code from coverage report. |
Please see my screenshot, there are two mappings: the coverage start position is |
Firstly, the case And, the logic of V8 coverage should be as follows:
So, when |
Here's the V8 report and the contents that Vitest runs for this file: coverage.json[
{
"functionName": "",
"ranges": [{ "startOffset": 0, "endOffset": 5760, "count": 1 }],
"isBlockCoverage": true
},
{
"functionName": "",
"ranges": [{ "startOffset": 13, "endOffset": 5760, "count": 1 }],
"isBlockCoverage": true
},
{
"functionName": "setup",
"ranges": [{ "startOffset": 1280, "endOffset": 1750, "count": 1 }],
"isBlockCoverage": true
},
{
"functionName": "",
"ranges": [{ "startOffset": 1416, "endOffset": 1462, "count": 1 }],
"isBlockCoverage": true
},
{
"functionName": "",
"ranges": [{ "startOffset": 1518, "endOffset": 1564, "count": 0 }],
"isBlockCoverage": false
},
{
"functionName": "_withScopeId",
"ranges": [{ "startOffset": 1777, "endOffset": 1886, "count": 1 }],
"isBlockCoverage": true
},
{
"functionName": "",
"ranges": [{ "startOffset": 2030, "endOffset": 2713, "count": 1 }],
"isBlockCoverage": true
},
{
"functionName": "_sfc_render",
"ranges": [
{ "startOffset": 2716, "endOffset": 3193, "count": 1 },
{ "startOffset": 2914, "endOffset": 3112, "count": 0 }
],
"isBlockCoverage": true
}
] transpiled.js'use strict';async (__vite_ssr_import__,__vite_ssr_dynamic_import__,__vite_ssr_exports__,__vite_ssr_exportAll__,__vite_ssr_import_meta__,require,exports,module,__filename,__dirname)=>{{const __vite_ssr_import_0__ = await __vite_ssr_import__("/node_modules/.pnpm/[email protected][email protected]/node_modules/vue/index.mjs?v=65178666", {"importedNames":["defineComponent"]});
const __vite_ssr_import_1__ = await __vite_ssr_import__("/node_modules/.pnpm/[email protected][email protected]/node_modules/vue/index.mjs?v=65178666", {"importedNames":["computed"]});
const __vite_ssr_import_2__ = await __vite_ssr_import__("/node_modules/.pnpm/[email protected][email protected]/node_modules/vue/index.mjs?v=65178666", {"importedNames":["toDisplayString","openBlock","createElementBlock","createCommentVNode","createElementVNode","createTextVNode","pushScopeId","popScopeId"]});
const __vite_ssr_import_3__ = await __vite_ssr_import__("/src/components/HelloWorld.vue?vue&type=style&index=0&scoped=e17ea971&lang.css");
const __vite_ssr_import_4__ = await __vite_ssr_import__("/@id/__x00__plugin-vue:export-helper", {"importedNames":["default"]});
const _sfc_main = /* @__PURE__ */ __vite_ssr_import_0__.defineComponent({
__name: "HelloWorld",
props: {
msg: { type: String, required: true }
},
setup(__props, { expose: __expose }) {
__expose();
const props = __props;
const hasMessage = __vite_ssr_import_1__.computed(() => {
return Boolean(props.msg);
});
const unreached = __vite_ssr_import_1__.computed(() => {
return Boolean(props.msg);
});
const __returned__ = { props, hasMessage, unreached };
Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
return __returned__;
}
});
const _withScopeId = (n) => (__vite_ssr_import_2__.pushScopeId("data-v-e17ea971"), n = n(), __vite_ssr_import_2__.popScopeId(), n);
const _hoisted_1 = { class: "greetings" };
const _hoisted_2 = {
key: 0,
class: "green"
};
const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ __vite_ssr_import_2__.createElementVNode(
"h3",
null,
[
/* @__PURE__ */ __vite_ssr_import_2__.createTextVNode(" You\u2019ve successfully created a project with "),
/* @__PURE__ */ __vite_ssr_import_2__.createElementVNode("a", {
href: "https://vitejs.dev/",
target: "_blank",
rel: "noopener"
}, "Vite"),
/* @__PURE__ */ __vite_ssr_import_2__.createTextVNode(" + "),
/* @__PURE__ */ __vite_ssr_import_2__.createElementVNode("a", {
href: "https://vuejs.org/",
target: "_blank",
rel: "noopener"
}, "Vue 3"),
/* @__PURE__ */ __vite_ssr_import_2__.createTextVNode(". ")
],
-1
/* HOISTED */
));
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return __vite_ssr_import_2__.openBlock(), __vite_ssr_import_2__.createElementBlock("div", _hoisted_1, [
$setup.hasMessage ? (__vite_ssr_import_2__.openBlock(), __vite_ssr_import_2__.createElementBlock(
"h1",
_hoisted_2,
__vite_ssr_import_2__.toDisplayString($props.msg),
1
/* TEXT */
)) : __vite_ssr_import_2__.createCommentVNode("v-if", true),
_hoisted_3
]);
}
__vite_ssr_exports__.default = /* @__PURE__ */ __vite_ssr_import_4__.default(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-e17ea971"], ["__file", "/Users/abc/efg/vue-test-template-coverage/src/components/HelloWorld.vue"]]);
//# sourceMappingSource=vite-node
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ3lCOzs7Ozs7OztBQUV6QixVQUFNLFFBQVE7QUFJZCxVQUFNLGFBQWEsK0JBQVMsTUFBTTtBQUNoQyxhQUFPLFFBQVEsTUFBTSxHQUFHO0FBQUEsSUFDMUIsQ0FBQztBQUVELFVBQU0sWUFBWSwrQkFBUyxNQUFNO0FBQy9CLGFBQU8sUUFBUSxNQUFNLEdBQUc7QUFBQSxJQUMxQixDQUFDOzs7Ozs7OztxQkFJTSxPQUFNLFlBQVc7OztFQUVkLE9BQU07O3NEQUVaO0FBQUEsRUFJSztBQUFBO0FBQUE7QUFBQSwwREFKRCxtREFFRjtBQUFBLDZEQUFxRTtBQUFBLE1BQWxFLE1BQUs7QUFBQSxNQUFzQixRQUFPO0FBQUEsTUFBUyxLQUFJO0FBQUEsT0FBVyxNQUFJO0FBQUEsMERBQUksS0FDckU7QUFBQSw2REFBcUU7QUFBQSxNQUFsRSxNQUFLO0FBQUEsTUFBcUIsUUFBTztBQUFBLE1BQVMsS0FBSTtBQUFBLE9BQVcsT0FBSztBQUFBLDBEQUFJLElBQ3ZFO0FBQUE7Ozs7OzRDQVJGLHlDQVNNLE9BVE4sWUFTTTtBQUFBLElBUlksd0RBQ2Q7QUFBQSxNQUFnQztBQUFBLE1BQWhDO0FBQUEsTUFBZ0Msc0NBQVgsVUFBRztBQUFBO0FBQUE7QUFBQTtJQUUxQjtBQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIkhlbGxvV29ybGQudnVlIl0sInNvdXJjZXNDb250ZW50IjpbIjxzY3JpcHQgc2V0dXAgbGFuZz1cInRzXCI+XG5pbXBvcnQgeyBjb21wdXRlZCB9IGZyb20gXCJ2dWVcIjtcblxuY29uc3QgcHJvcHMgPSBkZWZpbmVQcm9wczx7XG4gIG1zZzogc3RyaW5nO1xufT4oKTtcblxuY29uc3QgaGFzTWVzc2FnZSA9IGNvbXB1dGVkKCgpID0+IHtcbiAgcmV0dXJuIEJvb2xlYW4ocHJvcHMubXNnKTtcbn0pO1xuXG5jb25zdCB1bnJlYWNoZWQgPSBjb21wdXRlZCgoKSA9PiB7XG4gIHJldHVybiBCb29sZWFuKHByb3BzLm1zZyk7XG59KTtcbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxkaXYgY2xhc3M9XCJncmVldGluZ3NcIj5cbiAgICA8dGVtcGxhdGUgdi1pZj1cImhhc01lc3NhZ2VcIj5cbiAgICAgIDxoMSBjbGFzcz1cImdyZWVuXCI+e3sgbXNnIH19PC9oMT5cbiAgICA8L3RlbXBsYXRlPlxuICAgIDxoMz5cbiAgICAgIFlvdeKAmXZlIHN1Y2Nlc3NmdWxseSBjcmVhdGVkIGEgcHJvamVjdCB3aXRoXG4gICAgICA8YSBocmVmPVwiaHR0cHM6Ly92aXRlanMuZGV2L1wiIHRhcmdldD1cIl9ibGFua1wiIHJlbD1cIm5vb3BlbmVyXCI+Vml0ZTwvYT4gK1xuICAgICAgPGEgaHJlZj1cImh0dHBzOi8vdnVlanMub3JnL1wiIHRhcmdldD1cIl9ibGFua1wiIHJlbD1cIm5vb3BlbmVyXCI+VnVlIDM8L2E+LlxuICAgIDwvaDM+XG4gIDwvZGl2PlxuPC90ZW1wbGF0ZT5cblxuPHN0eWxlIHNjb3BlZD5cbmgxIHtcbiAgZm9udC13ZWlnaHQ6IDUwMDtcbiAgZm9udC1zaXplOiAyLjZyZW07XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbiAgdG9wOiAtMTBweDtcbn1cblxuaDMge1xuICBmb250LXNpemU6IDEuMnJlbTtcbn1cblxuLmdyZWV0aW5ncyBoMSxcbi5ncmVldGluZ3MgaDMge1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7XG59XG5cbkBtZWRpYSAobWluLXdpZHRoOiAxMDI0cHgpIHtcbiAgLmdyZWV0aW5ncyBoMSxcbiAgLmdyZWV0aW5ncyBoMyB7XG4gICAgdGV4dC1hbGlnbjogbGVmdDtcbiAgfVxufVxuPC9zdHlsZT5cbiJdLCJmaWxlIjoiL3NyYy9jb21wb25lbnRzL0hlbGxvV29ybGQudnVlIn0=
}} Should |
I think it should be:
|
Yep, exactly. So the first two entries of the V8 report will mark the |
Yes, at the beginning, the first two entries indeed mark |
Sure, but there is no such block. That's why it's covered. The |
this one is
|
@cenfun could you file a bug report to |
There is already a bug here. |
Describe the bug
v8 coverage provider report 100% statement coverage for uncovered v-if inside template
HelloWorld.vue
HelloWorld.spec.ts
In this example we test component without props so template with h1 tag will not render but provider report that coverage for that lines are 100% covered, as well as for entire file.
Reproduction
Link to repo where you can reproduce issue, https://github.com/index23/vue-test-template-coverage
Steps to reproduce:
npm run test:unit
coverage
directorycoverage/lcov-report/index.html
System Info
Used Package Manager
npm
Validations
The text was updated successfully, but these errors were encountered: