mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
155 lines
5.2 KiB
TypeScript
155 lines
5.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
ONBOARDING_STEPS,
|
|
ONBOARDING_STEP_IDS,
|
|
WIZARD_STEP_IDS,
|
|
LINK_STEP_IDS,
|
|
countCompletedSteps,
|
|
firstOpenWizardStep,
|
|
getStepStates,
|
|
isOnboardingComplete,
|
|
isOnboardingStepId,
|
|
type OnboardingContext,
|
|
} from "@/src/modules/onboarding/lib/onboarding-steps";
|
|
|
|
/** Frisch registriertes Studio: nichts eingerichtet. */
|
|
const emptyContext: OnboardingContext = {
|
|
stripeAccountStatus: null,
|
|
slug: null,
|
|
isPublic: false,
|
|
hasBranding: false,
|
|
hasCancellationPolicy: false,
|
|
roomCount: 0,
|
|
employeeCount: 0,
|
|
courseCount: 0,
|
|
};
|
|
|
|
/** Alle Pflichtschritte erledigt, optionale Schritte offen. */
|
|
const requiredDoneContext: OnboardingContext = {
|
|
...emptyContext,
|
|
stripeAccountStatus: "active",
|
|
slug: "mein-studio",
|
|
isPublic: true,
|
|
roomCount: 1,
|
|
courseCount: 1,
|
|
};
|
|
|
|
describe("isOnboardingStepId", () => {
|
|
it("erkennt gültige Registry-Keys", () => {
|
|
expect(isOnboardingStepId("payments")).toBe(true);
|
|
expect(isOnboardingStepId("course")).toBe(true);
|
|
});
|
|
|
|
it("lehnt unbekannte Keys ab", () => {
|
|
expect(isOnboardingStepId("zahlungen")).toBe(false);
|
|
expect(isOnboardingStepId("")).toBe(false);
|
|
expect(isOnboardingStepId("__proto__")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("Registry-Aufteilung", () => {
|
|
it("teilt alle Schritte überschneidungsfrei in Wizard- und Link-Schritte", () => {
|
|
expect([...WIZARD_STEP_IDS, ...LINK_STEP_IDS].sort()).toEqual([...ONBOARDING_STEP_IDS].sort());
|
|
expect(WIZARD_STEP_IDS.filter((id) => (LINK_STEP_IDS as string[]).includes(id))).toEqual([]);
|
|
});
|
|
|
|
it("hat mindestens einen Pflichtschritt", () => {
|
|
expect(ONBOARDING_STEP_IDS.some((id) => ONBOARDING_STEPS[id].required)).toBe(true);
|
|
});
|
|
|
|
it("verlinkt jeden Schritt auf einen app-internen Pfad", () => {
|
|
for (const id of ONBOARDING_STEP_IDS) {
|
|
expect(ONBOARDING_STEPS[id].href.startsWith("/")).toBe(true);
|
|
expect(ONBOARDING_STEPS[id].href.startsWith("//")).toBe(false);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("getStepStates", () => {
|
|
it("meldet im leeren Studio jeden Schritt als offen", () => {
|
|
const states = getStepStates(emptyContext);
|
|
expect(states).toHaveLength(ONBOARDING_STEP_IDS.length);
|
|
expect(states.every((s) => !s.done)).toBe(true);
|
|
});
|
|
|
|
it("behält die Registry-Reihenfolge bei", () => {
|
|
expect(getStepStates(emptyContext).map((s) => s.id)).toEqual(ONBOARDING_STEP_IDS);
|
|
});
|
|
});
|
|
|
|
describe("Ableitung der einzelnen Schritte", () => {
|
|
it("hakt Zahlungen nur bei aktivem Stripe-Konto ab", () => {
|
|
expect(
|
|
ONBOARDING_STEPS.payments.isDone({ ...emptyContext, stripeAccountStatus: "active" })
|
|
).toBe(true);
|
|
// "pending" und "restricted" reichen NICHT: in beiden Fällen kann das Studio
|
|
// kein Geld empfangen, der Schritt muss also offen bleiben.
|
|
expect(
|
|
ONBOARDING_STEPS.payments.isDone({ ...emptyContext, stripeAccountStatus: "pending" })
|
|
).toBe(false);
|
|
expect(
|
|
ONBOARDING_STEPS.payments.isDone({ ...emptyContext, stripeAccountStatus: "restricted" })
|
|
).toBe(false);
|
|
});
|
|
|
|
it("verlangt für die öffentliche Seite Slug UND Sichtbarkeit", () => {
|
|
expect(
|
|
ONBOARDING_STEPS.publicPage.isDone({ ...emptyContext, isPublic: true, slug: null })
|
|
).toBe(false);
|
|
expect(
|
|
ONBOARDING_STEPS.publicPage.isDone({ ...emptyContext, isPublic: false, slug: "s" })
|
|
).toBe(false);
|
|
expect(ONBOARDING_STEPS.publicPage.isDone({ ...emptyContext, isPublic: true, slug: "s" })).toBe(
|
|
true
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("isOnboardingComplete", () => {
|
|
it("ist im leeren Studio nicht erfüllt", () => {
|
|
expect(isOnboardingComplete(emptyContext)).toBe(false);
|
|
});
|
|
|
|
it("ist erfüllt, sobald alle Pflichtschritte stehen - auch ohne die optionalen", () => {
|
|
expect(isOnboardingComplete(requiredDoneContext)).toBe(true);
|
|
expect(requiredDoneContext.hasBranding).toBe(false);
|
|
expect(requiredDoneContext.employeeCount).toBe(0);
|
|
});
|
|
|
|
it("fällt zurück, wenn ein Pflichtschritt wegbricht (z.B. Stripe wird restricted)", () => {
|
|
expect(
|
|
isOnboardingComplete({ ...requiredDoneContext, stripeAccountStatus: "restricted" })
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("countCompletedSteps", () => {
|
|
it("zählt über ALLE Schritte, nicht nur die Pflichtschritte", () => {
|
|
expect(countCompletedSteps(emptyContext)).toBe(0);
|
|
expect(countCompletedSteps(requiredDoneContext)).toBe(
|
|
ONBOARDING_STEP_IDS.filter((id) => ONBOARDING_STEPS[id].required).length
|
|
);
|
|
expect(
|
|
countCompletedSteps({ ...requiredDoneContext, hasBranding: true, employeeCount: 2 })
|
|
).toBe(ONBOARDING_STEP_IDS.length - 1); // nur die Storno-Policy fehlt noch
|
|
});
|
|
});
|
|
|
|
describe("firstOpenWizardStep", () => {
|
|
it("liefert den ersten offenen Wizard-Schritt in Registry-Reihenfolge", () => {
|
|
expect(firstOpenWizardStep(emptyContext)).toBe(WIZARD_STEP_IDS[0]);
|
|
});
|
|
|
|
it("überspringt bereits erledigte Schritte", () => {
|
|
expect(firstOpenWizardStep({ ...emptyContext, hasBranding: true })).toBe(WIZARD_STEP_IDS[1]);
|
|
});
|
|
|
|
it("liefert null, wenn alle Wizard-Schritte erledigt sind", () => {
|
|
const allWizardDone: OnboardingContext = {
|
|
...requiredDoneContext,
|
|
hasBranding: true,
|
|
hasCancellationPolicy: true,
|
|
};
|
|
expect(firstOpenWizardStep(allWizardDone)).toBeNull();
|
|
});
|
|
});
|