Files
app/src/AppRoutes.admin.test.tsx
T
CC WorkerandClaude Sonnet 4.6 3389fdcb5b
app-ci-deploy / test-build-deploy (push) Has been cancelled
feat(exam): S4-11 marking flow, results table, CSV, ResultsWidget
- /exam-marker/:batchId/mark — ExamMarkingPage: student queue, per-part mark entry, upsert marks
- /exam-marker/:batchId/results — ExamResultsPage: results table with absent rows, CSV download
- ResultsWidget on ClassDetailPage: last batch summary, class average, absent count, batch creation
- New types: MarkingBatch, StudentSubmission, BatchQueueResponse, BatchResultsResponse, MarkUpsertPayload
- New repo methods: createBatch, listBatches, getBatchQueue, getBatchResults, getBatchCsv, upsertMark

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-06-07 04:35:34 +00:00

151 lines
5.7 KiB
TypeScript

import React from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import AppRoutes from './AppRoutes';
const mockUseAuth = vi.fn();
vi.mock('./contexts/AuthContext', () => ({
useAuth: () => mockUseAuth(),
}));
vi.mock('./contexts/UserContext', () => ({
useUser: () => ({ isInitialized: true }),
}));
vi.mock('./debugConfig', () => ({
logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
vi.mock('./pages/Layout', () => ({
default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('./pages/auth/PlatformAdminPage', () => ({ default: () => <div>Platform Admin Page</div> }));
vi.mock('./pages/auth/adminPage', () => ({ default: () => <div>Legacy Admin</div> }));
vi.mock('./pages/auth/loginPage', () => ({ default: () => <div>Login Page</div> }));
vi.mock('./pages/auth/signupPage', () => ({ default: () => <div>Signup Page</div> }));
vi.mock('./pages/user/dashboardPage', () => ({ default: () => <div>Dashboard Page</div> }));
vi.mock('./pages/user/NotFound', () => ({ default: () => <div>Private Not Found</div> }));
vi.mock('./pages/NotFoundPublic', () => ({ default: () => <div>Public Not Found</div> }));
vi.mock('./pages/tldraw/TLDrawCanvas', () => ({ default: () => <div>Public Home</div> }));
vi.mock('./pages/tldraw/singlePlayerPage', () => ({ default: () => <div>Single Player</div> }));
vi.mock('./pages/tldraw/multiplayerUser', () => ({ default: () => <div>Multiplayer</div> }));
vi.mock('./pages/exam', () => ({
ExamDashboardPage: () => <div>Exam Marker</div>,
ExamTemplateSetupPage: () => <div>Exam Template Setup</div>,
MarkSchemePage: () => <div>Mark Scheme editor</div>,
ExamMarkingPage: () => <div>Exam Marking</div>,
ExamResultsPage: () => <div>Exam Results</div>,
ResultsWidget: () => <div>Results Widget</div>,
}));
vi.mock('./pages/user/calendarPage', () => ({ default: () => <div>Calendar</div> }));
vi.mock('./pages/user/settingsPage', () => ({ default: () => <div>Settings</div> }));
vi.mock('./pages/tldraw/devPlayerPage', () => ({ default: () => <div>TLDraw Dev</div> }));
vi.mock('./pages/tldraw/devPage', () => ({ default: () => <div>Dev</div> }));
vi.mock('./pages/react-flow/teacherPlanner', () => ({ default: () => <div>Teacher Planner</div> }));
vi.mock('./pages/morphicPage', () => ({ default: () => <div>Morphic</div> }));
vi.mock('./pages/tldraw/ShareHandler', () => ({ default: () => <div>Share</div> }));
vi.mock('./pages/searxngPage', () => ({ default: () => <div>Search</div> }));
vi.mock('./pages/dev/SimpleUploadTest', () => ({ default: () => <div>Upload Test</div> }));
vi.mock('./pages/tldraw/CCDocumentIntelligence/CCDocumentIntelligence', () => ({
CCDocumentIntelligence: () => <div>Doc Intelligence</div>,
}));
vi.mock('./pages/timetable', () => ({
TimetablePage: () => <div>Timetable</div>,
TimetableListPage: () => <div>Timetable List</div>,
ClassesPage: () => <div>Classes</div>,
LessonPage: () => <div>Lesson</div>,
TaughtLessonsPage: () => <div>Taught Lessons</div>,
MyClassesPage: () => <div>My Classes</div>,
EnrollmentRequestsPage: () => <div>Enrollment Requests</div>,
StaffManagerPage: () => <div>Staff Manager</div>,
StudentManagerPage: () => <div>Student Manager</div>,
SchoolSettingsPage: () => <div>School Settings</div>,
ClassDetailPage: () => <div>Class Detail</div>,
StudentLessonsPage: () => <div>Student Lessons</div>,
LessonPlansPage: () => <div>Lesson Plans</div>,
LessonPlanDetailPage: () => <div>Lesson Plan Detail</div>,
}));
function renderAt(path: string) {
return render(
<MemoryRouter initialEntries={[path]}>
<AppRoutes />
</MemoryRouter>
);
}
function authState(overrides: Record<string, unknown>) {
return {
user: null,
user_role: null,
accessToken: null,
loading: false,
error: null,
signIn: vi.fn(),
signOut: vi.fn(),
clearError: vi.fn(),
...overrides,
};
}
describe('/admin route authorization', () => {
beforeEach(() => {
mockUseAuth.mockReset();
});
it('redirects anonymous users away from /admin', () => {
mockUseAuth.mockReturnValue(authState({ user: null, user_role: null }));
renderAt('/admin');
expect(screen.queryByText('Platform Admin Page')).not.toBeInTheDocument();
expect(screen.getByText('Login Page')).toBeInTheDocument();
});
it('redirects authenticated non-admin users away from /admin', () => {
mockUseAuth.mockReturnValue(authState({
user: { id: 'user-1', email: '[email protected]', user_type: 'email_teacher' },
user_role: 'email_teacher',
accessToken: 'token',
}));
renderAt('/admin');
expect(screen.queryByText('Platform Admin Page')).not.toBeInTheDocument();
expect(screen.getByText('Dashboard Page')).toBeInTheDocument();
});
it('allows super_admin users to access /admin', () => {
mockUseAuth.mockReturnValue(authState({
user: { id: 'admin-1', email: '[email protected]', user_type: 'super_admin' },
user_role: 'super_admin',
accessToken: 'token',
}));
renderAt('/admin');
expect(screen.getByText('Platform Admin Page')).toBeInTheDocument();
});
});
describe('exam-marker routes', () => {
beforeEach(() => {
mockUseAuth.mockReset();
});
it('renders the mark scheme editor route for authenticated users', () => {
mockUseAuth.mockReturnValue(authState({
user: { id: 'teacher-1', email: '[email protected]', user_type: 'email_teacher' },
user_role: 'email_teacher',
accessToken: 'token',
}));
renderAt('/exam-marker/template-123/marks');
expect(screen.getByText('Mark Scheme editor')).toBeInTheDocument();
});
});