Code snippet:
// menu.vue
export default {
name: 'Menu',
setup(props, context) {
// logic here...
return () => h('div', options, children)
}
}
// test.js
const localVue = createLocalVue()
localVue.use(VueCompositionApi)
describe('Menu', () => {
it('should render', () => {
const wrapper = mount(Menu, {localVue})
expect(wrapper.html()).toMatchSnapshot()
})
})
which returns the following error
[Vue warn]: Failed to mount component: template or render function not defined.
I'm currently using jest 24.9.0, @vue/test-utils and vue-jest 3.0.5. Other tests work fine with templated SFCs using composition API
I am trying to test a jsx component by passing props to it and asserting that it renders them correctly. This fails though for a reason I cannot understand. Here's the component
// components/toast.tsx
import { createComponent } from "@vue/composition-api";
import style from "./toast.module.scss";
export default createComponent({
name: "Toast",
props: {
message: String
},
setup(props) {
return () => (
<div class={style.toast}>
<p>{props.message}</p>
</div>
);
}
});
Here's the snippet of the unit test that is failing:
// tests/unit/components/toast.spec.ts
import Toast from "@/components/toast";
import { shallowMount } from "@vue/test-utils";
// ... a describe block
it("should render props correctly", () => {
const msg = "hello";
const wrapper = shallowMount(Toast, {
propsData: {
message: msg
}
});
expect(wrapper.isVueInstance()).toBeTruthy();
expect(wrapper.text()).toMatch(msg);
});
And the output from the failing test is here:
Expected substring: "hello"
Received string: "function (el) {
var obj;·
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
if (shouldNotBeStubbed(el, stubs, modifiedComponents)) {
return originalCreateElement.apply(void 0, [ el ].concat( args ))
}·
if (isConstructor(el) || isComponentOptions(el)) {
if (stubAllComponents) {
var stub = createStubFromComponent(el, el.name || 'anonymous', _Vue);
return originalCreateElement.apply(void 0, [ stub ].concat( args ))
}
var Constructor = shouldExtend(el, _Vue) ? extend(el, _Vue) : el;·
return originalCreateElement.apply(void 0, [ Constructor ].concat( args ))
}·
if (typeof el === 'string') {
var original = resolveComponent(el, originalComponents);·
if (!original) {
return originalCreateElement.apply(void 0, [ el ].concat( args ))
}·
if (isDynamicComponent(original)) {
return originalCreateElement.apply(void 0, [ el ].concat( args ))
}·
var stub$1 = createStubIfNeeded(stubAllComponents, original, _Vue, el);·
if (stub$1) {
Object.assign(vm.$options.components, ( obj = {}, obj[el] = stub$1, obj));
modifiedComponents.add(el);
}
}·
return originalCreateElement.apply(void 0, [ el ].concat( args ))
}"
16 | });
17 | expect(wrapper.isVueInstance()).toBeTruthy();
> 18 | expect(wrapper.text()).toMatch(msg);
| ^
19 | });
20 | });
21 |
at Object.it (tests/unit/components/toast.spec.ts:18:28)
Everything works! except for this one part. If I use a single file component (.vue) the component mounts and the test passes with no problem.
I have the same problem on using tsx with composition api in jest
and if I log the wrapper html it is just an empty string while a .vue will return the raw html
I've gone back to using .vue component files to avoid this. I do however suspect that this might not have anything to do with the composition api and it's just on tsx
I was able to use this plugin with Vue Test Utils fine - I suspect it's related to tsx. Simple demo of VTU + composition API here.
@lmiller1990 The render function not playing nicely. Which also suggests that this issue should be closed and opened somewhere else
Does this render in a browser? Or does the error only happen in testing? Could be related to Jest not knowing how to read TSX.
It only happens in tests. That also makes sense. I tried this with the simplest HelloWorld component. Maybe my project might not be configured corectly. I wish there was a reference sample should this be a non issue
I have had the problem before too. Did you make your project with Vue cli? Last time I did TS + TSX, I chose "include babel alonside TypeScript" option, and installed Vue TSX support with vue add tsx-support. A bit more info here.
Anyway I think this is not a problem with this repo... try the above, you can open an issue in Vue Test Utils if you are still stuck and I can try to help there.
Update: I managed to run tests using configurations like so, the component uses jsx
import Vue from 'vue/dist/vue.common.js'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
import GMenu from '../GMenu';
function createElement() {
if (document) {
const elem = document.createElement('div')
if (document.body) {
document.body.appendChild(elem)
}
return elem
}
}
describe('Menu', () => {
const template = `
<div>
<g-menu v-model="showMenu">
<template #activator="{toggleContent}">
<button @click="toggleContent">Activator</button>
</template>
<div>content</div>
</g-menu>
</div>`
afterEach(() => {
document.body.innerHTML = ''
})
it('should render slots', () => {
const vm = new Vue({
template,
components: { GMenu },
data() {
return {
showMenu: false
}
}
}).$mount(createElement());
expect(vm.$el.outerHTML).toMatchSnapshot()
expect(vm.$children[0].$scopedSlots.default).toBeTruthy()
expect(vm.$children[0].$scopedSlots.activator).toBeTruthy()
})
})
I have had the problem before too. Did you make your project with Vue cli? Last time I did TS + TSX, I chose "include babel alonside TypeScript" option, and installed Vue TSX support with
vue add tsx-support. A bit more info here.Anyway I think this is not a problem with this repo... try the above, you can open an issue in Vue Test Utils if you are still stuck and I can try to help there.
I tried vue-tsx-support before, jest is worked as expected but composition api is not.
So just wonder if it's because of composition api setup function
Any solution found? :/
Can someone post a repo that reproduces the problem?
I've attempted this yesterday, Here's a little example. I did this is a rush because I'm still at work but I hope it provides some context.
Here's my test
it("renders message", () => {
const localVue = createLocalVue();
localVue.use(VueCompositionApi);
const msg = "Hello, world";
const wrapper = shallowMount(HelloWorld, { localVue });
expect(wrapper.isVueInstance()).toBe(true);
expect(wrapper.vm.$el.textContent).toMatch(msg);
});
Here's what passes
export default createComponent({
name: "HelloWorld",
props: {
msg: String
},
setup() {},
render() {
return <div>Hello, world</div>;
}
}) as VueConstructor;
Here's what doesn't
export default createComponent({
name: "HelloWorld",
props: {
msg: String
},
setup() {
return () => <div>Hello, world</div>;
}
}) as VueConstructor;
NB: I'm using TSX here,
I mean, can you post an entire repository someone can pull down a run? There are so many plugins in these examples (composition api, jsx, typescript, jest vue transformer, babel) it's highly likely this is a configuration problem.
I hope this helps https://github.com/sduduzog/vue-composition-api-tsx-example
I'm also having this issue in a repo using TypeScript, but without TSX. Here’s a summary of the setup function throwing the warning in Jest:
setup() {
return () => createElement('div', slots.default());
},
Let me know if you want me to setup another reproduction repo.
I've investigated a bit and I found some stuff but not really the origin of the problem.
In the initSetup function the binding above has two very different values depending if its run in the browser or through Vue Test Utils.
In the browser it properly returns the component setup Function:
function () {
return Object(_vue_composition_api__WEBPACK_IMPORTED_MODULE_0__["createElement"])('div', slots["default"] && slots["default"]());
}
While being rendered by Vue Test Utils, binding value is an Object:
{ length: 0, name: '' }
This result in the following block being ignored and therefor the render method assignment skipped:
I'm not sure where to look at from here…
@sduduzog I cannot install your repo; I get
warning ../package.json: No license field
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
error An unexpected error occurred: "https://npm.codeo.co.za/@vue%2fcli-plugin-pwa/-/cli-plugin-pwa-4.2.2.tgz: Request failed \"401 Unauthorized\"".
info If you think this is a bug, please open a bug report with the information provided in "/Users/lachlan/code/dump/vue-composition-api-tsx-example/yarn-error.log".
It doesn't look like returning JSX from setup even is supported according to the readme in this repository. How did you get this working? setup doesn't have access to h and it isn't exported in Vue 2 from what I can see.
Has anyone else got that reproduction repo to run? Maybe it's a problem on my end.
@lmiller1990 I was able to make the repo works with npm, I can setup a simplier reproduction without JSX if you like, since it's probably not the problem here.
@sduduzog I cannot install your repo; I get
warning ../package.json: No license field [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... error An unexpected error occurred: "https://npm.codeo.co.za/@vue%2fcli-plugin-pwa/-/cli-plugin-pwa-4.2.2.tgz: Request failed \"401 Unauthorized\"". info If you think this is a bug, please open a bug report with the information provided in "/Users/lachlan/code/dump/vue-composition-api-tsx-example/yarn-error.log".It doesn't look like returning JSX from
setupeven is supported according to the readme in this repository. How did you get this working?setupdoesn't have access tohand it isn't exported in Vue 2 from what I can see.Has anyone else got that reproduction repo to run? Maybe it's a problem on my end.
Sorry @lmiller1990 please delete the yarn lock file or package.json-lock file then rebuild. I have a private npm registry that we use at work
any update?
Yes, read more details here: https://github.com/vuejs/vue-test-utils/issues/1484. Basically it's a conflict between VTU and composition API, both override the render function, VTU to accomplish shallowMount and stubs, and this library to allow a component's setup function to return a render function.
There is a potential fix I describe here, if anyone has some time they can try and implement this in VTU. I have not seen if this breaks anything else in VTU.
Here is the start of a fix https://github.com/vuejs/vue-test-utils/pull/1512 however this makes any tests calling inject in setup error out, since we called setup before the instance exists. Maybe we can move this logic somewhere like a mixin with a beforeCreate hook. The above PR is good place to start debugging this.
If you are working on a small/medium app, or just trying out the composition API and liked it, you can probably upgrade to Vue 3 and VTU next fairly easily to have everything work as expected. Plus all the goodness of Vue 3 like Suspense, Portal etc. I am not sure if JSX works for Vue 3 yet, though.
Most helpful comment
I tried vue-tsx-support before, jest is worked as expected but composition api is not.
So just wonder if it's because of composition api setup function