I'd like to test that a loading placeholder is displayed, while data from the server is fetching inside onMounted
hook. Here is the component I'm testing:
<template>
<section >
<div
v-if="collections && Object.keys(collections).length"
data-test="collectionCards"
>
<div
v-for="collection in collections"
:key="collection.cardSetId"
>
<div >
<p >4 days ago</p>
</div>
<div >
<div>
<div ></div>
<div >
<h3 data-test="collectionName">{{ collection.name }}</h3>
<p >
<span data-test="collectionLength">{{ collection.length }}</span> item(s)</p>
</div>
<div >
<base-collapse data-test="collectionDescription">{{ collection.description }}</base-collapse>
</div>
</div>
<div >
<p >Author: <span >Kanstantsin Lazouski</span></p>
</div>
</div>
</div>
</div>
<div v-else-if="isLoading" data-test="loadingCollections">Loading...</div>
<div v-else data-test="emptyCollectionWrapper">
<EmptyCollection />
</div>
</section>
</template>
/*....*/
onMounted(async () => {
isLoading.value = true;
const collectionList = await getCollectionList(userId.value as string);
try {
collections.value = collectionList;
} catch(err) {
/* TODO: add more appropriate error handling */
console.error(err);
} finally {
isLoading.value = false;
}
})
But everything I tried was not successful. As far as I understand, we should firstly mount component and then call flushPromises
in order to make Vue wait until promises inside onMounted
will be resolved. So, between calling mount
and flushPromises
there should be a timespan when isLoading
is set to true. The problem is that, when we log 'isLoading' inside the test, it will display true
(which is correct), but actual HTML is different from what it is supposed to be (I receive EmptyCollection
inside v-else
). Here is my test that's not working though:
it('renders "EmptyCollection" if "collectionList" is empty and "Loading..." while data is fetching', async () => {
(getCollectionList as jest.Mock).mockResolvedValueOnce({});
const wrapper = mount(MyCollections, wrapperOptions);
expect(wrapper.find('[data-test="loadingCollections"]').exists()).toBe(true); // received: false
await flushPromises();
expect(wrapper.find('[data-test="collectionCards"]').exists()).toBe(false);
expect(wrapper.find('[data-test="emptyCollectionWrapper"]').exists()).toBe(true);
});
Thanks in advance.
CodePudding user response:
const wrapper = mount(MyCollections, wrapperOptions);
await wrapper.vm.$nextTick();
expect(wrapper.find('[data-test="loadingCollections"]').exists()).toBe(true);
await flushPromises();
expect(wrapper.find('[data-test="loadingCollections"]').exists()).toBe(false);
Hello! You need to wait until vue is updated the html. Then you need to wait until all promises are done, then you can check again.
The reason for that is Vue updates html in ticks. At first your component mounts with default data. Then the onMounted
hook is called, in which you're setting isLoading
, which updates the DOM in the next tick. You need to await this DOM changes, then you can check the appearance of that element in your tests.
Then you have to wait until api call is ended. This can be done by awaiting flushPromises
(the side effect of what is that will include next tick of DOM update)