import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, fireEvent, waitFor } from "@testing-library/react"; // Mock next/navigation const mockPush = vi.fn(); const mockRefresh = vi.fn(); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: mockPush, refresh: mockRefresh }), })); // Mock Supabase const mockSignInWithPassword = vi.fn(); const mockSignInWithOtp = vi.fn(); vi.mock("@/lib/supabase/client", () => ({ createClient: () => ({ auth: { signInWithPassword: mockSignInWithPassword, signInWithOtp: mockSignInWithOtp, }, }), })); // Import after mocks const { default: LoginPage } = await import( "@/app/(auth)/login/page" ); describe("LoginPage", () => { beforeEach(() => { vi.clearAllMocks(); }); it("renders login form with email and password fields", () => { render(); expect(screen.getByText("KanzlAI")).toBeInTheDocument(); expect(screen.getByText("Melden Sie sich an")).toBeInTheDocument(); expect(screen.getByLabelText("E-Mail")).toBeInTheDocument(); expect(screen.getByLabelText("Passwort")).toBeInTheDocument(); expect(screen.getByText("Anmelden")).toBeInTheDocument(); }); it("renders mode toggle between Passwort and Magic Link", () => { render(); // "Passwort" appears twice (toggle button + label), so use getAllByText const passwortElements = screen.getAllByText("Passwort"); expect(passwortElements.length).toBeGreaterThanOrEqual(1); expect(screen.getByText("Magic Link")).toBeInTheDocument(); }); it("switches to magic link mode and hides password field", () => { render(); fireEvent.click(screen.getByText("Magic Link")); expect(screen.queryByLabelText("Passwort")).not.toBeInTheDocument(); expect(screen.getByText("Link senden")).toBeInTheDocument(); }); it("submits password login to Supabase", async () => { mockSignInWithPassword.mockResolvedValue({ error: null }); render(); fireEvent.change(screen.getByLabelText("E-Mail"), { target: { value: "test@kanzlei.de" }, }); fireEvent.change(screen.getByLabelText("Passwort"), { target: { value: "geheim123" }, }); fireEvent.click(screen.getByText("Anmelden")); await waitFor(() => { expect(mockSignInWithPassword).toHaveBeenCalledWith({ email: "test@kanzlei.de", password: "geheim123", }); }); }); it("redirects to / on successful login", async () => { mockSignInWithPassword.mockResolvedValue({ error: null }); render(); fireEvent.change(screen.getByLabelText("E-Mail"), { target: { value: "test@kanzlei.de" }, }); fireEvent.change(screen.getByLabelText("Passwort"), { target: { value: "geheim123" }, }); fireEvent.click(screen.getByText("Anmelden")); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith("/"); expect(mockRefresh).toHaveBeenCalled(); }); }); it("displays error on failed login", async () => { mockSignInWithPassword.mockResolvedValue({ error: { message: "Ungültige Anmeldedaten" }, }); render(); fireEvent.change(screen.getByLabelText("E-Mail"), { target: { value: "bad@email.de" }, }); fireEvent.change(screen.getByLabelText("Passwort"), { target: { value: "wrong" }, }); fireEvent.click(screen.getByText("Anmelden")); await waitFor(() => { expect(screen.getByText("Ungültige Anmeldedaten")).toBeInTheDocument(); }); }); it("shows magic link sent confirmation", async () => { mockSignInWithOtp.mockResolvedValue({ error: null }); render(); // Switch to magic link mode fireEvent.click(screen.getByText("Magic Link")); fireEvent.change(screen.getByLabelText("E-Mail"), { target: { value: "test@kanzlei.de" }, }); fireEvent.click(screen.getByText("Link senden")); await waitFor(() => { expect(screen.getByText("Link gesendet")).toBeInTheDocument(); expect(screen.getByText("Zurueck zum Login")).toBeInTheDocument(); }); }); it("has link to registration page", () => { render(); const registerLink = screen.getByText("Registrieren"); expect(registerLink).toBeInTheDocument(); expect(registerLink.closest("a")).toHaveAttribute("href", "/register"); }); });