Vue Snapshot testing with RouterLinkStub does not render anchor as expected - vue.js

I'm trying to run jest-snapshot tests in a Vue application which has a Component with router-link element.
I'm using RouterLinkStub as recommended in Snapshot Testing with Vue-Router
My component template is something like
<template>
<section class="content">
<div>
<router-link to="/" v-slot="{ href, route, navigate }">
<a :href="href" #click="navigate">
Homepage
</a>
</router-link>
</div>
</section>
</template>
The problem is that in snapshot, the router-link renders as empty anchor element (<a />), so if I change the link text the snapshot test stills pass.
This is my snapshot test:
import { RouterLinkStub, shallowMount } from '#vue/test-utils'
import PageNotFound from 'views/PageNotFound.vue'
describe('PageNotFound.vue', () => {
it('renders as expected', () => {
const wrapper = shallowMount(PageNotFound, {
stubs: {
RouterLink: RouterLinkStub
}
})
expect(wrapper.element).toMatchSnapshot()
})
})
Can you help me please?
Thanks

With Vue 3
import { shallowMount, RouterLinkStub } from '#vue/test-utils'
import router from '../../router'
const findComponentRouterLink = () =>
wrapper.findComponent(RouterLinkStub)
const wrapper = shallowMount(Component, {
global: {
plugins: [router],
stubs: {
RouterLink: RouterLinkStub,
},
},
})
findComponentRouterLink().text()).toBe('Link text')

Related

Cypress component testing with Vue-I18N

I am trying to use Cypress for component testing in my Vue app. I am using the vue-i18n library to provide localisation for the app. When attempting to test the rendering of my loading spinner component, I am getting the following error from the vue-i18n library:
SyntaxError: Need to install with `app.use` function
at createCompileError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:183:17)
at createI18nError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:2625:10)
at useI18n (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:4231:11)
Previously to this, I was getting an error from Pinia. I resolved this by adding the following to cypress/support/component.ts:
import { createPinia, setActivePinia } from 'pinia';
setActivePinia(
createPinia()
);
My LoadingSpinner component code is as follows:
<script setup lang="ts">
import { computed } from "#vue/reactivity";
import { useLocaleStore } from "#/stores/locale";
//props
const { i18n } = useLocaleStore();
</script>
<template>
<div class="d-flex justify-content-center m-5">
<div
class="spinner-border text-primary"
:style="{ width, height }"
role="status"
>
<span class="visually-hidden">{{ i18n.t("loading") }}</span>
</div>
</div>
</template>
And the test code:
import LoadingSpinner from "../../src/components/ui/LoadingSpinner.vue";
describe("LoadingSpinner", () => {
it("renders", () => {
cy.mount(LoadingSpinner);
});
});
/stores/locale:
import { computed } from "vue";
import { defineStore } from "pinia";
import { useI18n } from "vue-i18n";
export const useLocaleStore = defineStore("locale", () => {
const i18n = useI18n({
useScope: "global",
inheritLocale: true,
});
const currentLocale = computed(() => i18n.locale);
const locales = computed(() => i18n.availableLocales);
return { i18n, currentLocale, locales };
});
I found this github release that implies I need to add vue-i18n as a plugin to the mount() call, but I can't work out how to do it. Does anyone know a solution?

vue test utils mount option data not working with compostion api

When i am learning the vue-test-utils from the offical site conditional-rendering.
I tried to change the option api to composition api.
It seems like the mount option data not working with the composition api.
Nav.vue Composition API test FAIL
<template>
<div>
<a id="profile" href="/profile">My Profile</a>
<a v-if="admin" id="admin" href="/admin">Admin</a>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const admin = ref(false)
</script>
Nav.vue Option API test PASS
<template>
<div>
<a id="profile" href="/profile">My Profile</a>
<a v-if="admin" id="admin" href="/admin">Admin</a>
</div>
</template>
<script>
export default {
data() {
return {
admin: false,
}
},
}
</script>
Nav.spec.js test
test('renders an admin link', () => {
const wrapper = mount(Nav, {
data() {
return {
admin: true
}
}
})
// Again, by using `get()` we are implicitly asserting that
// the element exists.
expect(wrapper.get('#admin').text()).toEqual('Admin')
})
I found a solution but I don't know if it's a good solution
test("renders an admin link", async () => {
const wrapper = mount(Nav);
wrapper.vm.admin = true;
await nextTick();
expect(wrapper.get("#admin").text()).toEqual("Admin");
});

Invalid route component

