Backend Infrastructure: - Complete Express server setup with security middleware (helmet, CORS, rate limiting) - Comprehensive error handling and logging with Winston - Authentication system with JWT tokens and session management - Database models and migrations for Users, Documents, Feedback, and Processing Jobs - API routes structure for authentication and document management - Integration tests for all server components (86 tests passing) Frontend Infrastructure: - React application with TypeScript and Vite - Authentication UI with login form, protected routes, and logout functionality - Authentication context with proper async state management - Component tests with proper async handling (25 tests passing) - Tailwind CSS styling and responsive design Key Features: - User registration, login, and authentication - Protected routes with role-based access control - Comprehensive error handling and user feedback - Database schema with proper relationships - Security middleware and validation - Production-ready build configuration Test Coverage: 111/111 tests passing Tasks Completed: 1-5 (Project setup, Database, Auth system, Frontend UI, Backend infrastructure) Ready for Task 6: File upload backend infrastructure
132 lines
3.7 KiB
TypeScript
132 lines
3.7 KiB
TypeScript
import React from 'react';
|
|
import { render, screen } from '@testing-library/react';
|
|
import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
|
import ProtectedRoute from '../ProtectedRoute';
|
|
|
|
// Mock the useAuth hook to control its output in tests
|
|
const mockUseAuth = vi.fn();
|
|
vi.mock('../../contexts/AuthContext', () => ({
|
|
useAuth: () => mockUseAuth(),
|
|
}));
|
|
|
|
const TestComponent: React.FC = () => <div>Protected Content</div>;
|
|
const LoginComponent: React.FC = () => <div>Login Page</div>;
|
|
const UnauthorizedComponent: React.FC = () => <div>Unauthorized Page</div>;
|
|
|
|
const renderWithRouter = (ui: React.ReactNode, { initialEntries = ['/protected'] } = {}) => {
|
|
return render(
|
|
<MemoryRouter initialEntries={initialEntries}>
|
|
<Routes>
|
|
<Route path="/login" element={<LoginComponent />} />
|
|
<Route path="/unauthorized" element={<UnauthorizedComponent />} />
|
|
<Route path="/protected" element={ui} />
|
|
</Routes>
|
|
</MemoryRouter>
|
|
);
|
|
};
|
|
|
|
describe('ProtectedRoute', () => {
|
|
beforeEach(() => {
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
it('shows a loading spinner while authentication is in progress', () => {
|
|
mockUseAuth.mockReturnValue({
|
|
user: null,
|
|
isLoading: true,
|
|
isInitialized: false,
|
|
});
|
|
|
|
renderWithRouter(
|
|
<ProtectedRoute>
|
|
<TestComponent />
|
|
</ProtectedRoute>
|
|
);
|
|
|
|
expect(document.querySelector('.animate-spin')).toBeInTheDocument();
|
|
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('redirects to the login page if the user is not authenticated', () => {
|
|
mockUseAuth.mockReturnValue({
|
|
user: null,
|
|
isLoading: false,
|
|
isInitialized: true,
|
|
});
|
|
|
|
renderWithRouter(
|
|
<ProtectedRoute>
|
|
<TestComponent />
|
|
</ProtectedRoute>
|
|
);
|
|
|
|
expect(screen.getByText('Login Page')).toBeInTheDocument();
|
|
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the protected content if the user is authenticated', () => {
|
|
mockUseAuth.mockReturnValue({
|
|
user: { id: '1', role: 'user' },
|
|
isLoading: false,
|
|
isInitialized: true,
|
|
});
|
|
|
|
renderWithRouter(
|
|
<ProtectedRoute>
|
|
<TestComponent />
|
|
</ProtectedRoute>
|
|
);
|
|
|
|
expect(screen.getByText('Protected Content')).toBeInTheDocument();
|
|
});
|
|
|
|
it('redirects to an unauthorized page if the user does not have the required role', () => {
|
|
mockUseAuth.mockReturnValue({
|
|
user: { id: '1', role: 'user' },
|
|
isLoading: false,
|
|
isInitialized: true,
|
|
});
|
|
|
|
renderWithRouter(
|
|
<ProtectedRoute requiredRole="admin">
|
|
<TestComponent />
|
|
</ProtectedRoute>
|
|
);
|
|
|
|
expect(screen.getByText('Unauthorized Page')).toBeInTheDocument();
|
|
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the protected content if the user has the required admin role', () => {
|
|
mockUseAuth.mockReturnValue({
|
|
user: { id: '1', role: 'admin' },
|
|
isLoading: false,
|
|
isInitialized: true,
|
|
});
|
|
|
|
renderWithRouter(
|
|
<ProtectedRoute requiredRole="admin">
|
|
<TestComponent />
|
|
</ProtectedRoute>
|
|
);
|
|
|
|
expect(screen.getByText('Protected Content')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders the protected content if the user has the required user role', () => {
|
|
mockUseAuth.mockReturnValue({
|
|
user: { id: '1', role: 'user' },
|
|
isLoading: false,
|
|
isInitialized: true,
|
|
});
|
|
|
|
renderWithRouter(
|
|
<ProtectedRoute requiredRole="user">
|
|
<TestComponent />
|
|
</ProtectedRoute>
|
|
);
|
|
|
|
expect(screen.getByText('Protected Content')).toBeInTheDocument();
|
|
});
|
|
}); |