Im trying to use tests on component that is rendered asynchronously. I have tried to create simple component with async title but the problem still persists. I have added suspense around the component, added await flushPromises(); and also await wrapper.vm.$nextTick(); but nothing helps. Any recommendations?
simpleList.vue component:
<template>
<div>
<h1 v-if="title">{{ title }}</h1>
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
<!-- Slot to render custom content -->
<slot name="customSlot"></slot>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: "SimpleList",
props: {
items: {
type: Array,
required: true,
},
},
async setup() {
const title = ref('');
// Simulate async operation to fetch the title
const fetchTitle = async () => {
return new Promise((resolve) => {
setTimeout(() => {
title.value = 'Async Loaded Title';
resolve();
}, 500); // Simulate async delay
});
};
await fetchTitle();
return {
title,
};
},
};
</script>
test file:
import { mount, flushPromises } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import SimpleList from '@/components/SimpleList.vue';
import { defineComponent } from 'vue';
describe('SimpleList with async setup', () => {
it('renders the title and items when wrapped in Suspense', async () => {
const WrapperComponent = defineComponent({
components: { SimpleList },
template: `
<Suspense>
<template #default>
<SimpleList :items="['Item 1', 'Item 2', 'Item 3']" />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
`
});
const wrapper = mount(WrapperComponent);
// Wait for async operations
await flushPromises();
await flushPromises();
// Ensure Suspense resolves
await wrapper.vm.$nextTick();
// Test for the title rendered from async setup
const title = wrapper.find('h1');
expect(title.exists()).toBe(true);
expect(title.text()).toBe('Async Title');
// Test for the list items
const listItems = wrapper.findAll('li');
expect(listItems).toHaveLength(3);
expect(listItems[0].text()).toBe('Item 1');
expect(listItems[1].text()).toBe('Item 2');
expect(listItems[2].text()).toBe('Item 3');
});
});
Managed to fix it with adding useFakeTimers and then advancingTimersByTime. Fixed test looks like this:
import { mount, flushPromises } from '@vue/test-utils';
import { describe, it, expect, vi } from 'vitest';
import SimpleList from '@/components/rs/RsAnalysis/ImageAnalysis/SimpleList.vue';
import { defineComponent } from 'vue';
describe('SimpleList with async setup', () => {
it('renders the title and items when wrapped in Suspense', async () => {
vi.useFakeTimers();
const WrapperComponent = defineComponent({
components: { SimpleList },
template: `
<Suspense>
<template #default>
<SimpleList :items="['Item 1', 'Item 2', 'Item 3']" />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
`,
});
const wrapper = mount(WrapperComponent);
// Wait for async operations
vi.advanceTimersByTime(1500);
await flushPromises();
// Test for the title rendered from async setup
const title = wrapper.find('h1');
expect(title.exists()).toBe(true);
expect(title.text()).toBe('Async Loaded Title');
// Test for the list items
const listItems = wrapper.findAll('li');
expect(listItems).toHaveLength(3);
expect(listItems[0].text()).toBe('Item 1');
expect(listItems[1].text()).toBe('Item 2');
expect(listItems[2].text()).toBe('Item 3');
});
});
Thanks for the answer in vue-test-utils git: https://github.com/vuejs/test-utils/issues/2528#issuecomment-2429405980