diff --git a/src/components/Comments/__tests__/CommentBankManager.test.tsx b/src/components/Comments/__tests__/CommentBankManager.test.tsx
index 7156899d..fd306531 100644
--- a/src/components/Comments/__tests__/CommentBankManager.test.tsx
+++ b/src/components/Comments/__tests__/CommentBankManager.test.tsx
@@ -22,7 +22,6 @@ const makeAppContextMock = () => ({
deleteCommentBankItem: vi.fn(),
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Comments/__tests__/CommentBankModal.test.tsx b/src/components/Comments/__tests__/CommentBankModal.test.tsx
index b81cd5d1..8e48a4d0 100644
--- a/src/components/Comments/__tests__/CommentBankModal.test.tsx
+++ b/src/components/Comments/__tests__/CommentBankModal.test.tsx
@@ -21,7 +21,6 @@ const makeAppContextMock = () => ({
deleteCommentBankItem: vi.fn(),
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Layout/__tests__/Sidebar.test.tsx b/src/components/Layout/__tests__/Sidebar.test.tsx
index e996ab93..38a314b0 100644
--- a/src/components/Layout/__tests__/Sidebar.test.tsx
+++ b/src/components/Layout/__tests__/Sidebar.test.tsx
@@ -14,7 +14,6 @@ const makeAppContextMock = () => ({
students: [],
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Layout/__tests__/Topbar.test.tsx b/src/components/Layout/__tests__/Topbar.test.tsx
index 9b737616..2116037a 100644
--- a/src/components/Layout/__tests__/Topbar.test.tsx
+++ b/src/components/Layout/__tests__/Topbar.test.tsx
@@ -20,7 +20,6 @@ const makeAppContextMock = () => ({
essayAssignments: [],
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Students/__tests__/CsvImportModal.test.tsx b/src/components/Students/__tests__/CsvImportModal.test.tsx
index 9974f308..e04a6d6c 100644
--- a/src/components/Students/__tests__/CsvImportModal.test.tsx
+++ b/src/components/Students/__tests__/CsvImportModal.test.tsx
@@ -54,7 +54,6 @@ const makeAppContextMock = () => ({
settings: {},
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Students/__tests__/StudentDecksSection.test.tsx b/src/components/Students/__tests__/StudentDecksSection.test.tsx
index bef121d7..7ae3e3bc 100644
--- a/src/components/Students/__tests__/StudentDecksSection.test.tsx
+++ b/src/components/Students/__tests__/StudentDecksSection.test.tsx
@@ -24,7 +24,6 @@ const makeAppContextMock = () => ({
fetchMyStudentFlashcardDecks,
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Tests/__tests__/QuestionBankItemEditorModal.test.tsx b/src/components/Tests/__tests__/QuestionBankItemEditorModal.test.tsx
index ba77fff0..93d395fb 100644
--- a/src/components/Tests/__tests__/QuestionBankItemEditorModal.test.tsx
+++ b/src/components/Tests/__tests__/QuestionBankItemEditorModal.test.tsx
@@ -12,7 +12,6 @@ vi.mock('react-i18next', () => ({
const makeAppContextMock = () => ({ settings: {}, addQuestionBankItem: vi.fn() });
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Tests/__tests__/QuestionBankManager.test.tsx b/src/components/Tests/__tests__/QuestionBankManager.test.tsx
index 07466715..40734f64 100644
--- a/src/components/Tests/__tests__/QuestionBankManager.test.tsx
+++ b/src/components/Tests/__tests__/QuestionBankManager.test.tsx
@@ -79,7 +79,6 @@ const makeAppContextMock = () => ({
bulkUpdateQuestionBankItems,
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Tests/__tests__/QuestionBankSectionEditor.test.tsx b/src/components/Tests/__tests__/QuestionBankSectionEditor.test.tsx
index 56c1f65a..6dc444c5 100644
--- a/src/components/Tests/__tests__/QuestionBankSectionEditor.test.tsx
+++ b/src/components/Tests/__tests__/QuestionBankSectionEditor.test.tsx
@@ -12,7 +12,6 @@ vi.mock('react-i18next', () => ({
const makeAppContextMock = () => ({ settings: {}, addQuestionBankItem: vi.fn() });
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Tests/__tests__/QuestionEditor.test.tsx b/src/components/Tests/__tests__/QuestionEditor.test.tsx
index a3c3965a..8e256be7 100644
--- a/src/components/Tests/__tests__/QuestionEditor.test.tsx
+++ b/src/components/Tests/__tests__/QuestionEditor.test.tsx
@@ -16,7 +16,6 @@ vi.mock('react-i18next', () => ({
const makeAppContextMock = () => ({ settings: {} });
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/Tests/__tests__/TestAssignmentModal.test.tsx b/src/components/Tests/__tests__/TestAssignmentModal.test.tsx
index 6bb39ede..cf533477 100644
--- a/src/components/Tests/__tests__/TestAssignmentModal.test.tsx
+++ b/src/components/Tests/__tests__/TestAssignmentModal.test.tsx
@@ -46,7 +46,6 @@ const makeAppContextMock = () => ({
saveTestAssignment: mockSaveTestAssignment,
});
vi.mock('../../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/__tests__/GlobalSearch.test.tsx b/src/components/__tests__/GlobalSearch.test.tsx
index 036911a1..687aa995 100644
--- a/src/components/__tests__/GlobalSearch.test.tsx
+++ b/src/components/__tests__/GlobalSearch.test.tsx
@@ -53,7 +53,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/components/__tests__/StandardsPickerModal.test.tsx b/src/components/__tests__/StandardsPickerModal.test.tsx
index 1b799f46..9afb5af0 100644
--- a/src/components/__tests__/StandardsPickerModal.test.tsx
+++ b/src/components/__tests__/StandardsPickerModal.test.tsx
@@ -21,7 +21,6 @@ const makeAppContextMock = () => ({
isFavoriteStandard: mockIsFavorite,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/__tests__/components.smoke.test.tsx b/src/components/__tests__/components.smoke.test.tsx
index ce26c778..6267a156 100644
--- a/src/components/__tests__/components.smoke.test.tsx
+++ b/src/components/__tests__/components.smoke.test.tsx
@@ -42,7 +42,6 @@ const makeAppContextMock = () => ({
isFavoriteStandard: vi.fn(() => false),
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/components/__tests__/components.untested.test.tsx b/src/components/__tests__/components.untested.test.tsx
index 6a7f4f60..5b680e07 100644
--- a/src/components/__tests__/components.untested.test.tsx
+++ b/src/components/__tests__/components.untested.test.tsx
@@ -65,7 +65,6 @@ const makeAppContextMock = () => ({
dismissMigrationPrompt: mockDismissMigrationPrompt,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/context/AppContext.extended.test.tsx b/src/context/AppContext.extended.test.tsx
index 3d4ba3e3..db02b82e 100644
--- a/src/context/AppContext.extended.test.tsx
+++ b/src/context/AppContext.extended.test.tsx
@@ -1,7 +1,7 @@
import React, { ReactNode } from 'react';
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { AppProvider, useApp } from './AppContext';
+import { AppProvider, useRoster, useAuthoring, useAssessment } from './AppContext';
import type { Rubric, RubricVersion } from '../types';
import { DEFAULT_FORMAT } from '../types';
@@ -117,7 +117,7 @@ describe('AppContext — extended actions', () => {
// ─── Students & Classes ───────────────────────────────────────────────────
it('adds and deletes a student', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
let id: string;
act(() => {
const s = result.current.addStudent({ name: 'Bob', classId: 'c1' });
@@ -131,7 +131,7 @@ describe('AppContext — extended actions', () => {
});
it('updates a student', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
act(() => {
result.current.addStudent({ name: 'Bob', classId: 'c1' });
});
@@ -142,7 +142,7 @@ describe('AppContext — extended actions', () => {
});
it('adds, updates, and deletes a class', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
act(() => {
result.current.addClass({ name: 'Class A' });
});
@@ -158,7 +158,7 @@ describe('AppContext — extended actions', () => {
});
it('deleteClass with deleteStudents=true removes students in that class', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
act(() => {
result.current.addClass({ name: 'Class A' });
});
@@ -176,7 +176,7 @@ describe('AppContext — extended actions', () => {
// ─── Self Assessments ─────────────────────────────────────────────────────
it('saves and deletes a self assessment', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAssessment(), { wrapper });
const sa = { id: 'sa1', rubricId: 'r1', studentId: 's1', ratings: [], submittedAt: '2024-01-01' };
act(() => {
result.current.saveSelfAssessment(sa);
@@ -191,7 +191,7 @@ describe('AppContext — extended actions', () => {
// ─── Speaking Sessions ────────────────────────────────────────────────────
it('saves and updates a speaking session', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAssessment(), { wrapper });
const session = {
id: 'ss1',
rubricId: 'r1',
@@ -211,7 +211,7 @@ describe('AppContext — extended actions', () => {
});
it('deletes a speaking session', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAssessment(), { wrapper });
const session = {
id: 'ss1',
rubricId: 'r1',
@@ -232,7 +232,7 @@ describe('AppContext — extended actions', () => {
// ─── Rubric Versioning ────────────────────────────────────────────────────
it('saves a rubric version', async () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -246,7 +246,7 @@ describe('AppContext — extended actions', () => {
});
it('restores a rubric version', async () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -269,7 +269,7 @@ describe('AppContext — extended actions', () => {
});
it('saveRubricVersion does nothing for unknown rubricId', async () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
await act(async () => {
await result.current.saveRubricVersion('unknown');
});
@@ -278,7 +278,7 @@ describe('AppContext — extended actions', () => {
});
it('restoreRubricVersion does nothing for unknown rubricId', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.restoreRubricVersion('unknown', makeRubric() as Rubric);
});
@@ -288,7 +288,7 @@ describe('AppContext — extended actions', () => {
// ─── Rubric Snapshot Sync ─────────────────────────────────────────────────
it('syncRubricSnapshot updates student rubrics with new criteria', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring() }), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -319,7 +319,7 @@ describe('AppContext — extended actions', () => {
// ─── Soft-delete grades (Phase 15.3) ─────────────────────────────────────
it('deleteStudentRubric soft-deletes a solo grade and restoreStudentRubric brings it back', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring() }), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -347,7 +347,7 @@ describe('AppContext — extended actions', () => {
});
it('deleteStudentRubric with scope "group" soft-deletes every sibling sharing groupId', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring() }), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -370,7 +370,7 @@ describe('AppContext — extended actions', () => {
});
it('deleteStudentRubric with scope "student" only removes the target and detaches it from the group', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring() }), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -398,7 +398,7 @@ describe('AppContext — extended actions', () => {
// ─── Vocabulary Items ─────────────────────────────────────────────────────
it('adds, updates, and deletes vocabulary items', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addRubric(makeRubric());
});
@@ -425,7 +425,7 @@ describe('AppContext — extended actions', () => {
// ─── Analysis Results ─────────────────────────────────────────────────────
it('saves and deletes analysis results', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAssessment(), { wrapper });
const ar = {
id: 'ar1',
studentId: 's1',
diff --git a/src/context/AppContext.test.tsx b/src/context/AppContext.test.tsx
index cce31e2e..ade2ecd0 100644
--- a/src/context/AppContext.test.tsx
+++ b/src/context/AppContext.test.tsx
@@ -2,7 +2,7 @@ import React, { ReactNode, useLayoutEffect, useRef } from 'react';
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { renderWithRouter } from '../test-utils/renderWithProviders';
-import { AppProvider, useApp, useRoster, useSettings, useAuthoring } from './AppContext';
+import { AppProvider, useRoster, useSettings, useAuthoring, useAssessment } from './AppContext';
import * as storage from '../store/storage';
import { storageSync } from '../services/database';
import type { Rubric, GradeScale } from '../types';
@@ -132,7 +132,7 @@ describe('AppContext', () => {
const wrapper = ({ children }: { children: ReactNode }) => {children};
it('should initialize with default loaded state', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring(), ...useSettings() }), { wrapper });
expect(result.current.rubrics).toEqual([]);
expect(result.current.students).toEqual([]);
expect(result.current.classes).toEqual([]);
@@ -141,7 +141,7 @@ describe('AppContext', () => {
});
it('should add a rubric', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useAuthoring(), ...useSettings() }), { wrapper });
const newRubric: Omit = {
name: 'Test Rubric',
@@ -167,7 +167,7 @@ describe('AppContext', () => {
});
it('should update a rubric', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useAuthoring(), ...useSettings() }), { wrapper });
act(() => {
result.current.addRubric({
@@ -198,7 +198,7 @@ describe('AppContext', () => {
});
it('should delete a rubric', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useAuthoring(), ...useSettings() }), { wrapper });
let addedId = '';
act(() => {
@@ -227,7 +227,7 @@ describe('AppContext', () => {
});
it('should save and update student rubrics', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
const sr = { id: 'sr1', rubricId: 'r1', studentId: 's1', entries: [], overallComment: '', isPeerReview: false };
act(() => {
@@ -243,7 +243,7 @@ describe('AppContext', () => {
});
it('should create a student rubric with default entries', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring(), ...useSettings() }), { wrapper });
act(() => {
result.current.addRubric({
name: 'R1',
@@ -267,7 +267,7 @@ describe('AppContext', () => {
});
it('should create group student rubrics sharing one groupId, and propagate scores on save', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring(), ...useSettings() }), { wrapper });
act(() => {
result.current.addRubric({
name: 'Group Project',
@@ -310,7 +310,7 @@ describe('AppContext', () => {
});
it('should only fan out collaborative criteria in group grading, leaving individually-scoped criteria per student', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring(), ...useSettings() }), { wrapper });
act(() => {
result.current.addRubric({
name: 'Group Project',
@@ -361,7 +361,7 @@ describe('AppContext', () => {
});
it('should still fan out overallComment/globalModifier for an all-individually-scoped rubric, while entries stay per-student', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring(), ...useSettings() }), { wrapper });
act(() => {
result.current.addRubric({
name: 'Group Project',
@@ -407,7 +407,7 @@ describe('AppContext', () => {
});
it('should reuse an existing ungrouped StudentRubric instead of creating a duplicate', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => ({ ...useRoster(), ...useAuthoring(), ...useSettings() }), { wrapper });
act(() => {
result.current.addRubric({
name: 'Group Project',
@@ -441,7 +441,7 @@ describe('AppContext', () => {
});
it('should merge classes', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
let c1 = '',
c2 = '';
@@ -460,7 +460,7 @@ describe('AppContext', () => {
});
it('should manage favorite standards', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
const std = { guid: 'std1', description: 'desc', standardSetTitle: '', jurisdictionTitle: '' };
act(() => {
@@ -476,7 +476,7 @@ describe('AppContext', () => {
});
it('should manage comment bank items', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addCommentBankItem('Good job', ['tag1']);
@@ -496,7 +496,7 @@ describe('AppContext', () => {
});
it('should track comment bank usage without requiring a full item replace', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addCommentBankItem('Good job', ['tag1']);
@@ -517,7 +517,7 @@ describe('AppContext', () => {
});
it('should mange grade scales', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addGradeScale({ name: 'New Scale', type: 'points', ranges: [] });
@@ -537,7 +537,7 @@ describe('AppContext', () => {
});
it('should manage attachments', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useRoster(), { wrapper });
act(() => {
result.current.addAttachment({ name: 'File 1', mimeType: 'docx', size: 100, dataUrl: 'data' });
@@ -552,7 +552,7 @@ describe('AppContext', () => {
});
it('should manage export templates', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAuthoring(), { wrapper });
act(() => {
result.current.addExportTemplate({ name: 'Template 1', dataUrl: 'data', levelHeaders: ['H1'], size: 100 });
@@ -567,7 +567,7 @@ describe('AppContext', () => {
});
it('should manage peer reviews', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useAssessment(), { wrapper });
const pr = {
id: 'pr1',
rubricId: 'r1',
@@ -590,7 +590,7 @@ describe('AppContext', () => {
});
it('should retrieve active grade scale', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
let scale: GradeScale | undefined;
act(() => {
@@ -600,7 +600,7 @@ describe('AppContext', () => {
});
it('should update settings', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
act(() => {
result.current.updateSettings({ theme: 'dark' });
@@ -624,7 +624,7 @@ describe('AppContext', () => {
it('shows the storage_full toast when the quota is exceeded while offline/disconnected', () => {
vi.mocked(storageSync.isConnected).mockReturnValue(false);
- renderHook(() => useApp(), { wrapper });
+ renderHook(() => useSettings(), { wrapper });
triggerQuotaExceeded();
@@ -633,7 +633,7 @@ describe('AppContext', () => {
it('stays silent when the quota is exceeded while connected — Supabase already has the real data', () => {
vi.mocked(storageSync.isConnected).mockReturnValue(true);
- renderHook(() => useApp(), { wrapper });
+ renderHook(() => useSettings(), { wrapper });
triggerQuotaExceeded();
@@ -773,10 +773,11 @@ describe('AppContext', () => {
});
it('actions invoked from a layout effect see the latest rubric state', () => {
- // Regression for currentStateRef sync timing: the provider must refresh the ref in the
- // layout phase (parent layout effects fire before descendants'), so an action called from
- // a descendant's useLayoutEffect — e.g. pre-filling a just-created rubric — reads the
- // fresh snapshot instead of the previous render's.
+ // Regression for currentStateRef sync timing: the ref is written during render (not in an
+ // effect), so the first layout-effect run below dispatches an update and the second run —
+ // which fires after the re-render that dispatch produced — reads the ref's fresh snapshot
+ // instead of the previous render's. An effect-based sync (layout or passive) would lag
+ // behind by a commit and observe stale state.
const entryCounts: number[] = [];
const rubricIdRef: { current: string } = { current: '' };
@@ -846,8 +847,9 @@ describe('AppContext', () => {
renderWithRouter(, { withAppProvider: true });
act(() => {}); // flush the re-render scheduled by the first layout effect
- // With the fix the ref is refreshed in the layout phase, so the action sees the scale
- // added in run 1; with a passive-effect sync it would still resolve the old default.
+ // The ref is written during render, so run 2 — which fires after the re-render caused by
+ // run 1's dispatch — sees the scale added in run 1; with an effect-based sync it would lag
+ // by a commit and still resolve the old default.
expect(observedNames).toEqual(['Layout Scale']);
});
});
diff --git a/src/context/AppContext.tsx b/src/context/AppContext.tsx
index 2c1be06d..a506c300 100644
--- a/src/context/AppContext.tsx
+++ b/src/context/AppContext.tsx
@@ -9,7 +9,6 @@ import React, {
ReactNode,
} from 'react';
import {
- AppContextValue,
LOCAL_MODE_KEY,
MIGRATION_DONE_KEY,
flushToLocalStorage,
@@ -19,13 +18,13 @@ import {
} from './storeCore';
import { createSelectorStore, StoreProvider } from './useStore';
-import { useRoster, RosterProvider } from './domains/roster';
-import { useAuthoring, AuthoringProvider } from './domains/authoring';
-import { useAssessment, AssessmentProvider } from './domains/assessment';
-import { useEssays, EssaysProvider } from './domains/essays';
-import { useFlashcards, FlashcardsProvider } from './domains/flashcards';
-import { useSettings, SettingsProvider } from './domains/settings';
-import { usePlatform, PlatformProvider } from './domains/platform';
+import { RosterProvider } from './domains/roster';
+import { AuthoringProvider } from './domains/authoring';
+import { AssessmentProvider } from './domains/assessment';
+import { EssaysProvider } from './domains/essays';
+import { FlashcardsProvider } from './domains/flashcards';
+import { SettingsProvider } from './domains/settings';
+import { PlatformProvider } from './domains/platform';
import { useTranslation } from 'react-i18next';
import { useToast } from '../hooks/useToast';
import { loadDb, getDb } from '../services/database/lazyDb';
@@ -39,22 +38,6 @@ import { initClientLogger, setLoggerContext, STRESS_TEST_LOGGING_ENABLED } from
import { initAuditLogger, clearAuditLogger } from '../services/database/AuditLogger';
import type { DatabaseConfig } from '../services/database';
-// The merged view for tests and object-form call sites — app code must use the
-// domain hooks (enforced by the no-restricted-syntax ESLint rule).
-export function useApp(): AppContextValue {
- const roster = useRoster();
- const authoring = useAuthoring();
- const assessment = useAssessment();
- const essays = useEssays();
- const flashcards = useFlashcards();
- const settings = useSettings();
- const platform = usePlatform();
- return useMemo(
- () => ({ ...settings, ...roster, ...authoring, ...assessment, ...essays, ...flashcards, ...platform }),
- [roster, authoring, assessment, essays, flashcards, settings, platform]
- );
-}
-
export { useRoster } from './domains/roster';
export { useAuthoring } from './domains/authoring';
export { useAssessment } from './domains/assessment';
diff --git a/src/context/dyslexiaMode.test.tsx b/src/context/dyslexiaMode.test.tsx
index 64ad090b..56a583b4 100644
--- a/src/context/dyslexiaMode.test.tsx
+++ b/src/context/dyslexiaMode.test.tsx
@@ -1,7 +1,7 @@
import React, { ReactNode } from 'react';
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { AppProvider, useApp } from './AppContext';
+import { AppProvider, useSettings } from './AppContext';
import * as storage from '../store/storage';
vi.mock('../store/storage', () => ({
@@ -91,7 +91,7 @@ describe('Dyslexia-friendly reading mode', () => {
const wrapper = ({ children }: { children: ReactNode }) => {children};
it('sets line-height/letter-spacing CSS vars and persists the setting when enabled', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
act(() => {
result.current.updateSettings({ dyslexiaFriendlyMode: true });
@@ -104,7 +104,7 @@ describe('Dyslexia-friendly reading mode', () => {
});
it('removes the CSS vars when disabled again', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
act(() => {
result.current.updateSettings({ dyslexiaFriendlyMode: true });
@@ -118,7 +118,7 @@ describe('Dyslexia-friendly reading mode', () => {
});
it('sets dir="rtl" when the active language is Arabic, and "ltr" otherwise', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
expect(document.documentElement.dir).toBe('ltr');
diff --git a/src/context/themeBundleApply.test.tsx b/src/context/themeBundleApply.test.tsx
index df81ba72..256bff05 100644
--- a/src/context/themeBundleApply.test.tsx
+++ b/src/context/themeBundleApply.test.tsx
@@ -1,7 +1,7 @@
import React, { ReactNode } from 'react';
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { AppProvider, useApp } from './AppContext';
+import { AppProvider, useSettings } from './AppContext';
import { THEME_BUNDLES } from '../data/themes';
import * as storage from '../store/storage';
import type { AppSettings } from '../types';
@@ -111,7 +111,7 @@ describe('Theme bundle application', () => {
const wrapper = ({ children }: { children: ReactNode }) => {children};
it.each(THEME_BUNDLES)('applying the "$id" bundle updates settings to match the bundle', (theme) => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
act(() => {
applyTheme(result.current.updateSettings, result.current.settings, theme);
@@ -126,7 +126,7 @@ describe('Theme bundle application', () => {
});
it('preserves all other defaultFormat fields when applying a theme (spread regression)', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
const before = result.current.settings.defaultFormat;
const theme = THEME_BUNDLES[0];
@@ -155,7 +155,7 @@ describe('Theme bundle application', () => {
});
it('switching between two bundles updates settings to the latest bundle', () => {
- const { result } = renderHook(() => useApp(), { wrapper });
+ const { result } = renderHook(() => useSettings(), { wrapper });
const [first, second] = THEME_BUNDLES;
act(() => {
diff --git a/src/hooks/__tests__/hooks.test.ts b/src/hooks/__tests__/hooks.test.ts
index ef0b3d9c..93aef62d 100644
--- a/src/hooks/__tests__/hooks.test.ts
+++ b/src/hooks/__tests__/hooks.test.ts
@@ -7,7 +7,7 @@ import { useDbStatus } from '../useDbStatus';
// ─── Mocks ────────────────────────────────────────────────────────────────────
-// Mutable state consumed by useApp mock (useOverdueStudents)
+// Mutable state consumed by the AppContext mock (useOverdueStudents)
let mockStudents: Student[] = [];
let mockStudentRubrics: StudentRubric[] = [];
let mockThreshold = 7;
@@ -18,14 +18,13 @@ const makeAppContextMock = () => ({
settings: { overdueReminderThreshold: mockThreshold },
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
- useRoster: () => ({ students: mockStudents, studentRubrics: mockStudentRubrics }),
- useAuthoring: () => ({}),
- useAssessment: () => ({}),
- useEssays: () => ({}),
- useFlashcards: () => ({}),
- useSettings: () => ({ settings: { overdueReminderThreshold: mockThreshold } }),
- usePlatform: () => ({}),
+ useRoster: () => makeAppContextMock(),
+ useAuthoring: () => makeAppContextMock(),
+ useAssessment: () => makeAppContextMock(),
+ useEssays: () => makeAppContextMock(),
+ useFlashcards: () => makeAppContextMock(),
+ useSettings: () => makeAppContextMock(),
+ usePlatform: () => makeAppContextMock(),
}));
// Mutable unsub functions so we can verify cleanup
diff --git a/src/hooks/useNotificationFeed.test.ts b/src/hooks/useNotificationFeed.test.ts
index 4d723b92..0dbfbc96 100644
--- a/src/hooks/useNotificationFeed.test.ts
+++ b/src/hooks/useNotificationFeed.test.ts
@@ -29,19 +29,13 @@ const makeAppContextMock = () => ({
markMessageReadByTeacher: mockMarkMessageReadByTeacher,
});
vi.mock('../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
- useRoster: () => ({ students: mockStudents, studentRubrics: mockStudentRubrics }),
- useAuthoring: () => ({ rubrics: mockRubrics }),
- useAssessment: () => ({ peerReviews: mockPeerReviews }),
- useEssays: () => ({
- messages: mockMessages,
- notificationDismissals: mockNotificationDismissals,
- dismissNotification: mockDismissNotification,
- markMessageReadByTeacher: mockMarkMessageReadByTeacher,
- }),
- useFlashcards: () => ({}),
- useSettings: () => ({ settings: { overdueReminderThreshold: 7 } }),
- usePlatform: () => ({}),
+ useRoster: () => makeAppContextMock(),
+ useAuthoring: () => makeAppContextMock(),
+ useAssessment: () => makeAppContextMock(),
+ useEssays: () => makeAppContextMock(),
+ useFlashcards: () => makeAppContextMock(),
+ useSettings: () => makeAppContextMock(),
+ usePlatform: () => makeAppContextMock(),
}));
const rubric: Rubric = {
diff --git a/src/pages/__tests__/ActivityDashboardPage.test.tsx b/src/pages/__tests__/ActivityDashboardPage.test.tsx
index 7f845f97..a7961811 100644
--- a/src/pages/__tests__/ActivityDashboardPage.test.tsx
+++ b/src/pages/__tests__/ActivityDashboardPage.test.tsx
@@ -62,7 +62,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/AttachmentsPage.test.tsx b/src/pages/__tests__/AttachmentsPage.test.tsx
index 7cf61b27..98ad1f7d 100644
--- a/src/pages/__tests__/AttachmentsPage.test.tsx
+++ b/src/pages/__tests__/AttachmentsPage.test.tsx
@@ -62,7 +62,6 @@ const mockAppValue: Record = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/ComparativeGrading.test.tsx b/src/pages/__tests__/ComparativeGrading.test.tsx
index 5647fd50..470a8426 100644
--- a/src/pages/__tests__/ComparativeGrading.test.tsx
+++ b/src/pages/__tests__/ComparativeGrading.test.tsx
@@ -47,7 +47,7 @@ const mockSettings: AppSettings = {
const mockSaveStudentRubric = vi.fn();
const mockNavigate = vi.fn();
-// Stable references — a useApp() mock that builds new array literals on every call
+// Stable references — a domain-hook mock that builds new array literals on every call
// defeats this page's useMemo/useEffect deps and causes infinite render loops.
const mockRubricsArr = [mockRubric];
const mockStudentsArr = [mockStudentA, mockStudentB];
@@ -67,7 +67,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/DocsPage.test.tsx b/src/pages/__tests__/DocsPage.test.tsx
index 8f6fd05a..6d10b344 100644
--- a/src/pages/__tests__/DocsPage.test.tsx
+++ b/src/pages/__tests__/DocsPage.test.tsx
@@ -22,7 +22,6 @@ const makeAppContextMock = () => ({
studentRubrics: [],
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/EssayBuilderPage.test.tsx b/src/pages/__tests__/EssayBuilderPage.test.tsx
index 39d4fb94..4f20aa96 100644
--- a/src/pages/__tests__/EssayBuilderPage.test.tsx
+++ b/src/pages/__tests__/EssayBuilderPage.test.tsx
@@ -125,7 +125,6 @@ const makeAppContextMock = () => ({
...appOverrides,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/EssayListPage.test.tsx b/src/pages/__tests__/EssayListPage.test.tsx
index 088ecf0c..1c8ba023 100644
--- a/src/pages/__tests__/EssayListPage.test.tsx
+++ b/src/pages/__tests__/EssayListPage.test.tsx
@@ -74,7 +74,6 @@ const makeAppContextMock = () => ({
...appOverrides,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/ExportPage.test.tsx b/src/pages/__tests__/ExportPage.test.tsx
index 278b2659..ced8695b 100644
--- a/src/pages/__tests__/ExportPage.test.tsx
+++ b/src/pages/__tests__/ExportPage.test.tsx
@@ -108,7 +108,6 @@ const makeAppContextMock = () => ({
...appOverrides,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/GradeStudent.test.tsx b/src/pages/__tests__/GradeStudent.test.tsx
index f5b6f6ca..e6fa15fd 100644
--- a/src/pages/__tests__/GradeStudent.test.tsx
+++ b/src/pages/__tests__/GradeStudent.test.tsx
@@ -54,7 +54,7 @@ const mockSaveStudentRubric = vi.fn();
const mockUpdateSettings = vi.fn();
// Stable references — see ComparativeGrading/StatisticsPage tests for why this matters:
-// fresh array/object literals on every useApp() call defeat memo/effect deps and can
+// fresh array/object literals on every domain-hook call defeat memo/effect deps and can
// cause infinite render loops.
const mockRubricsArr = [mockRubric];
const mockStudentsArr = [mockStudent, mockStudentBob];
@@ -92,7 +92,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/LiveMonitorPage.test.tsx b/src/pages/__tests__/LiveMonitorPage.test.tsx
index cd1cca88..6e721a06 100644
--- a/src/pages/__tests__/LiveMonitorPage.test.tsx
+++ b/src/pages/__tests__/LiveMonitorPage.test.tsx
@@ -33,7 +33,6 @@ vi.mock('../../services/database', () => ({
let mockUseApp: any;
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/MarketplacePage.test.tsx b/src/pages/__tests__/MarketplacePage.test.tsx
index b58a0faf..61daf4fd 100644
--- a/src/pages/__tests__/MarketplacePage.test.tsx
+++ b/src/pages/__tests__/MarketplacePage.test.tsx
@@ -49,7 +49,6 @@ const mockAppValue: Record = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/MessagesPage.test.tsx b/src/pages/__tests__/MessagesPage.test.tsx
index 9186d0cf..a8fab029 100644
--- a/src/pages/__tests__/MessagesPage.test.tsx
+++ b/src/pages/__tests__/MessagesPage.test.tsx
@@ -48,7 +48,6 @@ const makeAppContextMock = () => ({
...appOverrides,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/ModerationQueuePage.test.tsx b/src/pages/__tests__/ModerationQueuePage.test.tsx
index a2bb2b15..f3ad964a 100644
--- a/src/pages/__tests__/ModerationQueuePage.test.tsx
+++ b/src/pages/__tests__/ModerationQueuePage.test.tsx
@@ -80,7 +80,6 @@ const makeAppContextMock = () => ({
...appOverrides,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/NotificationsPage.test.tsx b/src/pages/__tests__/NotificationsPage.test.tsx
index 76e1d859..fd518ad8 100644
--- a/src/pages/__tests__/NotificationsPage.test.tsx
+++ b/src/pages/__tests__/NotificationsPage.test.tsx
@@ -84,14 +84,13 @@ vi.mock('../../hooks/useNotificationFeed', () => ({
}),
}));
-// Topbar (rendered by NotificationsPage) reads settings/classes off useApp() directly.
+// Topbar (rendered by NotificationsPage) reads settings/classes off the domain hooks directly.
const makeAppContextMock = () => ({
settings: { theme: 'dark', language: 'en' },
updateSettings: vi.fn(),
classes: [],
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/PeerReviewAnalyticsPage.test.tsx b/src/pages/__tests__/PeerReviewAnalyticsPage.test.tsx
index 7627ca09..1d8f9731 100644
--- a/src/pages/__tests__/PeerReviewAnalyticsPage.test.tsx
+++ b/src/pages/__tests__/PeerReviewAnalyticsPage.test.tsx
@@ -74,7 +74,6 @@ const peerReview: StudentRubric = {
let mockUseApp: any;
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/PeerReviewView.test.tsx b/src/pages/__tests__/PeerReviewView.test.tsx
index 097efe08..d429ef4e 100644
--- a/src/pages/__tests__/PeerReviewView.test.tsx
+++ b/src/pages/__tests__/PeerReviewView.test.tsx
@@ -62,7 +62,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/RubricBuilder.test.tsx b/src/pages/__tests__/RubricBuilder.test.tsx
index e7740202..0304a270 100644
--- a/src/pages/__tests__/RubricBuilder.test.tsx
+++ b/src/pages/__tests__/RubricBuilder.test.tsx
@@ -137,7 +137,6 @@ const makeAppContextMock = () => ({
...appOverrides,
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/RubricList.test.tsx b/src/pages/__tests__/RubricList.test.tsx
index ad8c8bec..6ef6f44a 100644
--- a/src/pages/__tests__/RubricList.test.tsx
+++ b/src/pages/__tests__/RubricList.test.tsx
@@ -56,7 +56,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/SelfAssessPage.test.tsx b/src/pages/__tests__/SelfAssessPage.test.tsx
index da8cafe3..849ab7d3 100644
--- a/src/pages/__tests__/SelfAssessPage.test.tsx
+++ b/src/pages/__tests__/SelfAssessPage.test.tsx
@@ -83,7 +83,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/SettingsPage.test.tsx b/src/pages/__tests__/SettingsPage.test.tsx
index a06be56c..f97aca4c 100644
--- a/src/pages/__tests__/SettingsPage.test.tsx
+++ b/src/pages/__tests__/SettingsPage.test.tsx
@@ -26,7 +26,7 @@ const mockAddGradeScale = vi.fn(() => ({ ...mockGradeScale, id: 'gs2' }));
const mockShowToast = vi.fn();
// Stable references — see other page tests in this directory for why fresh array/object
-// literals on every useApp() call can cause infinite render loops via memo/effect deps.
+// literals on every domain-hook call can cause infinite render loops via memo/effect deps.
const mockGradeScalesArr = [mockGradeScale];
const mockCommentBankArr: never[] = [];
const mockExportTemplatesArr: never[] = [];
@@ -59,7 +59,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/SpeakingSession.test.tsx b/src/pages/__tests__/SpeakingSession.test.tsx
index 75e84db9..6fa0c26e 100644
--- a/src/pages/__tests__/SpeakingSession.test.tsx
+++ b/src/pages/__tests__/SpeakingSession.test.tsx
@@ -71,7 +71,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/StatisticsPage.test.tsx b/src/pages/__tests__/StatisticsPage.test.tsx
index 15b862de..4682a130 100644
--- a/src/pages/__tests__/StatisticsPage.test.tsx
+++ b/src/pages/__tests__/StatisticsPage.test.tsx
@@ -77,7 +77,7 @@ const mockSettings: AppSettings = {
const mockUpdateSettings = vi.fn();
// Stable references: StatisticsPage has effects/memos keyed on `classes` etc. — a mock
-// that builds new array literals on every useApp() call defeats those memo deps and
+// that builds new array literals on every domain-hook call defeats those memo deps and
// causes an infinite render loop (each render sees a "new" classes array).
const mockRubricsArr = [mockRubric];
const mockStudentsArr = [mockStudentA, mockStudentB];
@@ -99,7 +99,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/StudentLearningPathPage.test.tsx b/src/pages/__tests__/StudentLearningPathPage.test.tsx
index 6e7ab1ce..dc8ce973 100644
--- a/src/pages/__tests__/StudentLearningPathPage.test.tsx
+++ b/src/pages/__tests__/StudentLearningPathPage.test.tsx
@@ -37,7 +37,6 @@ const makeAppContextMock = () => ({
flashcardDecks: [],
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/StudentPortalPage.test.tsx b/src/pages/__tests__/StudentPortalPage.test.tsx
index 161d5ae7..d03571f2 100644
--- a/src/pages/__tests__/StudentPortalPage.test.tsx
+++ b/src/pages/__tests__/StudentPortalPage.test.tsx
@@ -200,7 +200,6 @@ const mockAppValue: Record = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/StudentProfilePage.test.tsx b/src/pages/__tests__/StudentProfilePage.test.tsx
index a6c6679a..67a50efb 100644
--- a/src/pages/__tests__/StudentProfilePage.test.tsx
+++ b/src/pages/__tests__/StudentProfilePage.test.tsx
@@ -96,7 +96,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/StudentsPage.test.tsx b/src/pages/__tests__/StudentsPage.test.tsx
index b717e9c8..ec3dac00 100644
--- a/src/pages/__tests__/StudentsPage.test.tsx
+++ b/src/pages/__tests__/StudentsPage.test.tsx
@@ -114,7 +114,6 @@ const mockAppValue = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockAppValue,
useRoster: () => mockAppValue,
useAuthoring: () => mockAppValue,
useAssessment: () => mockAppValue,
diff --git a/src/pages/__tests__/TestBuilderPage.test.tsx b/src/pages/__tests__/TestBuilderPage.test.tsx
index 27f04ee7..6c13820d 100644
--- a/src/pages/__tests__/TestBuilderPage.test.tsx
+++ b/src/pages/__tests__/TestBuilderPage.test.tsx
@@ -74,7 +74,6 @@ const mockUseApp = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/TestListPage.test.tsx b/src/pages/__tests__/TestListPage.test.tsx
index b22c923d..33716bbb 100644
--- a/src/pages/__tests__/TestListPage.test.tsx
+++ b/src/pages/__tests__/TestListPage.test.tsx
@@ -80,7 +80,6 @@ const mockUseApp = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/TestResultsPage.test.tsx b/src/pages/__tests__/TestResultsPage.test.tsx
index b637dcf2..9c6cdbc8 100644
--- a/src/pages/__tests__/TestResultsPage.test.tsx
+++ b/src/pages/__tests__/TestResultsPage.test.tsx
@@ -84,7 +84,6 @@ const mockUseApp = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/VocabularyDashboardPage.test.tsx b/src/pages/__tests__/VocabularyDashboardPage.test.tsx
index b10d0f22..0340cebc 100644
--- a/src/pages/__tests__/VocabularyDashboardPage.test.tsx
+++ b/src/pages/__tests__/VocabularyDashboardPage.test.tsx
@@ -66,7 +66,6 @@ const makeAppContextMock = () => ({
updateSettings: vi.fn(),
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/pages-phase4.a11y.test.tsx b/src/pages/__tests__/pages-phase4.a11y.test.tsx
index 2867b486..31a77392 100644
--- a/src/pages/__tests__/pages-phase4.a11y.test.tsx
+++ b/src/pages/__tests__/pages-phase4.a11y.test.tsx
@@ -70,7 +70,6 @@ const makeAppContextMock = () => ({
deleteGradingTask: vi.fn(),
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/pages.a11y.test.tsx b/src/pages/__tests__/pages.a11y.test.tsx
index 91e00f67..0ecadcd2 100644
--- a/src/pages/__tests__/pages.a11y.test.tsx
+++ b/src/pages/__tests__/pages.a11y.test.tsx
@@ -89,7 +89,7 @@ const mockFlashcardDeck: FlashcardDeck = {
updatedAt: '2024-01-01T00:00:00Z',
};
-// Per-test overrides merged into the mocked useApp() return value. Default empty so
+// Per-test overrides merged into the mocked domain-hook return value. Default empty so
// the shared mock stays as the other suites expect; a suite that needs seeded data
// (e.g. TestResultsPage's graded-submission view) sets this in beforeEach and resets
// it in afterEach.
@@ -97,7 +97,7 @@ let appStateOverride: Record = {};
// ─── Module mocks ──────────────────────────────────────────────────────────────
-// Build the context value ONCE so useApp() returns stable array references across
+// Build the context value ONCE so the domain hooks return stable array references across
// renders — a fresh object/array each call makes any page whose effect depends on a
// context array re-run that effect forever (setState → re-render → new array ref →
// effect → …), which OOMs the worker. `appStateOverride` is spread on top so seeded
@@ -216,7 +216,6 @@ const base = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => ({ ...base, ...appStateOverride }),
useRoster: () => ({ ...base, ...appStateOverride }),
useAuthoring: () => ({ ...base, ...appStateOverride }),
useAssessment: () => ({ ...base, ...appStateOverride }),
diff --git a/src/pages/__tests__/pages.admin.a11y.test.tsx b/src/pages/__tests__/pages.admin.a11y.test.tsx
index 395fa9a2..33bbaa15 100644
--- a/src/pages/__tests__/pages.admin.a11y.test.tsx
+++ b/src/pages/__tests__/pages.admin.a11y.test.tsx
@@ -45,7 +45,7 @@ const mockRubric: Rubric = {
// ─── Module mocks ──────────────────────────────────────────────────────────────
-// Build the context value ONCE so useApp() returns stable array references across
+// Build the context value ONCE so the domain hooks return stable array references across
// renders — a fresh object/array each call makes any page whose effect depends on a
// context array re-run that effect forever (setState → re-render → new array ref →
// effect → …), which OOMs the worker. In the real app these references are stable.
@@ -179,7 +179,6 @@ vi.mock('../../context/AppContext', () => {
signOutFromDatabase: vi.fn(() => Promise.resolve()),
};
return {
- useApp: () => base,
useRoster: () => base,
useAuthoring: () => base,
useAssessment: () => base,
diff --git a/src/pages/__tests__/pages.deepcoverage.test.tsx b/src/pages/__tests__/pages.deepcoverage.test.tsx
index 6baf29c7..c384f5b8 100644
--- a/src/pages/__tests__/pages.deepcoverage.test.tsx
+++ b/src/pages/__tests__/pages.deepcoverage.test.tsx
@@ -161,7 +161,6 @@ const mockUseApp = {
};
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/pages.deepcoverage2.test.tsx b/src/pages/__tests__/pages.deepcoverage2.test.tsx
index 1844a011..7c9f7e98 100644
--- a/src/pages/__tests__/pages.deepcoverage2.test.tsx
+++ b/src/pages/__tests__/pages.deepcoverage2.test.tsx
@@ -172,7 +172,6 @@ Object.defineProperty(navigator, 'clipboard', {
// ─── Module mocks ─────────────────────────────────────────────────────────────
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/pages.functions.test.tsx b/src/pages/__tests__/pages.functions.test.tsx
index 449e3bab..98c73405 100644
--- a/src/pages/__tests__/pages.functions.test.tsx
+++ b/src/pages/__tests__/pages.functions.test.tsx
@@ -178,7 +178,6 @@ function makeApp(overrides = {}) {
let currentApp = makeApp();
vi.mock('../../context/AppContext', () => ({
- useApp: () => currentApp,
useRoster: () => currentApp,
useAuthoring: () => currentApp,
useAssessment: () => currentApp,
diff --git a/src/pages/__tests__/pages.interaction.test.tsx b/src/pages/__tests__/pages.interaction.test.tsx
index 1cb799ec..874da44c 100644
--- a/src/pages/__tests__/pages.interaction.test.tsx
+++ b/src/pages/__tests__/pages.interaction.test.tsx
@@ -160,7 +160,6 @@ const mockUseApp = {
// ─── Module mocks ─────────────────────────────────────────────────────────────
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/pages.missing.test.tsx b/src/pages/__tests__/pages.missing.test.tsx
index 2da64d2c..6fdc81d3 100644
--- a/src/pages/__tests__/pages.missing.test.tsx
+++ b/src/pages/__tests__/pages.missing.test.tsx
@@ -56,7 +56,6 @@ const makeAppContextMock = () => ({
getActiveGradeScale: vi.fn(),
});
vi.mock('../../context/AppContext', () => ({
- useApp: () => makeAppContextMock(),
useRoster: () => makeAppContextMock(),
useAuthoring: () => makeAppContextMock(),
useAssessment: () => makeAppContextMock(),
diff --git a/src/pages/__tests__/pages.smoke.test.tsx b/src/pages/__tests__/pages.smoke.test.tsx
index c8444108..b9f7b3a9 100644
--- a/src/pages/__tests__/pages.smoke.test.tsx
+++ b/src/pages/__tests__/pages.smoke.test.tsx
@@ -153,7 +153,6 @@ const mockUseApp = {
// ─── Module mocks ──────────────────────────────────────────────────────────────
vi.mock('../../context/AppContext', () => ({
- useApp: () => mockUseApp,
useRoster: () => mockUseApp,
useAuthoring: () => mockUseApp,
useAssessment: () => mockUseApp,
diff --git a/src/pages/__tests__/pages.student.a11y.test.tsx b/src/pages/__tests__/pages.student.a11y.test.tsx
index ab7cd42b..b8afe498 100644
--- a/src/pages/__tests__/pages.student.a11y.test.tsx
+++ b/src/pages/__tests__/pages.student.a11y.test.tsx
@@ -45,7 +45,7 @@ const mockRubric: Rubric = {
// ─── Module mocks ──────────────────────────────────────────────────────────────
-// Build the context value ONCE so useApp() returns a stable object (and stable
+// Build the context value ONCE so the domain hooks return a stable object (and stable
// array references) across renders. A fresh object/array on every call makes any
// page whose effect depends on a context array re-run that effect every render —
// e.g. PeerReviewView setEntry()s a new object keyed on `peerReviews`, which loops
@@ -198,7 +198,6 @@ vi.mock('../../context/AppContext', () => {
fetchMyFlashcardReview: vi.fn(() => Promise.resolve(null)),
};
return {
- useApp: () => base,
useRoster: () => base,
useAuthoring: () => base,
useAssessment: () => base,
diff --git a/src/pages/__tests__/pages.unit.test.tsx b/src/pages/__tests__/pages.unit.test.tsx
index 80bff03d..e33809d1 100644
--- a/src/pages/__tests__/pages.unit.test.tsx
+++ b/src/pages/__tests__/pages.unit.test.tsx
@@ -146,7 +146,6 @@ function makeApp(overrides = {}) {
let currentApp = makeApp();
vi.mock('../../context/AppContext', () => ({
- useApp: () => currentApp,
useRoster: () => currentApp,
useAuthoring: () => currentApp,
useAssessment: () => currentApp,