When I want to practice a navigation bar with Vue, encountering a mistake about the invalid route component. In this div cannot display anything. As to the hint of the console, I can find no way out.
the App.vue, hereI only show the Home navigation
<template>
<div id="app">
<tab-bar>
<tab-bar-item class="tab-bar-item" path='/home' activeColor="red">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/Home1.svg" alt="">
<div slot="item-text">Home</div>
</tab-bar>
<router-view></router-view>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar'
import TabBarItem from './components/tabbar/TabBarItem'
export default {
name: 'App',
components: {
TabBar,
TabBarItem
}
}
</script>
the two vue components:
TabBarItem.vue
<template>
<div class="tab-bar-item" #click="itemClick">
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<div v-else><slot name="item-icon-active"></slot></div>
<div :style="activeStyle" :class="{active: isActive}"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default {
name: 'TabBarItem',
props: {
path: String,
activeColor: {
type: String,
default: 'red'
}
},
data() {
return {
isActive: true
}
},
methods: {
itemCilck() {
this.$router.replace();
console.log('itemclick')
}
}
}
</script>
TarBar.vue
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'TabBar'
}
</script>
index.js
import { createRouter, createWebHistory } from 'vue-router'
const Home = () => import('../views/home/Home')
const Categroy = () => import('../views/category/Category')
const Cart = () => import('../views/cart/Cart')
const Profile = () => import('../views/profile/Profile')
// import Home from '../views/Home.vue'
const routes = [
{
path: '/home',
component: Home
},
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
the error
vue-router.esm-bundler.js:3266 Error: Invalid route component
at extractComponentsGuards (vue-router.esm-bundler.js:1994)
at eval (vue-router.esm-bundler.js:3107)
the console picture
According to docs they do not omit file types in dynamic imports, try Home.vue instead of just Home:
const Home = () => import('../views/home/Home.vue')

VueJs / Vuex : Rendering list of items

I'm trying to render a list of offers from my vuex store. The problem is when i'm loading my list of offers page, they are not rendered.
Here is my code :
offers.js
export const namespaced = true
export const state = {}
export const mutations = {
[types.UPDATE_OFFERS] (state, offers) {
Object.assign(state, offers)
}
}
export const actions = {
async fetchOffers ({ commit }) {
const { data } = await axios.get('/api/offers')
commit(types.UPDATE_OFFERS, data)
}
}
offers.vue (my page component)
<template>
<div>
<div v-if="offers"
v-for="offer in offers"
:key="offer.id">
<div>
<div>
<router-link :to="{ name: 'offer', params: { offer: offer.id } }">
{{ offer.project }}
</router-link>
</div>
<div>
<div v-if="offer.versions[0] && offer.versions[0].status == 'edition'">
Validate the offer
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
...mapState(['offers'])
},
beforeMount () {
this.$store.dispatch('offers/fetchOffers')
}
}
</script>
When i'm loading the page, I can see that the store as loaded the offers but the page doesnt render them. The weird thing is when I load the page offers then another page (example createAnOffer) and then I come back to offers, it renders the offers proplery.
I tried beforemount, beforeCreate, mounted, created.
I admit I have no clue what's going on.
Any tip ?
Thank you for your answers.
Louis

vuex module mode in nuxtjs

I'm trying to implement a todo list using modules mode in the vuex store in nuxtjs but get the error this.$store.todo is undefined and cant find much about this relating to nuxt
Can anyone assist please I have
store index.js
export const state = () => ({
})
export const mutations = {
}
store todo.js
export const state = () => ({
todos: [],
})
export const mutations = {
mutations ...
}
export const actions = {
actions ...
}
export const getters = {
getters ...
}
index.vue page
<template>
<div>
<h2>Todos:</h2>
<p> Count: {{ doneTodosCount }} </p>
<ul v-if="todos.length > 0">
<li v-for="(todo, i) in todos" :key="i">
...
</li>
</ul>
<p v-else>Done!</p>
<div class="add-todo">
<input type="text" v-model="newTodoText">
<button #click="add">Add todo</button>
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
name: 'app',
data () {
return {
newTodoText: ""
}
},
created () {
this.$store.todo.dispatch('loadData')
},
computed: {
...mapState(['todos', ]),
...mapGetters([ 'doneTodosCount', 'doneTodos'])
},
methods: {
toggle (todo) {
this.$store.todo.dispatch('toggleTodo', todo)
},
}
}
</script>
From what i read I thought this should work but doesn't
I should add it all works fine if i don't use modules mode and just have a single index.js setup
Many Thanks
You need to call it differently
this.$store.dispatch('todo/toggleTodo', todo)
Also better to call it in fetch method, not created