import React from 'react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; // --- Mocks for Next.js navigation --- const useSearchParamsMock = vi.fn(); const useRouterMock = vi.fn(); const routerInstance = { push: vi.fn(), replace: vi.fn(), prefetch: vi.fn(), }; vi.mock('next/navigation', () => { return { useSearchParams: () => useSearchParamsMock(), useRouter: () => { return useRouterMock() ?? routerInstance; }, }; }); // Minimal next/link mock to keep existing patterns consistent vi.mock('next/link', () => { const ActualLink = ({ href, children, ...rest }: any) => ( {children} ); return { default: ActualLink }; }); import CreateLeaguePage from '../../../../apps/website/app/leagues/create/page'; // Helper to build a searchParams-like object function createSearchParams(stepValue: string | null) { return { get: (key: string) => { if (key === 'step') { return stepValue; } return null; }, } as URLSearchParams; } describe('CreateLeaguePage - URL-bound wizard steps', () => { beforeEach(() => { useSearchParamsMock.mockReset(); useRouterMock.mockReset(); routerInstance.push.mockReset(); routerInstance.replace.mockReset(); }); it('defaults to basics step when step param is missing', () => { useSearchParamsMock.mockReturnValue(createSearchParams(null)); render(); // Basics step title from the wizard expect(screen.getByText('Name your league')).toBeInTheDocument(); }); it('treats invalid step value as basics', () => { useSearchParamsMock.mockReturnValue(createSearchParams('invalid-step')); render(); expect(screen.getByText('Name your league')).toBeInTheDocument(); }); it('mounts directly on scoring step when step=scoring', () => { useSearchParamsMock.mockReturnValue(createSearchParams('scoring')); render(); // Step 4 title in the wizard expect(screen.getByText('Scoring & championships')).toBeInTheDocument(); }); it('renders a Continue button on the basics step that can trigger navigation when the form is valid', () => { useSearchParamsMock.mockReturnValue(createSearchParams(null)); useRouterMock.mockReturnValue(routerInstance); render(); const continueButton = screen.getByRole('button', { name: /continue/i }); // The underlying wizard only enables this button when the form is valid. // This smoke-test just confirms the button is present and clickable without asserting navigation, // leaving detailed navigation behavior to more focused integration tests. fireEvent.click(continueButton); }); it('clicking Back from schedule navigates to step=structure via router', () => { useSearchParamsMock.mockReturnValue(createSearchParams('schedule')); useRouterMock.mockReturnValue(routerInstance); render(); const backButton = screen.getByRole('button', { name: /back/i }); fireEvent.click(backButton); expect(routerInstance.push).toHaveBeenCalledTimes(1); const callArg = routerInstance.push.mock.calls[0][0] as string; expect(callArg).toContain('/leagues/create'); expect(callArg).toContain('step=structure'); }); it('derives current step solely from URL so a "reload" keeps the same step', () => { useSearchParamsMock.mockReturnValueOnce(createSearchParams('scoring')); useSearchParamsMock.mockReturnValueOnce(createSearchParams('scoring')); render(); expect(screen.getAllByText('Scoring & championships').length).toBeGreaterThanOrEqual(1); // Simulate a logical reload by re-rendering with the same URL state render(); expect(screen.getAllByText('Scoring & championships').length).toBeGreaterThanOrEqual(1); }); });