'How to test transition function in Vue 3 test utils?
I have a problem testing transiton functions with jest and vue test utlis. This is my component code:
<div class="disclosure">
<button
class="label"
type="button"
@click="toggle"
>
<span class="label-text">
<slot name="disclosure-title" />
</span>
</button>
<Transition
name="disclosure"
@before-enter="transitionBaseState"
@enter="transitionEndState"
@before-leave="transitionEndState"
@leave="transitionBaseState"
>
<div
v-show="open"
class="panel"
>
<div class="panel-content">
<slot />
</div>
</div>
</Transition>
</div>
I have a problem with transition functions: transitionBaseState and transitionEndState. When I run ejst spec for the component, coverage tab shows that those functions are not covered. Do you know what is the best way to test those functions? My test for showing element is this:
it('can be changed to opened by clicking the panel', async () => {
await wrapper.find(buttonSelector).trigger('click');
expect(wrapper.find(panelSelector).isVisible()).toBe(true);
});
Functions:
function transitionBaseState(el: HTMLElement): void {
el.style.height = '0';
}
function transitionEndState(el: HTMLElement): void {
el.style.height = `${el.scrollHeight}px`;
}
Solution 1:[1]
Ok. This is the tough one. Vue test utils stubs transitions by default. And this behavior is not covered by docs. You need to mock it globally by hardcoded false
value.
import { config, mount } from '@vue/test-utils'
...
// before Mount
config.global.stubs = {
transition: false
}
jest.useFakeTimers() // to deal with durations
// mount
// trigger click
jest.advanceTimersByTime(duration + 1)
// expect whatever you need from before-enter, enter, after-enter callbacks
// trigger click
jest.advanceTimersByTime(duration + 1)
// expect whatever you need from before-leave, leave, after-leave callbacks
As you can see, I added workaround with fake timers to deal with transition duration.
In fact, in most cases you just don't need to test what transition does. This is already tested by vue core team, but maybe there are some cases where it should be tested. As you can see, the number of line in tests grows just to avoid default behavior. It also uses undocumented mocks and you should to keep that working after every update of Vue/vue-test-utils with some breaking changes.
Anyway I've tested this code above. Here is my attempt
it('should call all the callbacks', async () => {
expect.hasAssertions()
const callbacks = {
onBeforeEnter: jest.fn(),
onEnter: jest.fn(),
onAfterEnter: jest.fn(),
onBeforeLeave: jest.fn(),
onLeave: jest.fn(),
onAfterLeave: jest.fn(),
}
const component = defineComponent({
name: 'Component',
data() {
return {
flag: false
}
},
template: `<div>
<transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave">
<span v-if="flag"></span>
</transition>
</div>`,
methods: {
onBeforeEnter: callbacks.onBeforeEnter,
onEnter(e: Event, done: Function) {
callbacks.onEnter()
done()
},
onAfterEnter() {
callbacks.onAfterEnter()
},
onBeforeLeave() {
callbacks.onBeforeLeave()
},
onLeave(e: Event, done: Function) {
callbacks.onLeave();
done()
},
onAfterLeave() {
callbacks.onAfterLeave()
}
}
})
config.global.stubs = {
transition: false
}
jest.useFakeTimers()
const wrapper = mount(component)
await wrapper.setData({ flag: true })
expect(callbacks.onBeforeEnter).toHaveBeenCalled()
expect(callbacks.onEnter).toHaveBeenCalled()
expect(callbacks.onAfterEnter).toHaveBeenCalled()
await wrapper.setData({ flag: false })
jest.advanceTimersByTime(1)
expect(callbacks.onBeforeLeave).toHaveBeenCalled()
expect(callbacks.onLeave).toHaveBeenCalled()
expect(callbacks.onAfterLeave).toHaveBeenCalled()
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Andrey Nelubin |