Skip to content

Instantly share code, notes, and snippets.

@mustafadalga
Created January 8, 2024 10:36
Show Gist options
  • Save mustafadalga/3ecf0857a169cc9c115033b35bc82eaf to your computer and use it in GitHub Desktop.
Save mustafadalga/3ecf0857a169cc9c115033b35bc82eaf to your computer and use it in GitHub Desktop.
Unit Testing Vue.js Composables: Verifying Vuex Store Interaction and Reactive Behavior in useCheckUserPosts
import { createStore } from "vuex";
import type { Post } from "@/types";
export interface ApisLoadState {
isCalled: boolean;
isLoaded: boolean;
}
export interface ApiLoadState extends ApisLoadState {
api: string;
}
export interface State {
posts: Post[];
activeUserID: number;
apisLoadState: {
[key: string]: ApisLoadState;
};
}
const initialState = () => ({
posts: [],
activeUserID: 1,
apisLoadState: {},
});
export const store = createStore<State>({
state: initialState,
getters: {
getPosts: (state: State): State["posts"] => state.posts,
getActiveUserID: (state: State): State["activeUserID"] =>
state.activeUserID,
getApisLoadState: (state: State): State["apisLoadState"] =>
state.apisLoadState,
},
mutations: {
setPosts(state, posts: Post[]) {
state.posts = posts;
},
setActiveUserID: (state, id: number) => state.activeUserID = id,
setApisLoadState: (state, data: ApiLoadState) => {
if (!state.apisLoadState[data.api]) {
state.apisLoadState[data.api] = {
isCalled: false,
isLoaded: false,
};
}
if (Object.prototype.hasOwnProperty.call(data, "isCalled")) {
state.apisLoadState[data.api].isCalled = data.isCalled;
}
if (Object.prototype.hasOwnProperty.call(data, "isLoaded")) {
state.apisLoadState[data.api].isLoaded = data.isLoaded;
}
},
resetStore: (state) => Object.assign(state, initialState()),
},
actions: {
async setPosts({ state, commit }) {
try {
const url = `https://jsonplaceholder.typicode.com/posts?userID=${state.activeUserID}`;
const response = await fetch(url);
const responseJson = await response.json();
commit("setPosts", responseJson);
} catch (error) {
console.error(
`An error occurred during fetching post detail. Please try again: ${error}`
);
}
},
},
});
import { describe, it, beforeEach, vi, expect } from "vitest";
import { defineComponent, onMounted } from "vue";
import { createStore, useStore } from "vuex";
import useCheckUserPosts from "./useCheckUserPosts";
import { mount } from "@vue/test-utils"
import type { State } from "@/store";
const store = createStore<State>({
state: {
posts: [],
activeUserID: 0,
apisLoadState: {
posts: {
isLoaded: false,
isCalled: false
}
}
},
getters: {
getActiveUserID: (state) => state.activeUserID,
},
mutations: {
setApisLoadStates: () => {
},
setActiveUserID: (state, id: number) => state.activeUserID = id,
},
actions: {
setPosts: () => {
}
}
})
const TestComponent = defineComponent({
setup() {
const store = useStore();
useCheckUserPosts();
function changeActiveUserID() {
store.commit("resetStore");
const id = Math.floor(Math.random() * 100) + 1;
store.commit("setActiveUserID", id);
}
onMounted(() => {
changeActiveUserID()
})
},
template: "<h1></h1>"
})
describe("useCheckUserPosts", () => {
beforeEach(() => {
vi.useFakeTimers()
})
it('should watch and fetch posts by active user ID', async () => {
const commitSpy = vi.spyOn(store, 'commit');
const dispatchSpy = vi.spyOn(store, 'dispatch');
mount(TestComponent, {
global: {
plugins: [ store ]
},
});
await vi.runAllTimers();
expect(commitSpy).toHaveBeenCalledWith('setApisLoadState', { api: 'posts', isCalled: true });
expect(dispatchSpy).toHaveBeenCalledWith('setPosts');
});
});
import { computed, watch } from "vue";
import { useStore } from "vuex";
export default function useCheckUserPosts() {
const store = useStore();
const activeUserID = computed(() => store.getters.getActiveUserID);
const handler = async () => {
store.commit("setApisLoadState", { api: "posts", isCalled: true });
await store.dispatch("setPosts");
store.commit("setApisLoadState", { api: "posts", isLoaded: true });
};
watch(() => activeUserID.value, handler, { immediate: true });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment