feat: test onboarding redirects
This commit is contained in:
parent
bfeca6e8e0
commit
3644a0d77d
|
|
@ -68,6 +68,10 @@ export const itDelete = (url: RequestInfo) => {
|
|||
return itPost(url, {}, { method: "DELETE" });
|
||||
};
|
||||
|
||||
export const itPut = (url: RequestInfo, data: unknown) => {
|
||||
return itPost(url, data, { method: "PUT" });
|
||||
};
|
||||
|
||||
const itGetPromiseCache = new Map<string, Promise<any>>();
|
||||
|
||||
export function bustItGetCache(key?: string) {
|
||||
|
|
|
|||
|
|
@ -1,27 +1,49 @@
|
|||
<script setup lang="ts">
|
||||
import WizardPage from "@/components/onboarding/WizardPage.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import AvatarImage from "@/components/ui/AvatarImage.vue";
|
||||
import { useFileUpload } from "@/composables";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useFetch } from "@/fetchHelpers";
|
||||
import { itPut, useFetch } from "@/fetchHelpers";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
import { profileNextRoute } from "@/services/onboarding";
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
type Organisation = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const user = useUserStore();
|
||||
const route = useRoute();
|
||||
|
||||
const { data: companies } = useFetch(() => "/api/core/organisations/");
|
||||
const fetchResult = useFetch("/api/core/organisations/");
|
||||
const organisations: Ref<Organisation[] | null> = fetchResult.data;
|
||||
|
||||
const selectedCompany = ref({
|
||||
const selectedOrganisation = ref({
|
||||
id: "0",
|
||||
name: t("a.Auswählen"),
|
||||
});
|
||||
const validCompany = computed(() => {
|
||||
return selectedCompany.value.id !== "0";
|
||||
|
||||
watch(
|
||||
organisations,
|
||||
(newOrganisations) => {
|
||||
if (newOrganisations) {
|
||||
const userOrganisation = newOrganisations.find((c) => c.id === user.organisation);
|
||||
if (userOrganisation) {
|
||||
selectedOrganisation.value = userOrganisation;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const validOrganisation = computed(() => {
|
||||
return selectedOrganisation.value.id !== "0";
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -35,18 +57,15 @@ watch(avatarFileInfo, (info) => {
|
|||
console.log("fileInfo changed", info);
|
||||
});
|
||||
|
||||
watch(selectedCompany, (company) => {
|
||||
console.log("company changed", company);
|
||||
watch(selectedOrganisation, (organisation) => {
|
||||
console.log("organisation changed", organisation);
|
||||
itPut("/api/core/me/", {
|
||||
organisation: organisation.id,
|
||||
});
|
||||
});
|
||||
|
||||
const nextRoute = computed(() => {
|
||||
if (route.params.courseType === "uk") {
|
||||
return "setupComplete";
|
||||
}
|
||||
if (route.params.courseType === "vv") {
|
||||
return "checkoutAddress";
|
||||
}
|
||||
return "";
|
||||
return profileNextRoute(route.params.courseType);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -62,7 +81,11 @@ const nextRoute = computed(() => {
|
|||
andere Personen einfacher finden.
|
||||
</p>
|
||||
|
||||
<ItDropdownSelect v-if="companies" v-model="selectedCompany" :items="companies" />
|
||||
<ItDropdownSelect
|
||||
v-if="organisations"
|
||||
v-model="selectedOrganisation"
|
||||
:items="organisations"
|
||||
/>
|
||||
|
||||
<div class="mt-16 flex flex-col justify-between gap-12 lg:flex-row lg:gap-24">
|
||||
<div>
|
||||
|
|
@ -94,7 +117,7 @@ const nextRoute = computed(() => {
|
|||
<template #footer>
|
||||
<router-link v-slot="{ navigate }" :to="{ name: nextRoute }" custom>
|
||||
<button
|
||||
:disabled="!validCompany"
|
||||
:disabled="!validOrganisation"
|
||||
class="btn-blue flex items-center"
|
||||
role="link"
|
||||
@click="navigate"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
import { createPinia, setActivePinia } from "pinia";
|
||||
import { beforeEach, describe, expect, vi } from "vitest";
|
||||
|
||||
import { START_LOCATION } from "vue-router";
|
||||
import { useUserStore } from "../../stores/user";
|
||||
import { onboardingRedirect } from "../onboarding";
|
||||
|
||||
describe("Onboarding", () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
});
|
||||
|
||||
it("redirect guest", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = false;
|
||||
const mock = vi.fn();
|
||||
onboardingRedirect(routeLocation("accountConfirm", "uk"), START_LOCATION, mock);
|
||||
expect(mock).toHaveBeenCalledWith({
|
||||
name: "accountCreate",
|
||||
params: { courseType: "uk" },
|
||||
});
|
||||
});
|
||||
|
||||
it("redirect logged-in user from accountCreate to accountConfirm", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = true;
|
||||
const mockNext = vi.fn();
|
||||
onboardingRedirect(routeLocation("accountCreate", "uk"), START_LOCATION, mockNext);
|
||||
expect(mockNext).toHaveBeenCalledWith({
|
||||
name: "accountConfirm",
|
||||
params: { courseType: "uk" },
|
||||
});
|
||||
});
|
||||
|
||||
it("UK: redirect to profile next route for logged-in user with organisation", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = true;
|
||||
user.organisation = "1";
|
||||
const mockNext = vi.fn();
|
||||
onboardingRedirect(routeLocation("accountConfirm", "uk"), START_LOCATION, mockNext);
|
||||
expect(mockNext).toHaveBeenCalledWith({
|
||||
name: "setupComplete",
|
||||
params: { courseType: "uk" },
|
||||
});
|
||||
});
|
||||
|
||||
it("VV: redirect to profile next route for logged-in user with organisation", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = true;
|
||||
user.organisation = "1";
|
||||
const mockNext = vi.fn();
|
||||
onboardingRedirect(routeLocation("accountConfirm", "vv"), START_LOCATION, mockNext);
|
||||
expect(mockNext).toHaveBeenCalledWith({
|
||||
name: "checkoutAddress",
|
||||
params: { courseType: "vv" },
|
||||
});
|
||||
});
|
||||
|
||||
it("no redirect for logged-in user without organisation to accountConfirm", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = true;
|
||||
user.organisation = "";
|
||||
const mockNext = vi.fn();
|
||||
onboardingRedirect(routeLocation("accountConfirm", "uk"), START_LOCATION, mockNext);
|
||||
expect(mockNext).toHaveBeenCalledWith(); // No arguments passed means no redirection
|
||||
});
|
||||
|
||||
it("no redirect for logged-in user to a non-relevant route", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = true;
|
||||
const mockNext = vi.fn();
|
||||
onboardingRedirect(routeLocation("someOtherRoute", "uk"), START_LOCATION, mockNext);
|
||||
expect(mockNext).toHaveBeenCalledWith(); // No arguments passed means no redirection
|
||||
});
|
||||
|
||||
it("no redirect for guest on accountCreate page", () => {
|
||||
const user = useUserStore();
|
||||
user.loggedIn = false;
|
||||
const mockNext = vi.fn();
|
||||
onboardingRedirect(routeLocation("accountCreate", "uk"), START_LOCATION, mockNext);
|
||||
expect(mockNext).toHaveBeenCalledWith(); // No arguments passed means no redirection
|
||||
});
|
||||
});
|
||||
|
||||
function routeLocation(name: string, courseType: string) {
|
||||
return {
|
||||
fullPath: "",
|
||||
hash: "",
|
||||
matched: [],
|
||||
meta: {},
|
||||
name: name,
|
||||
params: {
|
||||
courseType,
|
||||
},
|
||||
path: "",
|
||||
query: {},
|
||||
redirectedFrom: undefined,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,25 +1,36 @@
|
|||
import { profileNextRoute } from "@/services/onboarding";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import type { NavigationGuardNext, RouteLocationNormalized } from "vue-router";
|
||||
import { START_LOCATION } from "vue-router";
|
||||
|
||||
export async function onboardingRedirect(
|
||||
to: RouteLocationNormalized,
|
||||
from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
const userStore = useUserStore();
|
||||
const user = useUserStore();
|
||||
|
||||
// Guest
|
||||
if (!userStore.loggedIn) {
|
||||
if (!user.loggedIn) {
|
||||
if (to.name !== "accountCreate") {
|
||||
return next({ name: "accountCreate", params: to.params });
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
// Logged in
|
||||
// Member
|
||||
if (to.name === "accountCreate") {
|
||||
return next({ name: "accountConfirm", params: to.params });
|
||||
}
|
||||
|
||||
// Maybe we can skip the account setup steps
|
||||
if (
|
||||
from === START_LOCATION &&
|
||||
user.organisation &&
|
||||
(to.name === "accountConfirm" || to.name === "accountProfile")
|
||||
) {
|
||||
return next({ name: profileNextRoute(to.params.courseType), params: to.params });
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
export function profileNextRoute(courseType: string | string[]) {
|
||||
if (courseType === "uk") {
|
||||
return "setupComplete";
|
||||
}
|
||||
if (courseType === "vv") {
|
||||
return "checkoutAddress";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ export type UserState = {
|
|||
email: string;
|
||||
username: string;
|
||||
avatar_url: string;
|
||||
organisation: string;
|
||||
is_superuser: boolean;
|
||||
course_session_experts: string[];
|
||||
loggedIn: boolean;
|
||||
|
|
@ -57,6 +58,7 @@ const initialUserState: UserState = {
|
|||
username: "",
|
||||
avatar_url: "",
|
||||
is_superuser: false,
|
||||
organisation: "",
|
||||
course_session_experts: [],
|
||||
loggedIn: false,
|
||||
language: defaultLanguage,
|
||||
|
|
|
|||
Loading…
Reference in New Issue