I am setting up some tests for a component that is dependent on a pinia store but I'm currently unable to update the store state to affect the component during the tests;
My Component looks like this:
<script setup lang="ts">
import { ComputedRef } from 'vue';
import { storeToRefs } from 'pinia';
import { RouterLink } from 'vue-router';
import { useBooksStore } from '@/stores/books';
interface BookStore {
pageInfo: PageInfo;
nextPage: ComputedRef<number>;
previousPage: ComputedRef<number>;
}
const bookStore = useBooksStore();
const { pageInfo, nextPage, previousPage }: BookStore = storeToRefs(bookStore);
</script>
<template>
<nav class="book-nav">
<RouterLink
class="button"
data-test-id="previous-page"
:class="{ 'button--disabled': previousPage < 1 }"
:to="`/books/${previousPage}`">
<
</RouterLink>
<span>
Page
<span data-test-id="page-no">{{ pageInfo.page }}</span>
of
<span data-test-id="total-pages">{{ pageInfo.totalPages }}</span>
</span>
<RouterLink
class="button"
data-test-id="next-page"
:class="{ 'button--disabled': nextPage > pageInfo.totalPages }"
:to="`/books/${nextPage}`">
>
</RouterLink>
</nav>
</template>
<style lang="scss" scoped>
.book-nav {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
}
</style>
My Store looks like this
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
export const useBooksStore = defineStore('books', () => {
const pageInfo = ref({
page: 1,
totalPages: 5,
});
const nextPage = computed<number>(() => pageInfo.value.page + 1);
const previousPage = computed<number>(() => pageInfo.value.page - 1);
const updatePage = (page: number) => {
const clampedPage = Math.min(Math.max(page, 1), pageInfo.value.totalPages);
pageInfo.value.page = clampedPage;
};
return { pageInfo, nextPage, previousPage, updatePage };
});
and this is my test for the BookNav component
import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';
import { shallowMount, RouterLinkStub, VueWrapper } from '@vue/test-utils';
import { createTestingPinia, type TestingPinia } from '@pinia/testing';
import { setActivePinia, createPinia } from 'pinia';
import { useBooksStore } from '@/stores/books';
import BookNav from './BookNav.vue';
const PREV_PAGE = '[data-test-id=previous-page]';
const NEXT_PAGE = '[data-test-id=next-page]';
const PAGE_NO = '[data-test-id=page-no]';
const TOTAL_PAGES = '[data-test-id=total-pages]';
let wrapper: VueWrapper;
let pinia: TestingPinia;
let store;
describe('shows the current and total pages', () => {
beforeEach(() => {
pinia = createTestingPinia({
createSpy: vi.fn,
stubActions: false,
});
wrapper = shallowMount(BookNav, {
global: {
stubs: {
RouterLink: RouterLinkStub,
},
plugins: [pinia],
},
});
store = useBooksStore();
});
afterEach(() => {
wrapper.unmount();
});
it('/books shows the current and total pages', () => {
expect(
wrapper.find(PREV_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/0');
expect(wrapper.find(PREV_PAGE).classes()).toContain('button--disabled');
expect(wrapper.find(PAGE_NO).text()).toContain(1);
expect(wrapper.find(TOTAL_PAGES).text()).toContain(5);
expect(
wrapper.find(NEXT_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/2');
});
it('/books/2 updates links and pages on next page', async () => {
store.updatePage(2);
expect(
wrapper.find(PREV_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/1');
expect(wrapper.find(PREV_PAGE).classes()).not.toContain('button--disabled');
expect(wrapper.find(PAGE_NO).text()).toContain(2);
expect(wrapper.find(TOTAL_PAGES).text()).toContain(5);
expect(
wrapper.find(NEXT_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/3');
});
});
The first test is completing correctly as it relies on the default values but the 2nd test is failing because the state isn't being updates from this initial state
FAIL src/components/books/BookNav.spec.ts > shows the current and total pages > /books/2 updates links and pages on next page
AssertionError: expected '/books/0' to deeply equal '/books/1'
Do I need to wait for the DOM to update or something?
I am setting up some tests for a component that is dependent on a pinia store but I'm currently unable to update the store state to affect the component during the tests;
My Component looks like this:
<script setup lang="ts">
import { ComputedRef } from 'vue';
import { storeToRefs } from 'pinia';
import { RouterLink } from 'vue-router';
import { useBooksStore } from '@/stores/books';
interface BookStore {
pageInfo: PageInfo;
nextPage: ComputedRef<number>;
previousPage: ComputedRef<number>;
}
const bookStore = useBooksStore();
const { pageInfo, nextPage, previousPage }: BookStore = storeToRefs(bookStore);
</script>
<template>
<nav class="book-nav">
<RouterLink
class="button"
data-test-id="previous-page"
:class="{ 'button--disabled': previousPage < 1 }"
:to="`/books/${previousPage}`">
<
</RouterLink>
<span>
Page
<span data-test-id="page-no">{{ pageInfo.page }}</span>
of
<span data-test-id="total-pages">{{ pageInfo.totalPages }}</span>
</span>
<RouterLink
class="button"
data-test-id="next-page"
:class="{ 'button--disabled': nextPage > pageInfo.totalPages }"
:to="`/books/${nextPage}`">
>
</RouterLink>
</nav>
</template>
<style lang="scss" scoped>
.book-nav {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
}
</style>
My Store looks like this
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
export const useBooksStore = defineStore('books', () => {
const pageInfo = ref({
page: 1,
totalPages: 5,
});
const nextPage = computed<number>(() => pageInfo.value.page + 1);
const previousPage = computed<number>(() => pageInfo.value.page - 1);
const updatePage = (page: number) => {
const clampedPage = Math.min(Math.max(page, 1), pageInfo.value.totalPages);
pageInfo.value.page = clampedPage;
};
return { pageInfo, nextPage, previousPage, updatePage };
});
and this is my test for the BookNav component
import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';
import { shallowMount, RouterLinkStub, VueWrapper } from '@vue/test-utils';
import { createTestingPinia, type TestingPinia } from '@pinia/testing';
import { setActivePinia, createPinia } from 'pinia';
import { useBooksStore } from '@/stores/books';
import BookNav from './BookNav.vue';
const PREV_PAGE = '[data-test-id=previous-page]';
const NEXT_PAGE = '[data-test-id=next-page]';
const PAGE_NO = '[data-test-id=page-no]';
const TOTAL_PAGES = '[data-test-id=total-pages]';
let wrapper: VueWrapper;
let pinia: TestingPinia;
let store;
describe('shows the current and total pages', () => {
beforeEach(() => {
pinia = createTestingPinia({
createSpy: vi.fn,
stubActions: false,
});
wrapper = shallowMount(BookNav, {
global: {
stubs: {
RouterLink: RouterLinkStub,
},
plugins: [pinia],
},
});
store = useBooksStore();
});
afterEach(() => {
wrapper.unmount();
});
it('/books shows the current and total pages', () => {
expect(
wrapper.find(PREV_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/0');
expect(wrapper.find(PREV_PAGE).classes()).toContain('button--disabled');
expect(wrapper.find(PAGE_NO).text()).toContain(1);
expect(wrapper.find(TOTAL_PAGES).text()).toContain(5);
expect(
wrapper.find(NEXT_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/2');
});
it('/books/2 updates links and pages on next page', async () => {
store.updatePage(2);
expect(
wrapper.find(PREV_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/1');
expect(wrapper.find(PREV_PAGE).classes()).not.toContain('button--disabled');
expect(wrapper.find(PAGE_NO).text()).toContain(2);
expect(wrapper.find(TOTAL_PAGES).text()).toContain(5);
expect(
wrapper.find(NEXT_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/3');
});
});
The first test is completing correctly as it relies on the default values but the 2nd test is failing because the state isn't being updates from this initial state
FAIL src/components/books/BookNav.spec.ts > shows the current and total pages > /books/2 updates links and pages on next page
AssertionError: expected '/books/0' to deeply equal '/books/1'
Do I need to wait for the DOM to update or something?
Share Improve this question asked Feb 16 at 22:14 overbyteoverbyte 6489 silver badges25 bronze badges1 Answer
Reset to default 2Okay - sounds like I was on the right track.
I imported nextTick()
from vue and awaited it before running the tests
import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';
import { nextTick } from 'vue';
import { shallowMount, RouterLinkStub, VueWrapper } from '@vue/test-utils';
import { createTestingPinia, type TestingPinia } from '@pinia/testing';
import { useBooksStore } from '@/stores/books';
import BookNav from './BookNav.vue';
const PREV_PAGE = '[data-test-id=previous-page]';
const NEXT_PAGE = '[data-test-id=next-page]';
const PAGE_NO = '[data-test-id=page-no]';
const TOTAL_PAGES = '[data-test-id=total-pages]';
let wrapper: VueWrapper;
let pinia: TestingPinia;
let store: ReturnType<typeof useBooksStore>;
describe('shows the current and total pages', () => {
beforeEach(() => {
pinia = createTestingPinia({
createSpy: vi.fn,
stubActions: false,
});
wrapper = shallowMount(BookNav, {
global: {
stubs: {
RouterLink: RouterLinkStub,
},
plugins: [pinia],
},
});
store = useBooksStore();
});
afterEach(() => {
wrapper.unmount();
});
it('/books shows the current and total pages', () => {
expect(
wrapper.find(PREV_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/0');
expect(wrapper.find(PREV_PAGE).classes()).toContain('button--disabled');
expect(wrapper.find(PAGE_NO).text()).toContain(1);
expect(wrapper.find(TOTAL_PAGES).text()).toContain(5);
expect(
wrapper.find(NEXT_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/2');
});
it('/books/2 updates links and pages on next page', async () => {
store.updatePage(2);
// ⚠️ added next tick to wait for vue to update
await nextTick();
expect(
wrapper.find(PREV_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/1');
expect(wrapper.find(PREV_PAGE).classes()).not.toContain('button--disabled');
expect(wrapper.find(PAGE_NO).text()).toContain(2);
expect(wrapper.find(TOTAL_PAGES).text()).toContain(5);
expect(
wrapper.find(NEXT_PAGE).findComponent(RouterLinkStub).props('to')
).toEqual('/books/3');
});
});