Why Vitest mock Axios doesn't work on vuex store testing? - testing

I have the same issue than this post: Why does vitest mock not catch my axios get-requests?
I would like to test my vuex store on vuejs and it works for getters etc but not for actions part with axios get request.
I don't know if it's a good practice to test vuex store than the component in Vue ?
But I guess I need to test both, right ?
a project https://stackblitz.com/edit/vitest-dev-vitest-nyks4u?file=test%2Ftag.spec.js
my js file to test tag.js
import axios from "axios";
const actions = {
async fetchTags({ commit }) {
try {
const response = await axios.get(
CONST_CONFIG.VUE_APP_URLAPI + "tag?OrderBy=id&Skip=0&Take=100"
);
commit("setTags", response.data);
} catch (error) {
console.log(error);
return;
}
},
};
export default {
state,
getters,
actions,
mutations,
};
then my test (tag.spec.js)
import { expect } from "chai";
import { vi } from "vitest";
import axios from "axios";
vi.mock("axios", () => {
return {
default: {
get: vi.fn(),
},
};
});
describe("tag", () => {
test("actions - fetchTags", async () => {
const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Andrew" },
];
axios.get.mockImplementation(() => Promise.resolve({ data: users }));
axios.get.mockResolvedValueOnce(users);
const commit = vi.fn();
await tag.actions.fetchTags({ commit });
expect(axios.get).toHaveBeenCalledTimes(1);
expect(commit).toHaveBeenCalledTimes(1);
});
});
It looks like some other peolpe have the same issues https://github.com/vitest-dev/vitest/issues/1274 but it's still not working.
I try with .ts too but I have exactly the same mistake:
FAIL tests/unit/store/apiObject/tag.spec.js > tag > actions - fetchTags
AssertionError: expected "spy" to be called 1 times
❯ tests/unit/store/apiObject/tag.spec.js:64:24
62| await tag.actions.fetchTags({ commit });
63|
64| expect(axios.get).toHaveBeenCalledTimes(1);
| ^
65| expect(commit).toHaveBeenCalledTimes(1);
66| });
Expected "1"
Received "0"
Thanks a lot for your help.
I finally found the mistake, it was on my vitest.config.ts file, I have to add my global config varaible for my api: import { config } from "#vue/test-utils";
import { defineConfig } from "vitest/config";
import { resolve } from "path";
var configApi = require("./public/config.js");
const { createVuePlugin } = require("vite-plugin-vue2");
const r = (p: string) => resolve(__dirname, p);
export default defineConfig({
test: {
globals: true,
environment: "jsdom",
},
define: {
CONST_CONFIG: configApi,
},
plugins: [createVuePlugin()],
resolve: {
alias: {
"#": r("."),
"~": r("."),
},
// alias: {
// "#": fileURLToPath(new URL("./src", import.meta.url)),
// },
},
});

Related

How can I access useRoute in Cypress Vue?

In my component there are conditionals and labels using route.meta.
import { useRoute } from 'vue-router';
const route = useRoute();
const name = route.meta?.name;
When running the test in Cypress I get an error
TypeError: Cannot read properties of undefined (reading 'meta')
I have attempted to use mocking the route however this doesn't resolve the issue
const mountTalentCommunityDetailsPage = (props: Record<string, unknown>) => {
const mockRoute = {
params: {
meta: {},
},
}
return mount(
{
components: {
MyComponent,
},
template: `<MyComponent/>`,
},
{
global: {
mocks: {
$route: mockRoute,
},
provide: {
useRoute,
},
},
}
)
}
Alternatively I could update name to const name = route.meta?.name; , however I don't think it's great to update this to fit the test
Thank you in advance!

vuex error with quasar $store.auth is undefined

I am trying to use vuex with Quasar. I have created an authentication module as below.
// src/store/auth/index.js
import { api } from 'boot/axios';
export default {
state: {
user: null,
},
getters: {
isAuthenticated: state => !!state.user,
StateUser: state => state.user,
},
mutations: {
setUser(state, username){
state.user = username
},
LogOut(state){
state.user = null
},
},
actions: {
LOGIN: ({ commit }, payload) => {
return new Promise((resolve, reject) => {
api
.post(`/api/login`, payload)
.then(({ data, status }) => {
if (status === 200) {
commit('setUser', data.refresh_token)
resolve(true);
}
})
.catch(error => {
reject(error);
});
});
},
}
}
I imported it in the store
// src/store/index.js
import { store } from 'quasar/wrappers'
import { createStore } from 'vuex'
import auth from './auth'
export default store(function (/* { ssrContext } */) {
const Store = createStore({
modules: {
auth:auth
},
// enable strict mode (adds overhead!)
// for dev mode and --debug builds only
strict: process.env.DEBUGGING
})
return Store
})
And I imported it into MainLayout to check if the user is logged in.
// src/layouts/MainLayout
<template>
</template>
<script>
import { ref, onMounted } from 'vue'
import packageInfo from '../../package.json'
import { useStore } from 'vuex'
export default {
name: 'MainLayout',
setup () {
const $store = useStore;
const connected = ref(false);
function checkLogin(){
//console.log($store)
return connected.value = $store.auth.isAuthenticated
};
onMounted(()=> {
checkLogin();
});
return {
appName: packageInfo.productName,
link:ref('dashboard'),
drawer: ref(false),
miniState: ref(true),
checkLogin,
}
}
}
</script>
But every time, I get the same error :
$store.auth is undefined
I tried to follow the quasar documentation, but I can't. Can anyone tell me what I am doing wrong please?
Thank you.
Someone helped me to find the solution. My error is to have written const $store = useStore instead of const $store = useStore(). Thanks

