import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { AuthProvider, useAuth } from './AuthContext';
import { useRouter } from 'next/navigation';
import { useCurrentSession } from '@/hooks/auth/useCurrentSession';
import { useLogout } from '@/hooks/auth/useLogout';
// Mock Next.js navigation
vi.mock('next/navigation', () => ({
useRouter: vi.fn(),
}));
// Mock auth hooks
vi.mock('@/hooks/auth/useCurrentSession', () => ({
useCurrentSession: vi.fn(),
}));
vi.mock('@/hooks/auth/useLogout', () => ({
useLogout: vi.fn(),
}));
// Test component that uses the auth context
const TestConsumer = () => {
const auth = useAuth();
return (
{auth.session ? 'has-session' : 'no-session'}
{auth.loading ? 'loading' : 'not-loading'}
);
};
describe('AuthContext', () => {
let mockRouter: any;
let mockRefetch: any;
let mockMutateAsync: any;
beforeEach(() => {
vi.clearAllMocks();
mockRouter = {
push: vi.fn(),
refresh: vi.fn(),
};
mockRefetch = vi.fn();
mockMutateAsync = vi.fn().mockResolvedValue(undefined);
(useRouter as any).mockReturnValue(mockRouter);
(useCurrentSession as any).mockReturnValue({
data: null,
isLoading: false,
refetch: mockRefetch,
});
(useLogout as any).mockReturnValue({
mutateAsync: mockMutateAsync,
});
});
describe('AuthProvider', () => {
it('should provide default context values', () => {
render(
);
expect(screen.getByTestId('session')).toHaveTextContent('no-session');
expect(screen.getByTestId('loading')).toHaveTextContent('not-loading');
});
it('should provide loading state', () => {
(useCurrentSession as any).mockReturnValue({
data: null,
isLoading: true,
refetch: mockRefetch,
});
render(
);
expect(screen.getByTestId('loading')).toHaveTextContent('loading');
});
it('should provide session data', () => {
const mockSession = { user: { id: '123', name: 'Test User' } };
(useCurrentSession as any).mockReturnValue({
data: mockSession,
isLoading: false,
refetch: mockRefetch,
});
render(
);
expect(screen.getByTestId('session')).toHaveTextContent('has-session');
});
it('should provide initial session data', () => {
const mockSession = { user: { id: '123', name: 'Test User' } };
render(
);
expect(screen.getByTestId('session')).toHaveTextContent('has-session');
});
});
describe('useAuth hook', () => {
it('should throw error when used outside AuthProvider', () => {
// Suppress console.error for this test
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
expect(() => {
render();
}).toThrow('useAuth must be used within an AuthProvider');
consoleSpy.mockRestore();
});
it('should provide login function', async () => {
render(
);
const loginButton = screen.getByText('Login');
loginButton.click();
await waitFor(() => {
expect(mockRouter.push).toHaveBeenCalledWith('/auth/login');
});
});
it('should provide login function with returnTo parameter', async () => {
const TestConsumerWithReturnTo = () => {
const auth = useAuth();
return (
);
};
render(
);
const loginButton = screen.getByText('Login with Return');
loginButton.click();
await waitFor(() => {
expect(mockRouter.push).toHaveBeenCalledWith('/auth/login?returnTo=%2Fdashboard');
});
});
it('should provide logout function', async () => {
render(
);
const logoutButton = screen.getByText('Logout');
logoutButton.click();
await waitFor(() => {
expect(mockMutateAsync).toHaveBeenCalled();
expect(mockRouter.push).toHaveBeenCalledWith('/');
expect(mockRouter.refresh).toHaveBeenCalled();
});
});
it('should handle logout failure gracefully', async () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
mockMutateAsync.mockRejectedValue(new Error('Logout failed'));
render(
);
const logoutButton = screen.getByText('Logout');
logoutButton.click();
await waitFor(() => {
expect(mockMutateAsync).toHaveBeenCalled();
expect(mockRouter.push).toHaveBeenCalledWith('/');
});
consoleSpy.mockRestore();
});
it('should provide refreshSession function', async () => {
render(
);
const refreshButton = screen.getByText('Refresh');
refreshButton.click();
await waitFor(() => {
expect(mockRefetch).toHaveBeenCalled();
});
});
});
describe('edge cases', () => {
it('should handle null initial session', () => {
render(
);
expect(screen.getByTestId('session')).toHaveTextContent('no-session');
});
it('should handle undefined initial session', () => {
render(
);
expect(screen.getByTestId('session')).toHaveTextContent('no-session');
});
it('should handle multiple consumers', () => {
render(
);
const consumers = screen.getAllByTestId('auth-consumer');
expect(consumers).toHaveLength(2);
});
});
});