i'm learning Vue 3 using composition api, and im very new to Vitest aswell (i know it uses Vue Test Utils though).
So, in short i'm having a problem where I mock the Vue Router, trigger click on a router-link element and expect the router to have been called, but it fails.
First of all and just in case, these are the versions I'm using on the dependencies that might be relevant for this matter:
"dependencies": {
"vue": "^3.3.2",
"vue-router": "^4.2.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/test-utils": "^2.3.2",
"jsdom": "^22.0.0",
"start-server-and-test": "^2.0.0",
"vite": "^4.3.5",
"vitest": "^0.31.0"
}
I have a simple view to test this:
TestView.vue
<template>
<router-link data-test="test-link" to="login">test link</router-link>
</template>
And I have tried several ways to make the test but here go the 2 that I think would make more sense:
1 - With mockImplementationOnce, from VTU Docs
testView.test.js
import { describe, expect, test, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { useRouter } from 'vue-router'
import TestView from '../../views/TestView.vue'
vi.mock('vue-router', () => ({
useRoute: vi.fn(),
useRouter: vi.fn(() => ({
push: () => {}
}))
}))
describe('Test View', () => {
test('Login button redirects to login view', async () => {
const push = vi.fn()
useRouter.mockImplementationOnce(() => ({
push
}))
const wrapper = mount(TestView, {
global: {
stubs: ['router-link'] // tried removing this stub aswell
}
})
const btn = wrapper.get('[data-test="test-link"]')
await btn.trigger('click')
// Few things to note here:
// I also tried adding await nextTick() and even a promise with 1s timeout to make sure
// the button is clicked before the assertion happens, but doesn't help.
// Also it's not like the button isn't found, the following assert succeeds
expect(btn.attributes('to')).toEqual('login')
// Output: AssertionError: expected "spy" to be called at least once
expect(push).toHaveBeenCalled()
})
})
2- With mockReturnValue, saw in this article
testView.test.js
import { beforeEach, describe, expect, test, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { useRouter } from 'vue-router'
import TestView from '../../views/TestView.vue'
vi.mock('vue-router')
describe('Test View', () => {
useRouter.mockReturnValue({
push: vi.fn()
})
beforeEach(() => {
useRouter().push.mockReset()
})
test('Login button redirects to login view', async () => {
const wrapper = mount(TestView, {
global: {
stubs: ['router-link'] // tried removing this stub aswell
}
})
const btn = wrapper.get('[data-test="test-link"]')
await btn.trigger('click')
// Few things to note here:
// I also tried adding await nextTick() and even a promise with 1s timeout to make sure
// the button is clicked before the assertion happens, but doesn't help.
// Also it's not like the button isn't found, the following assert succeeds
expect(btn.attributes('to')).toEqual('login')
// Output: AssertionError: expected "spy" to be called at least once
expect(useRouter().push).toHaveBeenCalled()
})
})
So, in both cases I get the same error:
AssertionError: expected "spy" to be called at least once
Every other potential solutions I've found are neither working for me, many of them are most likely outdated.
Anyone sees what I'm missing or if there is another way to do this?
Thank you in advance and sorry if I didn't fully follow the guidelines, it's the first time I ask something here.