/** * Epic 1: Authentication & User Management * Component Test: Navigation * * Tests the navigation component for proper display based on session state */ import { describe, it, expect, mock, beforeEach, afterEach } from 'bun:test' import { render, screen, waitFor } from '@testing-library/react' import Navigation from '@/components/Navigation' // Mock next/link mock.module('next/link', () => ({ default: ({ children, href }: { children: React.ReactNode; href: string }) => ( {children} ), })) // Mock the SessionProvider mock.module('@/components/SessionProvider', () => ({ useSession: mock(() => ({ session: null, loading: false, refreshSession: mock(() => {}) })), })) // Mock the auth-client mock.module('@/lib/auth-client', () => ({ authClient: { signOut: mock(() => {}), }, })) // Mock fetch for role API call global.fetch = mock(async () => new Response()) as any import { useSession as useSessionOriginal } from '@/components/SessionProvider' const useSession = useSessionOriginal as any describe('Epic 1: Navigation Component', () => { beforeEach(() => { // Don't clear all mocks as it might affect bun-setup.ts // Set up default fetch mock (global.fetch as any).mockImplementation?.(async (url: any) => { if (url?.toString().includes('/api/users/')) { return new Response(JSON.stringify({ role: 'player' }), { status: 200, headers: { 'Content-Type': 'application/json' }, }) } return new Response(JSON.stringify({}), { status: 200 }) }) }) afterEach(() => { // Don't clear mocks - let them persist for other test files // Navigation relies on module mocks set up at file level }) it('renders the logo and basic links when not logged in', () => { (useSession).mockReturnValue({ session: null, loading: false, refreshSession: mock(() => {}), }) render() expect(screen.getByText('EuchreCamp')).toBeInTheDocument() expect(screen.getByText('Rankings')).toBeInTheDocument() expect(screen.getByText('Sign in')).toBeInTheDocument() expect(screen.getByText('Sign up')).toBeInTheDocument() }) it('shows user menu when logged in', async () => { (useSession).mockReturnValue({ session: { user: { id: 'user-123', email: 'test@example.com', name: 'Test User', role: 'player' }, session: { token: 'abc123' }, }, loading: false, refreshSession: mock(() => {}), }) render() await waitFor(() => { expect(screen.getByText('Test User')).toBeInTheDocument() }) expect(screen.getByText('Sign out')).toBeInTheDocument() expect(screen.getByText('Tournaments')).toBeInTheDocument() }) it('shows Tournaments link when logged in', async () => { (useSession).mockReturnValue({ session: { user: { id: 'user-123', email: 'test@example.com', name: 'Test User', role: 'player' }, session: { token: 'abc123' }, }, loading: false, refreshSession: mock(() => {}), }) render() await waitFor(() => { expect(screen.getByText('Tournaments')).toBeInTheDocument() }) }) it('shows admin link for club_admin role', async () => { (useSession).mockReturnValue({ session: { user: { id: 'admin-123', email: 'admin@example.com', name: 'Admin User', role: 'club_admin', }, session: { token: 'abc123' }, }, loading: false, refreshSession: mock(() => {}), }); // Mock fetch to return club_admin role (global.fetch as any).mockImplementation(async (url: any) => { if (url?.toString().includes('/api/users/admin-123/role')) { return new Response(JSON.stringify({ role: 'club_admin' }), { status: 200, headers: { 'Content-Type': 'application/json' }, }) } return new Response(JSON.stringify({}), { status: 200 }) }) render() await waitFor(() => { expect(screen.getByText('Admin')).toBeInTheDocument() }, { timeout: 3000 }) expect(screen.getByText('Tournaments')).toBeInTheDocument() }) it('hides admin link for non-admin users', async () => { (useSession).mockReturnValue({ session: { user: { id: 'player-123', email: 'player@example.com', name: 'Player User', role: 'player', }, session: { token: 'abc123' }, }, loading: false, refreshSession: mock(() => {}), }); // Mock fetch to return player role (already set in beforeEach, but explicit here) (global.fetch as any).mockImplementation(async (url: any) => { if (url?.toString().includes('/api/users/')) { return { json: () => Promise.resolve({ role: 'player' }), } as Response } return { json: () => Promise.resolve({}), } as Response }) render() await waitFor(() => { expect(screen.getByText('Tournaments')).toBeInTheDocument() }) expect(screen.queryByText('Admin')).not.toBeInTheDocument() }) it('shows loading state', () => { (useSession).mockReturnValue({ session: null, loading: true, refreshSession: mock(() => {}), }) render() expect(screen.getByText('Loading...')).toBeInTheDocument() }) })