From 105d3b58142dc996ff80dbb4672583280c7ba648 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 15 Nov 2023 12:29:39 +0100 Subject: [PATCH] test: fix front-end tests Signed-off-by: Thomas Citharel --- .gitlab-ci.yml | 2 +- config/dev.exs | 2 +- package-lock.json | 13 +- package.json | 3 +- src/components/Comment/CommentTree.vue | 55 +++--- src/components/Comment/EventComment.vue | 2 +- src/components/Report/ReportCard.vue | 2 +- src/router/index.ts | 4 +- src/views/User/LoginView.vue | 11 +- tests/unit/setup.ts | 9 +- .../components/Comment/CommentTree.spec.ts | 7 +- .../__snapshots__/CommentTree.spec.ts.snap | 56 +++--- .../__snapshots__/GroupSection.spec.ts.snap | 2 +- .../ParticipationWithoutAccount.spec.ts.snap | 6 +- .../__snapshots__/PostListItem.spec.ts.snap | 33 ++-- .../components/Report/ReportCard.spec.ts | 8 +- .../__snapshots__/ReportModal.spec.ts.snap | 16 +- .../components/User/PasswordReset.spec.ts | 37 ++-- .../__snapshots__/PasswordReset.spec.ts.snap | 4 +- .../unit/specs/components/User/login.spec.ts | 72 +++---- .../__snapshots__/navbar.spec.ts.snap | 182 +----------------- tests/unit/specs/mocks/config.ts | 1 + tests/unit/specs/mocks/event.ts | 5 +- vite.config.js | 2 +- 24 files changed, 185 insertions(+), 349 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c5d740f..3b2532ec 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -76,7 +76,7 @@ lint-front: - npm ci script: - npm run lint || export EXITVALUE=1 - - npm run prettier -c . || export EXITVALUE=1 + - npx prettier -c . || export EXITVALUE=1 - exit $EXITVALUE build-frontend: diff --git a/config/dev.exs b/config/dev.exs index 10f7ea0d..1d3f99ee 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -15,7 +15,7 @@ config :mobilizon, Mobilizon.Web.Endpoint, check_origin: false, watchers: [ node: [ - "node_modules/.bin/vite", + "node_modules/.bin/vite" ] ] diff --git a/package-lock.json b/package-lock.json index 06382a8c..e0fd3059 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,7 +119,8 @@ "vite": "^4.0.4", "vite-plugin-pwa": "^0.16.4", "vitest": "^0.34.1", - "vue-i18n-extract": "^2.0.4" + "vue-i18n-extract": "^2.0.4", + "vue-router-mock": "^1.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -11332,6 +11333,16 @@ "vue": "^3.2.0" } }, + "node_modules/vue-router-mock": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vue-router-mock/-/vue-router-mock-1.0.0.tgz", + "integrity": "sha512-j65lh+jhEuEM7sWAz/eenNsEV0VrRXFreNxtIGgLApZkM2h7orImvHSLJ3LhbUa2sJnTtjuy8otSrvoTsdGVYw==", + "dev": true, + "peerDependencies": { + "vue": "^3.2.23", + "vue-router": "^4.0.12" + } + }, "node_modules/vue-scrollto": { "version": "2.20.0", "license": "MIT", diff --git a/package.json b/package.json index 15504382..3e9963f0 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "vite": "^4.0.4", "vite-plugin-pwa": "^0.16.4", "vitest": "^0.34.1", - "vue-i18n-extract": "^2.0.4" + "vue-i18n-extract": "^2.0.4", + "vue-router-mock": "^1.0.0" } } diff --git a/src/components/Comment/CommentTree.vue b/src/components/Comment/CommentTree.vue index c2383779..116909d5 100644 --- a/src/components/Comment/CommentTree.vue +++ b/src/components/Comment/CommentTree.vue @@ -61,7 +61,11 @@ @@ -110,29 +114,30 @@ import { AbsintheGraphQLError } from "@/types/errors.model"; import { useI18n } from "vue-i18n"; import { Notifier } from "@/plugins/notifier"; +const props = defineProps<{ + event: IEvent; + newComment?: IComment; +}>(); + +const event = computed(() => props.event); +const newCommentProps = computed(() => props.newComment); + const { currentActor } = useCurrentActorClient(); const { result: commentsResult, loading: commentsLoading } = useQuery<{ event: Pick; }>( COMMENTS_THREADS_WITH_REPLIES, - () => ({ eventUUID: props.event?.uuid }), - () => ({ enabled: props.event?.uuid !== undefined }) + () => ({ eventUUID: event.value?.uuid }), + () => ({ enabled: event.value?.uuid !== undefined }) ); const comments = computed(() => commentsResult.value?.event.comments ?? []); -const props = defineProps<{ - event: IEvent; - newComment?: IComment; -}>(); - const Editor = defineAsyncComponent( () => import("@/components/TextEditor.vue") ); -const newCommentProps = computed(() => props.newComment); - const newCommentValue = ref(new CommentModel(newCommentProps.value)); const emptyCommentError = ref(false); @@ -170,18 +175,18 @@ const { ) => { if (data == null) return; // comments are attached to the event, so we can pass it to replies later - const newCommentLocal = { ...data.createComment, event: props.event }; + const newCommentLocal = { ...data.createComment, event: event.value }; // we load all existing threads const commentThreadsData = store.readQuery<{ event: IEvent }>({ query: COMMENTS_THREADS_WITH_REPLIES, variables: { - eventUUID: props.event?.uuid, + eventUUID: event.value?.uuid, }, }); if (!commentThreadsData) return; - const { event } = commentThreadsData; - const oldComments = [...event.comments]; + const { event: cachedEvent } = commentThreadsData; + const oldComments = [...cachedEvent.comments]; // if it's no a root comment, we first need to find // existing replies and add the new reply to it @@ -206,12 +211,12 @@ const { query: COMMENTS_THREADS_WITH_REPLIES, data: { event: { - ...event, + ...cachedEvent, comments: oldComments, }, }, variables: { - eventUUID: props.event?.uuid, + eventUUID: event.value?.uuid, }, }); }, @@ -239,10 +244,10 @@ const createCommentForEvent = (comment: IComment) => { if (emptyCommentError.value) return; if (!comment.actor) return; - if (!props.event?.id) return; + if (!event.value?.id) return; createCommentForEventMutation({ - eventId: props.event?.id, + eventId: event.value?.id, text: comment.text, inReplyToCommentId: comment.inReplyToComment?.id, isAnnouncement: comment.isAnnouncement, @@ -266,12 +271,12 @@ const { mutate: deleteComment, onError: deleteCommentMutationError } = const commentsData = store.readQuery<{ event: IEvent }>({ query: COMMENTS_THREADS_WITH_REPLIES, variables: { - eventUUID: props.event?.uuid, + eventUUID: event.value?.uuid, }, }); if (!commentsData) return; - const { event } = commentsData; - let updatedComments: IComment[] = [...event.comments]; + const { event: cachedEvent } = commentsData; + let updatedComments: IComment[] = [...cachedEvent.comments]; if (variables?.originCommentId) { // we have deleted a reply to a thread @@ -309,11 +314,11 @@ const { mutate: deleteComment, onError: deleteCommentMutationError } = store.writeQuery({ query: COMMENTS_THREADS_WITH_REPLIES, variables: { - eventUUID: props.event?.uuid, + eventUUID: event.value?.uuid, }, data: { event: { - ...event, + ...cachedEvent, comments: updatedComments, }, }, @@ -359,14 +364,14 @@ const filteredOrderedComments = computed((): IComment[] => { const isEventOrganiser = computed((): boolean => { const organizerId = - props.event?.organizerActor?.id || props.event?.attributedTo?.id; + event.value?.organizerActor?.id || event.value?.attributedTo?.id; return organizerId !== undefined && currentActor.value?.id === organizerId; }); const areCommentsClosed = computed((): boolean => { return ( currentActor.value?.id !== undefined && - props.event?.options.commentModeration !== CommentModeration.CLOSED + event.value?.options.commentModeration !== CommentModeration.CLOSED ); }); diff --git a/src/components/Comment/EventComment.vue b/src/components/Comment/EventComment.vue index 64d87c76..672daa30 100644 --- a/src/components/Comment/EventComment.vue +++ b/src/components/Comment/EventComment.vue @@ -250,7 +250,7 @@ const props = withDefaults( event: IEvent; currentActor: IPerson; rootComment?: boolean; - readOnly: boolean; + readOnly?: boolean; }>(), { rootComment: true, readOnly: false } ); diff --git a/src/components/Report/ReportCard.vue b/src/components/Report/ReportCard.vue index 18c6bca2..4166acd6 100644 --- a/src/components/Report/ReportCard.vue +++ b/src/components/Report/ReportCard.vue @@ -36,7 +36,7 @@
-
+
{{ t("Reported by someone on {domain}", { diff --git a/src/router/index.ts b/src/router/index.ts index 5d036f47..fb072356 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -189,7 +189,7 @@ routes.push({ redirect: { name: RouteName.PAGE_NOT_FOUND }, }); -export const router = createRouter({ +const router = createRouter({ scrollBehavior, history: createWebHistory("/"), routes, @@ -216,3 +216,5 @@ router.onError((error, to) => { window.location.href = to.fullPath; } }); + +export { router }; diff --git a/src/views/User/LoginView.vue b/src/views/User/LoginView.vue index 2f0a68e0..94888fe4 100644 --- a/src/views/User/LoginView.vue +++ b/src/views/User/LoginView.vue @@ -142,6 +142,7 @@ import RouteName from "@/router/name"; import { LoginError, LoginErrorCode } from "@/types/enums"; import { useCurrentUserClient } from "@/composition/apollo/user"; import { useHead } from "@vueuse/head"; +import { enumTransformer, useRouteQuery } from "vue-use-route-query"; const { t } = useI18n({ useScope: "global" }); const router = useRouter(); @@ -174,9 +175,8 @@ const credentials = reactive({ typeof route.query.password === "string" ? route.query.password : "", }); -const redirect = ref(""); - -const errorCode = ref(null); +const redirect = useRouteQuery("redirect", ""); +const errorCode = useRouteQuery("code", null, enumTransformer(LoginErrorCode)); const { onDone: onLoginMutationDone, @@ -195,6 +195,7 @@ onLoginMutationDone(async (result) => { await setupClientUserAndActors(data.login); if (redirect.value) { + console.debug("We have a redirect", redirect.value); router.push(redirect.value); return; } @@ -293,10 +294,6 @@ const currentProvider = computed(() => { }); onMounted(() => { - const query = route?.query; - errorCode.value = query?.code as LoginErrorCode; - redirect.value = query?.redirect as string | undefined; - // Already-logged-in and accessing /login if (currentUser.value?.isLoggedIn) { console.debug( diff --git a/tests/unit/setup.ts b/tests/unit/setup.ts index 05ec937b..ae673823 100644 --- a/tests/unit/setup.ts +++ b/tests/unit/setup.ts @@ -1,14 +1,7 @@ import "./specs/mocks/matchMedia"; import { config } from "@vue/test-utils"; import { createHead } from "@vueuse/head"; -import { createI18n } from "vue-i18n"; -import en_US from "@/i18n/en_US.json"; - -const i18n = createI18n({ - legacy: false, - messages: { en_US }, - locale: "en_US", -}); +import { i18n } from "@/utils/i18n"; const head = createHead(); diff --git a/tests/unit/specs/components/Comment/CommentTree.spec.ts b/tests/unit/specs/components/Comment/CommentTree.spec.ts index 2b07cf7d..7b47282b 100644 --- a/tests/unit/specs/components/Comment/CommentTree.spec.ts +++ b/tests/unit/specs/components/Comment/CommentTree.spec.ts @@ -119,7 +119,7 @@ describe("CommentTree", () => { await flushPromises(); expect(wrapper.find("p.text-center").exists()).toBe(false); - expect(wrapper.findAllComponents("comment-stub").length).toBe(2); + expect(wrapper.findAllComponents("event-comment-stub").length).toBe(2); expect(wrapper.html()).toMatchSnapshot(); }); @@ -130,6 +130,9 @@ describe("CommentTree", () => { newComment: { text: newCommentForEventMock.text, isAnnouncement: false, + insertedAt: "2021-12-03T13:02:00Z", + updatedAt: "2021-12-03T13:02:00Z", + publishedAt: "2021-12-03T13:02:00Z", }, } ); @@ -137,7 +140,7 @@ describe("CommentTree", () => { await flushPromises(); expect(wrapper.find("form").isVisible()).toBe(true); - expect(wrapper.findAllComponents("comment-stub").length).toBe(2); + expect(wrapper.findAllComponents("event-comment-stub").length).toBe(2); wrapper.getComponent({ ref: "commenteditor" }); wrapper.find("form").trigger("submit"); diff --git a/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap b/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap index 7636fdb1..06bb3813 100644 --- a/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap +++ b/tests/unit/specs/components/Comment/__snapshots__/CommentTree.spec.ts.snap @@ -1,31 +1,31 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`CommentTree > renders a comment tree with comments 1`] = ` "
-
+ -
-
- +
+
+
-
-
-
- +
+
+
+
-
- +
+
- - - - + + + +
" @@ -34,34 +34,34 @@ exports[`CommentTree > renders a comment tree with comments 1`] = ` exports[`CommentTree > renders a loading comment tree 1`] = ` "
-

Loading comments…

+

Loading comments…

" `; exports[`CommentTree > renders an empty comment tree 1`] = ` "
-
+ -
-
- +
+
+
-
-
-
- +
+
+
+
-
- +
+
- - + +
" `; diff --git a/tests/unit/specs/components/Group/__snapshots__/GroupSection.spec.ts.snap b/tests/unit/specs/components/Group/__snapshots__/GroupSection.spec.ts.snap index de0a9aef..7dc38b91 100644 --- a/tests/unit/specs/components/Group/__snapshots__/GroupSection.spec.ts.snap +++ b/tests/unit/specs/components/Group/__snapshots__/GroupSection.spec.ts.snap @@ -1,4 +1,4 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`GroupSection > renders group section with basic informations 1`] = ` "
diff --git a/tests/unit/specs/components/Participation/__snapshots__/ParticipationWithoutAccount.spec.ts.snap b/tests/unit/specs/components/Participation/__snapshots__/ParticipationWithoutAccount.spec.ts.snap index 19854d85..c59631d6 100644 --- a/tests/unit/specs/components/Participation/__snapshots__/ParticipationWithoutAccount.spec.ts.snap +++ b/tests/unit/specs/components/Participation/__snapshots__/ParticipationWithoutAccount.spec.ts.snap @@ -1,11 +1,11 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`ParticipationWithoutAccount > handles being already a participant 1`] = ` "

This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.

- +
@@ -15,7 +15,7 @@ exports[`ParticipationWithoutAccount > handles being already a participant 1`] =
- +
diff --git a/tests/unit/specs/components/Post/__snapshots__/PostListItem.spec.ts.snap b/tests/unit/specs/components/Post/__snapshots__/PostListItem.spec.ts.snap index be813624..cc0c700c 100644 --- a/tests/unit/specs/components/Post/__snapshots__/PostListItem.spec.ts.snap +++ b/tests/unit/specs/components/Post/__snapshots__/PostListItem.spec.ts.snap @@ -1,11 +1,12 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`PostListItem > renders post list item with basic informations 1`] = ` -" +" -
-

My Blog Post

-

Dec 2, 2020

+
+

My Blog Post

+

Dec 2, 2020

+
@@ -13,24 +14,26 @@ exports[`PostListItem > renders post list item with basic informations 1`] = ` `; exports[`PostListItem > renders post list item with publisher name 1`] = ` -"
+" -
-

My Blog Post

-

Dec 2, 2020

+
+

My Blog Post

+

Dec 2, 2020

+ +

Published by An author

-

Published by An author

" `; exports[`PostListItem > renders post list item with tags 1`] = ` -" +" -
-

My Blog Post

-

Dec 2, 2020

-
A tag
+
+

My Blog Post

+

Dec 2, 2020

+
A tag
+
" diff --git a/tests/unit/specs/components/Report/ReportCard.spec.ts b/tests/unit/specs/components/Report/ReportCard.spec.ts index 08ac2e7f..9a0d3338 100644 --- a/tests/unit/specs/components/Report/ReportCard.spec.ts +++ b/tests/unit/specs/components/Report/ReportCard.spec.ts @@ -36,11 +36,7 @@ describe("ReportCard", () => { reportData.reported.name ); - expect(wrapper.find(".flex.gap-1 div p:nth-child(2)").text()).toBe( - `@${reportData.reported.preferredUsername}` - ); - - expect(wrapper.find(".reported_by div:first-child").text()).toBe( + expect(wrapper.find(".reported_by span:first-child").text()).toBe( `Reported by John Snow` ); }); @@ -50,7 +46,7 @@ describe("ReportCard", () => { reporter: { domain: "somewhere.else", type: ActorType.APPLICATION }, }); - expect(wrapper.find(".reported_by div:first-child").text()).toBe( + expect(wrapper.find(".reported_by span:first-child").text()).toBe( "Reported by someone on somewhere.else" ); }); diff --git a/tests/unit/specs/components/Report/__snapshots__/ReportModal.spec.ts.snap b/tests/unit/specs/components/Report/__snapshots__/ReportModal.spec.ts.snap index d85250eb..511f9797 100644 --- a/tests/unit/specs/components/Report/__snapshots__/ReportModal.spec.ts.snap +++ b/tests/unit/specs/components/Report/__snapshots__/ReportModal.spec.ts.snap @@ -1,16 +1,16 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`ReportModal > renders report modal with basic informations and submits it 1`] = ` -"
+"
-
+

The report will be sent to the moderators of your instance. You can explain why you report this content below.

-
+
-
-
+
+
@@ -20,9 +20,9 @@ exports[`ReportModal > renders report modal with basic informations and submits
-
" diff --git a/tests/unit/specs/components/User/PasswordReset.spec.ts b/tests/unit/specs/components/User/PasswordReset.spec.ts index 7dd17b4f..f30be1b6 100644 --- a/tests/unit/specs/components/User/PasswordReset.spec.ts +++ b/tests/unit/specs/components/User/PasswordReset.spec.ts @@ -1,9 +1,3 @@ -const useRouterMock = vi.fn(() => ({ - push: function () { - // do nothing - }, -})); - import { config, mount } from "@vue/test-utils"; import PasswordReset from "@/views/User/PasswordReset.vue"; import { createMockClient, RequestHandler } from "mock-apollo-client"; @@ -11,15 +5,17 @@ import { RESET_PASSWORD } from "@/graphql/auth"; import { resetPasswordResponseMock } from "../../mocks/auth"; import RouteName from "@/router/name"; import flushPromises from "flush-promises"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { DefaultApolloClient } from "@vue/apollo-composable"; import Oruga from "@oruga-ui/oruga-next"; +import { + VueRouterMock, + createRouterMock, + injectRouterMock, +} from "vue-router-mock"; config.global.plugins.push(Oruga); - -vi.mock("vue-router/dist/vue-router.mjs", () => ({ - useRouter: useRouterMock, -})); +config.plugins.VueWrapper.install(VueRouterMock); let requestHandlers: Record; @@ -58,8 +54,21 @@ const generateWrapper = ( }; describe("Reset page", () => { + const router = createRouterMock({ + spy: { + create: (fn) => vi.fn(fn), + reset: (spy) => spy.mockReset(), + }, + }); + beforeEach(() => { + // inject it globally to ensure `useRoute()`, `$route`, etc work + // properly and give you access to test specific functions + injectRouterMock(router); + }); + it("renders correctly", () => { const wrapper = generateWrapper(); + expect(wrapper.router).toBe(router); expect(wrapper.findAll('input[type="password"').length).toBe(2); expect(wrapper.html()).toMatchSnapshot(); }); @@ -93,10 +102,6 @@ describe("Reset page", () => { }); it("redirects to homepage if token is valid", async () => { - const push = vi.fn(); // needs to write this code before render() - useRouterMock.mockImplementationOnce(() => ({ - push, - })); const wrapper = generateWrapper(); wrapper @@ -110,6 +115,6 @@ describe("Reset page", () => { token: "some-token", }); await flushPromises(); - expect(push).toHaveBeenCalledWith({ name: RouteName.HOME }); + expect(wrapper.router.push).toHaveBeenCalledWith({ name: RouteName.HOME }); }); }); diff --git a/tests/unit/specs/components/User/__snapshots__/PasswordReset.spec.ts.snap b/tests/unit/specs/components/User/__snapshots__/PasswordReset.spec.ts.snap index f5385ff1..0deaa747 100644 --- a/tests/unit/specs/components/User/__snapshots__/PasswordReset.spec.ts.snap +++ b/tests/unit/specs/components/User/__snapshots__/PasswordReset.spec.ts.snap @@ -1,4 +1,4 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Reset page > renders correctly 1`] = ` "
@@ -25,7 +25,7 @@ exports[`Reset page > renders correctly 1`] = ` exports[`Reset page > shows error if token is invalid 1`] = ` "

Password reset

- +
diff --git a/tests/unit/specs/components/User/login.spec.ts b/tests/unit/specs/components/User/login.spec.ts index e19a58fc..c1e42f10 100644 --- a/tests/unit/specs/components/User/login.spec.ts +++ b/tests/unit/specs/components/User/login.spec.ts @@ -1,15 +1,3 @@ -const useRouterMock = vi.fn(() => ({ - push: function () { - // do nothing - }, - replace: function () { - // do nothing - }, -})); -const useRouteMock = vi.fn(function () { - // do nothing -}); - import { config, mount, VueWrapper } from "@vue/test-utils"; import Login from "@/views/User/LoginView.vue"; import { @@ -26,23 +14,37 @@ import { CURRENT_USER_CLIENT } from "@/graphql/user"; import { ICurrentUser } from "@/types/current-user.model"; import flushPromises from "flush-promises"; import RouteName from "@/router/name"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DefaultApolloClient } from "@vue/apollo-composable"; import Oruga from "@oruga-ui/oruga-next"; import { cache } from "@/apollo/memory"; - -vi.mock("vue-router/dist/vue-router.mjs", () => ({ - useRouter: useRouterMock, - useRoute: useRouteMock, -})); +import { + VueRouterMock, + createRouterMock, + injectRouterMock, + getRouter, +} from "vue-router-mock"; config.global.plugins.push(Oruga); +config.plugins.VueWrapper.install(VueRouterMock); describe("Render login form", () => { let wrapper: VueWrapper; let mockClient: MockApolloClient | null; let requestHandlers: Record; + const router = createRouterMock({ + spy: { + create: (fn) => vi.fn(fn), + reset: (spy) => spy.mockReset(), + }, + }); + beforeEach(() => { + // inject it globally to ensure `useRoute()`, `$route`, etc work + // properly and give you access to test specific functions + injectRouterMock(router); + }); + const generateWrapper = ( handlers: Record = {}, customProps: Record = {}, @@ -90,6 +92,7 @@ describe("Render login form", () => { generateWrapper(); await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); + await flushPromises(); expect(wrapper.exists()).toBe(true); expect(requestHandlers.configQueryHandler).toHaveBeenCalled(); @@ -103,15 +106,10 @@ describe("Render login form", () => { }); it("renders and submits the login form", async () => { - const replace = vi.fn(); // needs to write this code before render() - const push = vi.fn(); - useRouterMock.mockImplementationOnce(() => ({ - replace, - push, - })); generateWrapper(); await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); + await flushPromises(); expect(wrapper.exists()).toBe(true); expect(requestHandlers.configQueryHandler).toHaveBeenCalled(); @@ -133,16 +131,12 @@ describe("Render login form", () => { expect(currentUser?.email).toBe("some@email.tld"); expect(currentUser?.id).toBe("1"); await flushPromises(); - expect(replace).toHaveBeenCalledWith({ name: RouteName.HOME }); + expect(wrapper.router.replace).toHaveBeenCalledWith({ + name: RouteName.HOME, + }); }); it("handles a login error", async () => { - const replace = vi.fn(); // needs to write this code before render() - const push = vi.fn(); - useRouterMock.mockImplementationOnce(() => ({ - push, - replace, - })); generateWrapper({ loginMutationHandler: vi.fn().mockResolvedValue({ errors: [ @@ -156,6 +150,7 @@ describe("Render login form", () => { await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); + await flushPromises(); expect(wrapper.exists()).toBe(true); expect(requestHandlers.configQueryHandler).toHaveBeenCalled(); @@ -170,28 +165,21 @@ describe("Render login form", () => { expect(wrapper.find(".o-notification--danger").text()).toContain( "Impossible to authenticate, either your email or password are invalid." ); - expect(push).not.toHaveBeenCalled(); + expect(wrapper.router.push).not.toHaveBeenCalled(); }); it("handles redirection after login", async () => { - const replace = vi.fn(); // needs to write this code before render() - const push = vi.fn(); - useRouterMock.mockImplementationOnce(() => ({ - replace, - push, - })); - useRouteMock.mockImplementationOnce(() => ({ - query: { redirect: "/about/instance" }, - })); generateWrapper(); + getRouter().setQuery({ redirect: "/about/instance" }); await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); + await flushPromises(); wrapper.find('form input[type="email"]').setValue("some@email.tld"); wrapper.find('form input[type="password"]').setValue("somepassword"); wrapper.find("form").trigger("submit"); await flushPromises(); - expect(push).toHaveBeenCalledWith("/about/instance"); + expect(wrapper.router.push).toHaveBeenCalledWith("/about/instance"); }); }); diff --git a/tests/unit/specs/components/__snapshots__/navbar.spec.ts.snap b/tests/unit/specs/components/__snapshots__/navbar.spec.ts.snap index 13e592eb..cf310114 100644 --- a/tests/unit/specs/components/__snapshots__/navbar.spec.ts.snap +++ b/tests/unit/specs/components/__snapshots__/navbar.spec.ts.snap @@ -1,9 +1,9 @@ -// Vitest Snapshot v1 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`App component > renders a Vue component 1`] = ` " -" +" `; diff --git a/tests/unit/specs/mocks/config.ts b/tests/unit/specs/mocks/config.ts index b404b4fa..eb2924be 100644 --- a/tests/unit/specs/mocks/config.ts +++ b/tests/unit/specs/mocks/config.ts @@ -134,6 +134,7 @@ export const loginMock = { __typename: "Config", auth: { __typename: "Auth", + databaseLogin: true, oauthProviders: [], }, registrationsOpen: true, diff --git a/tests/unit/specs/mocks/event.ts b/tests/unit/specs/mocks/event.ts index 6410f098..20679474 100644 --- a/tests/unit/specs/mocks/event.ts +++ b/tests/unit/specs/mocks/event.ts @@ -106,6 +106,7 @@ export const eventCommentThreadsMock = { }, deletedAt: null, insertedAt: "2020-12-03T09:02:00Z", + publishedAt: "2020-12-03T09:02:00Z", isAnnouncement: false, language: "en", }, @@ -137,6 +138,7 @@ export const eventCommentThreadsMock = { }, deletedAt: null, insertedAt: "2020-12-03T11:02:00Z", + publishedAt: "2020-12-03T11:02:00Z", isAnnouncement: false, language: "en", }, @@ -164,7 +166,6 @@ export const newCommentForEventResponse: DataMock = { local: true, visibility: "PUBLIC", totalReplies: 0, - updatedAt: "2020-12-03T13:02:00Z", originComment: null, inReplyToComment: null, replies: [], @@ -183,6 +184,8 @@ export const newCommentForEventResponse: DataMock = { }, deletedAt: null, insertedAt: "2020-12-03T13:02:00Z", + updatedAt: "2020-12-03T13:02:00Z", + publishedAt: "2020-12-03T13:02:00Z", isAnnouncement: false, language: "en", }, diff --git a/vite.config.js b/vite.config.js index e20c7d60..10d95904 100644 --- a/vite.config.js +++ b/vite.config.js @@ -114,7 +114,7 @@ export default defineConfig(({ command }) => { coverage: { reporter: ["text", "json", "html"], }, - setupFiles: path.resolve(__dirname, "./tests/unit/setup.ts"), + setupFiles: [path.resolve(__dirname, "./tests/unit/setup.ts")], include: [path.resolve(__dirname, "./tests/unit/specs/**/*.spec.ts")], }, };