1.0.0-beta.29
https://github.com/anthonygore/vuetify-test
npm i
npm run test:unit
All tests should pass
The second test fails as the VTextField component can't be found.
I don't know if this is a Vuetify issue or a Vue Test Utils issue, but some stubbed child components can't be found with the find wrapper method when the parent is shallow mounted.
It's not just VTextField, either, VSelect also has this problem, while others like VIcon work just fine.
import { shallowMount } from "@vue/test-utils";
import Vuetify from "vuetify";
import { VBtn, VTextField } from "vuetify/lib";
Vue.use(Vuetify);
const component = {
template: `
<div>
<v-btn />
<v-text-field />
</div>
`
};
// Passes
it('should find v-btn', () => {
const wrapper = shallowMount(component);
expect(wrapper.find(VBtn).exists()).toBe(true);
});
// Fails
it('should find v-text-field', () => {
const wrapper = shallowMount(component);
expect(wrapper.find(VTextField).exists()).toBe(true);
});
I think the issue is something to do with how some global components are registered because this works:
Vue.use(Vuetify);
const component = {
template: `
<div>
<v-btn />
<v-text-field />
</div>
`,
components: {
VTextField
}
};
Note that I don't need to locally register VBtn, just VTextField...
Hey @anthonygore , This is likely something to do with the fact VTextField extends from VInput, as shown here in the source code for Vuetify.
Iirc if you pass a component to find it will match against the prototype, and in this case VTextField.prototype !== VInput.prototype it returns false.
A work around would be to pass an object with name to find. So you could try:
it('should find v-text-field', () => {
const wrapper = shallowMount(component);
expect(wrapper.find({ name: 'v-text-field' }).exists()).toBe(true);
});
That may work.
Hey @lmiller1990, I tried your solution and it doesn't work. But, I think you're onto something with the extends thing. It seems that all the Vuetify components that don't work with find do extend another component. That gives me a lead to keep researching. Thanks!
Interesting. I recently got asked to help out with a Vuetify project that has vue-test-utils in it, so I'll have a hack this weekend and see if I can figure this out (likely I will run into this soon as well).
Champion, thank you.
Happens to v-list-tile-title too: https://github.com/vuetifyjs/vuetify/blob/6f32c6439a4c789ac992794f5d514db6fec4a5fc/packages/vuetify/src/components/VList/index.ts#L12
There is currently _no_ way to find a functional component - I doubt this will change.
Are you sure? v-icon is functional and I can find that one.
import { VIcon } from "vuetify/lib";
it('should find v-icon', () => {
const wrapper = shallowMount(component);
expect(wrapper.find(VIcon).exists()).toBe(true); // Passes
});
Hm, I guess that isn't what prevents me finding v-list-tile-title.
I wonder what the common thing that prevents find from working...
I'm having this issue as well in my own library (not using or related to Vuetify).
In my jest setup I use Vue.component('FooBar', require('.../path/to/FooBar.vue').default)
In my test I import 'FooBar' from '.../path/to/FooBar.vue'
I provide stubs: {FooBar: true} in my options when mount-ing (not shallow)
Then wrapper.find(FooBar) fails, even though I'm using <foo-bar .../> in the component.
I've noticed that wrapper.find('foobar-stub') works, but the props are empty so testing it is moot.
I find it very interesting too that the stub I found is named foobar (without the dash!)
I still don't understand why wrapper.find(VBtn) works above, but your update makes sense if you look at the code
When stubbing, vue-test-utils uses the component's components options. So without providing any components (relying on the ones already given to Vue) then it will fail to use the component to stub and make a new stub component instead.
In your update, since you pass the components option, it'll find it correctly.
Seems to me that line 56 could be changed to Object.assign(Object.create(_Vue.options.components), componentOptions.components) to also reference the globally registered components.
Testing this out locally did the trick for me.
As a workaround before I declare my tests I do:
ComponentUnderTest.components = Object.assign(Object.create(Vue.options.components), ComponentUnderTest.components || {});
And then the stubs work like a charm
EDIT: Looks like I can find FooBar now (and test it's props), but triggering events isn't working as expected.
Are you sure?
v-iconis functional and I canfindthat one.import { VIcon } from "vuetify/lib"; it('should find v-icon', () => { const wrapper = shallowMount(component); expect(wrapper.find(VIcon).exists()).toBe(true); // Passes });
For me, I cant use wrapper.find with a VBtn, for example. The console returns:
TypeScript diagnostics (customize using `[jest-config].globals.ts-jest.diagnostics` option):
tests/unit/infosets/components/InfosetsHeader.spec.ts:17:25 - error TS2345: Argument of type 'Component<DefaultData<never>, DefaultMethods<never>, DefaultComputed, Record<string, any>>' is not assignable to parameter of type 'NameSelector'.
Type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>' is not assignable to type 'NameSelector'.
Types of property 'name' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
17 expect(wrapper.find(VBtn).exists()).toBe(true);
@aislanmaia I noticed I could find some functional components too - I can't see any common pattern that makes a component unfindable at this point.
@lmiller1990 I've some progress here, but in the same point 0 again.
Take a look:
InfosetsHeader.component
✓ renders Minhas Listas at top when rendered (3ms)
✕ renders a Button component to create new lists (13ms)
● InfosetsHeader.component › renders a Button component to create new lists
expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
32 | const btn = wrapper.find(".v-btn");
33 | // console.log("btn", btn);
> 34 | expect(btn.exists()).toBe(true);
| ^
35 |
36 | /* mock event */
37 | const event = jest.fn();
at Object.<anonymous> (tests/unit/infosets/components/InfosetsHeader.spec.ts:34:26)
The v-btn and any vuetify components are unfindable.
The current state is that Vuetifiy is just untestable at all, at least with Jest.
Things like that, just increase the Javascript fatigue for me, because IMHO, things like this (testing, etc), should work out-of-box really.
It feels like Vuetify is responsible for a lot of problems around vue-test-utils, I've had an equally frustrating experience (with attempting to test Vuetify based apps).
One work-around you can try is adding <v-btn data-test="button"> ..... </v-btn> and trying to do wrapper.find('[data-test="button"]') and see if you can find it that way.
Don't work either. It seems that the element isn't there yet, or maybe it never wasn't there in the component at the moment the test is running.
What if you do console.log(wrapper.html({ pretty: true })) (not sure if the pretty: true API is correct or not, been a while). Can you see the markup you expect?
If you are using post vue-test-utils beta 28, the way rendering updates are applied changed a bit. You might need to do await wrapper.vm.$nextTick() to ensure Vue has had time to update the DOM.
What if you do
console.log(wrapper.html({ pretty: true }))(not sure if thepretty: trueAPI is correct or not, been a while). Can you see the markup you expect?If you are using post
vue-test-utilsbeta 28, the way rendering updates are applied changed a bit. You might need to doawait wrapper.vm.$nextTick()to ensure Vue has had time to update the DOM.
It doesn't accept a parameter like pretty. Just calling wrapper.html() is enough to get returned the html, however in unformatted way.
The markup returned show up some Vuetify components marked with the "-stub" in the names.
It happens that even referencing this names like wrapper.find('.v-btn-stub') the "find" cannot return my Vuetify components.
I might take (another) look at this one this week. Whoever solves this will be a hero; this issue has plagued us for a while. I have noticed this only happens _sometimes_, especially in larger projects. Have you had a similar experience?
@lmiller1990 no similar, because our project is in early stages and not considered big right now. Saying that, this is our first unit tests with Vuetify, and it is disappointing that it not working at all with jest.
@aislanmaia I just found that removing the . actually solves this. When running the wrapper.html() I saw that the stubs aren't actually divs or similar with classes applied but actually a v-btn-stub element.
What if you do
console.log(wrapper.html({ pretty: true }))(not sure if thepretty: trueAPI is correct or not, been a while). Can you see the markup you expect?
If you are using postvue-test-utilsbeta 28, the way rendering updates are applied changed a bit. You might need to doawait wrapper.vm.$nextTick()to ensure Vue has had time to update the DOM.It doesn't accept a parameter like
pretty. Just callingwrapper.html()is enough to get returned the html, however in unformatted way.The markup returned show up some Vuetify components marked with the "-stub" in the names.
It happens that even referencing this names likewrapper.find('.v-btn-stub')the "find" cannot return my Vuetify components.
Regarding Vuetify, what I am currently doing is just using mount and doing things on the raw <input /> or <button /> that VInput and VBtn render. I'm starting to think shallowMount should be avoided wherever possible - it's not ideal to be testing against something different (eg stubs) to your actual component.
We are exploring better ways to test third party UI components for the Vue 3 integration.
@aislanmaia Have you tried my workaround above?
Most helpful comment
Hey @anthonygore , This is likely something to do with the fact
VTextFieldextends fromVInput, as shown here in the source code for Vuetify.Iirc if you pass a component to
findit will match against the prototype, and in this caseVTextField.prototype !== VInput.prototypeit returns false.A work around would be to pass an object with
nametofind. So you could try:That may work.