test-utils
test-utils copied to clipboard
feature: allow stubbing components by definition, not by name
stubs
has an important limitation for years - you can't stub anonymous component. Let me show that in code:
const AnonymousComponent = { template: '<div>anonymous</div>' }
const Component = {
computed: {
cmp() {
return AnonymousComponent;
}
},
template: '<div><component :is="cmp" /></div>',
}
const wrapper = mount(Component, {
global: {
stubs: {
// How do I stub AnonymousComponent?
}
}
})
If you feel that this is rare use-case, it will come quite frequent when all fancy setup()
/ <script setup>
things will come in play:
const AnonymousComponent = { template: '<div>anonymous</div>' }
const Comp = {
render() {
return h(AnonymousComponent)
}
}
const wrapper = mount(Comp, {
global: {
stubs: {
// How do I stub AnonymousComponent?
}
}
})
In real world, AnonymousComponent
is usually coming from third-party library so "just add name" is not a solution. Obviously there is an escape hatch - one can use jest.mock
to mock AnonymousComponent
import and provide stub in that way, but it looks ugly, complex, and (most important) - creates two different ways of solving same task (stubbing components)
The core reason of the problem is obvious - we're using name
as an identifier for components to stub. I also see certain caveats here - for example registeredName
which is used for stubbing definitely feels like implementation detail for me - I want to treat my components as black box as much as possible and I don't care how components: { ... }
is defined
So, basically an idea is to allow passing component definition to stubs. Luckily, we've supported array syntax in stubs for long time, so the only change will be to allow that arrays to contain a new type of "stub definition", something like:
{ component: AnonymousComponent, stub: MySuperStub }
So, the usage will be:
const wrapper = mount(Comp, {
global: {
stubs: [
{ component: AnonymousComponent, stub: MySuperStub }, // using concrete stub
{ component: SomeOtherComponent, stub: true }, // let VTU generate the stub,
'HelloCmp', // mixing old array-style stubs, ugly, but works,
{ component: 'StubByName', stub: true } // Old good stub-by-name
]
}
})
This change, while being completely additive and non-breaking will simplify life for every person who is stubbing here :) WDYT?
I personally have no idea how useful this would be for the majority of users, but since it's not a breaking change, I don't have anything against it. I agree "just add a name" is not a very good solution.
The only real consideration is here is the complexity- do you see 1) significant difficultly in implementing or 2) unsolvable edge cases?
I personally have no idea how useful this would be for the majority of users, but since it's not a breaking change, I don't have anything against it. I agree "just add a name" is not a very good solution.
The only real consideration is here is the complexity- do you see 1) significant difficultly in implementing or 2) unsolvable edge cases?
No, actually I have a working prototype and it does not bring any complexity while really solving important pain points for shallow users. I'll open PR within couple of days
Sounds good!
Also your many fixes are now out in rc.10 - thanks a lot for that. https://github.com/vuejs/vue-test-utils-next/releases/tag/v2.0.0-rc.10
I currently have a util to stub a component but render the slots and also with slot data. Here I have a quick example:
<template>
<BigLayout>
<template #header="{ toolbar }">
{{ toolbar ? 'Header in toolbar' : 'Header not in toolbar' }}
</template>
<span>Some Content</span>
</BigLayout>
</template>
I want to stub the BigLayout
but still render the header slot with some data.
With the new stub definition we could implement this and be able to add more enhancement to the stubs:
const wrapper = mount(Comp, {
global: {
stubs: [
{
stub: true,
component: BigLayout,
scopedSlots: {
header: { toolbar: true }
}
}
]
}
})
I currently have a util to stub a component but render the slots and also with slot data. Here I have a quick example:
<template> <BigLayout> <template #header="{ toolbar }"> {{ toolbar ? 'Header in toolbar' : 'Header not in toolbar' }} </template> <span>Some Content</span> </BigLayout> </template>
I want to stub the
BigLayout
but still render the header slot with some data.With the new stub definition we could implement this and be able to add more enhancement to the stubs:
const wrapper = mount(Comp, { global: { stubs: [ { stub: true, component: BigLayout, scopedSlots: { header: { toolbar: true } } } ] } })
I like this. I also have utils to handling scoped slots and rendering those.
I know this is kind of old, but this is a issue still happening. I will draft an Issue to bring the rendering of slots / dynamic / named slots back