mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
119 lines
4.6 KiB
TypeScript
119 lines
4.6 KiB
TypeScript
import "@testing-library/jest-dom/vitest";
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { render, screen, cleanup } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import OnboardingWizardView from "@/src/modules/onboarding/ui/views/onboarding-wizard-view";
|
|
import {
|
|
ONBOARDING_STEPS,
|
|
WIZARD_STEP_IDS,
|
|
type OnboardingStepId,
|
|
} from "@/src/modules/onboarding/lib/onboarding-steps";
|
|
|
|
// Server Actions und Next-Navigation wegmocken - hier geht es ausschließlich um
|
|
// die Render-Stabilität des Wizards beim Durchklicken, nicht um die Fachlogik.
|
|
vi.mock("next/navigation", () => ({
|
|
useRouter: () => ({ push: vi.fn(), refresh: vi.fn() }),
|
|
}));
|
|
vi.mock("@/src/modules/settings/actions/business-branding-actions", () => ({
|
|
saveBusinessBranding: vi.fn(async () => ({ success: true })),
|
|
}));
|
|
vi.mock("@/src/modules/settings/actions/cancellation-settings-actions", () => ({
|
|
saveCancellationPolicy: vi.fn(async () => ({ success: true })),
|
|
}));
|
|
vi.mock("@/src/modules/settings/actions/stripe-connect-actions", () => ({
|
|
createConnectOnboardingLink: vi.fn(async () => ({ success: true, url: "https://stripe.test" })),
|
|
}));
|
|
vi.mock("@/src/modules/rooms/actions/room-actions", () => ({
|
|
createRoom: vi.fn(async () => ({ success: true, message: "ok" })),
|
|
}));
|
|
vi.mock("@/src/modules/courses/actions/course-actions", () => ({
|
|
toggleBusinessPublic: vi.fn(async () => ({ success: true, slug: "test-studio" })),
|
|
}));
|
|
|
|
const allOpen = Object.fromEntries(
|
|
Object.keys(ONBOARDING_STEPS).map((id) => [id, false])
|
|
) as Record<OnboardingStepId, boolean>;
|
|
|
|
const defaultProps = {
|
|
businessId: "b1",
|
|
businessName: "Test Studio",
|
|
managerFirstName: "Alex",
|
|
done: allOpen,
|
|
branding: {
|
|
companyName: "Test Studio",
|
|
logoUrl: null,
|
|
primaryColor: "#1d3557",
|
|
accentColor: "#c9a84c",
|
|
},
|
|
policy: { enabled: true, mode: "tiered" as const, fixedRefundPercent: 100, tiers: [] },
|
|
isPublic: false,
|
|
slug: null,
|
|
stripeAccountStatus: null,
|
|
initialStep: null,
|
|
};
|
|
|
|
beforeEach(() => {
|
|
// jsdom kennt window.scrollTo nicht - der Wizard scrollt bei jedem Schritt nach oben.
|
|
window.scrollTo = vi.fn();
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("OnboardingWizardView - Render-Stabilität", () => {
|
|
it("klickt ohne Hook-Fehler vom Willkommens-Screen bis zum Abschluss durch", async () => {
|
|
const user = userEvent.setup();
|
|
// Ein Hook-Order-Verstoß ("Rendered more hooks than during the previous render")
|
|
// wirft React während des Renders - der Test schlägt dann hier fehl.
|
|
render(<OnboardingWizardView {...defaultProps} />);
|
|
|
|
expect(screen.getByText(/Hallo Alex/)).toBeInTheDocument();
|
|
await user.click(screen.getByRole("button", { name: /Los geht/ }));
|
|
|
|
// Jeder Wizard-Schritt einmal vorwärts
|
|
for (let i = 0; i < WIZARD_STEP_IDS.length; i++) {
|
|
expect(
|
|
screen.getByText(`Schritt ${i + 1} von ${WIZARD_STEP_IDS.length}`)
|
|
).toBeInTheDocument();
|
|
const nextLabel = i === WIZARD_STEP_IDS.length - 1 ? /^Fertig$/ : /Weiter|Überspringen/;
|
|
await user.click(screen.getByRole("button", { name: nextLabel }));
|
|
}
|
|
|
|
expect(screen.getByText(/Grundeinrichtung erledigt/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("übersteht Vor- und Zurücknavigation zwischen allen Screens", async () => {
|
|
const user = userEvent.setup();
|
|
render(<OnboardingWizardView {...defaultProps} initialStep="publicPage" />);
|
|
|
|
// Direkteinstieg beim letzten Schritt (wie beim Rücksprung von Stripe)
|
|
expect(
|
|
screen.getByText(`Schritt ${WIZARD_STEP_IDS.length} von ${WIZARD_STEP_IDS.length}`)
|
|
).toBeInTheDocument();
|
|
|
|
// Über die Punkte-Navigation quer durch alle Schritte springen und wieder zurück
|
|
for (const id of WIZARD_STEP_IDS) {
|
|
await user.click(screen.getByRole("button", { name: ONBOARDING_STEPS[id].label }));
|
|
}
|
|
for (const id of [...WIZARD_STEP_IDS].reverse()) {
|
|
await user.click(screen.getByRole("button", { name: ONBOARDING_STEPS[id].label }));
|
|
}
|
|
|
|
expect(screen.getByText("Schritt 1 von " + WIZARD_STEP_IDS.length)).toBeInTheDocument();
|
|
});
|
|
|
|
it("wechselt zwischen Abschluss-Screen und letztem Schritt hin und her", async () => {
|
|
const user = userEvent.setup();
|
|
render(<OnboardingWizardView {...defaultProps} initialStep="publicPage" />);
|
|
|
|
await user.click(screen.getByRole("button", { name: /^Fertig$/ }));
|
|
expect(screen.getByText(/Grundeinrichtung erledigt/)).toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: /^Zurück$/ }));
|
|
expect(
|
|
screen.getByText(`Schritt ${WIZARD_STEP_IDS.length} von ${WIZARD_STEP_IDS.length}`)
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|