Is there a way to detect query changes using Vue-Router and successfully get new data?

I am simply making an asynchronous request to get data about a MLB player but am failing to get new data by manually changing the query parameters in the URL. When I use watch, the from and the to are the same for some reason upon debugging with Vue dev tools. However, all works well when I manually click a link to navigate routes as the from and the to correctly represent the from and the to routes.
PitcherForm.vue
export default {
name: "PitcherForm",
components: {
PlayerForm,
},
watch: {
$route() {
this.handleSubmit({ firstName: this.$route.query.firstName, lastName: this.$route.query.lastName });
}
},
methods: {
handleSubmit: function(formValues) {
// this.$store.dispatch("fetchPlayer", { formValues, router: this.$router, player: "pitching" });
this.$store
.dispatch("fetchPlayer", { formValues, player: "pitching" })
.then((promiseObject) => {
console.log(promiseObject)
this.$router.push({
// name: 'PitcherData',
path: "pitching/player",
query: {
firstName: promiseObject.firstName,
lastName: promiseObject.lastName,
},
});
})
.catch((error) => {
console.log(error);
});
},
},
//store.js
import Vue from 'vue';
import Vuex from 'vuex';
import VuexPersist from 'vuex-persist';
import axios from 'axios';
Vue.use(Vuex);
// VuexPersist stuff
const vuexLocalStorage = new VuexPersist({
key: 'vuex',
storage: window.localStorage,
});
export const store = new Vuex.Store({
plugins: [vuexLocalStorage.plugin],
state: {
playerStats: []
},
// mutations, getters, excluded for convenience
actions: {
fetchPlayer({ commit }, data) {
return new Promise((resolve) => {
let firstName = data.formValues.firstName.replace(/\s/g, "").toLowerCase();
let lastName = data.formValues.lastName.replace(/\s/g, "").toLowerCase();
axios.get(`http://localhost:5000/${data.player}/player`,
{
params: {
first: firstName.charAt(0).toUpperCase() + firstName.slice(1),
last: lastName.charAt(0).toUpperCase() + lastName.slice(1),
},
}).then(response => {
commit('setPlayers', response.data);
}).catch((error) => {
console.log(error);
});
resolve({firstName, lastName});
});
}
}
});

How to test namespaced mapAction in Vue

I have a namespaced Vuex store in my Vue 2.6 app one module of which is like this:
//books.js
export default {
state: {
books: null
},
mutations: {
SET_BOOKS(state, books) {
state.books = books;
},
},
actions: {
setBooks: async function ({ commit }) {
//api call
commit('SET_BOOKS', books);
}
},
namespaced: true
};
I want to test a component that calls the setBooks action. I am using mapActions to access the actions. The relevant code is:
//Books.vue
methods: {
...mapActions("books", ["setBooks"]),
}
},
created: async function() {
await this.setBooks();
}
The problem is that my test can't find the action:
import { shallowMount } from '#vue/test-utils';
import Books from '#/views/Books';
import Vuex from 'vuex';
import flushPromises from 'flush-promises';
import { createLocalVue } from '#vue/test-utils'
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Books', () => {
let actions;
let store;
let wrapper;
beforeEach(() => {
store = new Vuex.Store({
state: {
books: {
books: null
}
},
actions: {
setBooks: jest.fn()
}
});
wrapper = shallowMount(Books, { store, localVue })
});
it("renders the books", async () => {
await flushPromises();
expect(actions.setBooks).toHaveBeenCalled();
});
});
I get an error [vuex] module namespace not found in mapActions(): books/
if I try to namespace the actions code in the my test to:
actions: {
books: {
setBooks: jest.fn()
}
}
I get TypeError: Cannot read property 'setBooks' of undefined
Please help, thank you!
The docs for vue-test-utils include an example of testing with modules.
Change your beforeEach:
beforeEach(() => {
actions = { setBooks: jest.fn() }
store = new Vuex.Store({
modules: {
books: {
state: { books: null },
actions
}
}
})
...
})
Your test calls actions.setBooks, but in your original code actions was simply declared, but not used in the creation of your store.

Nuxt nuxtServerInit now being called

I am trying to setup the store/index.js in Nuxt and don't understand why nuxtServerInit is not being called. I have added the console.log to test it out, but it doesn't seem to work or output the log.
import Vuex from 'vuex'
import axios from 'axios'
const createStore = () => {
return new Vuex.Store({
state: {
loadedPosts: []
},
mutations: {
setPosts(state, posts) {
state.loadedPosts = posts;
}
},
actions: {
nuxtServerInit(vuexContext, context) {
console.log('Init works!');
return axios.get("<firebase.link>")
.then(res => {
const postsArray = []
for (const key in res.data) {
postsArray.push({...res.data[key], id: key})
}
vuexContext.commit('setPosts', postsArray)
})
.catch(e => context.error(e))
},
setPosts(vuexContext, posts) {
vuexContext.commit('setPosts', posts)
}
},
getters: {
loadedPosts(state) {
console.log("Here we go",state.loadedPosts);
return state.loadedPosts
}
}
})
}
export default createStore
Fixed it!
The app was in spa mode instead of universal.