OpenMAIC-React / tests /quiz /persistence.test.ts
muthuk1's picture
Add missing files: LICENSE, Dockerfile, .github, tests, e2e, eval, scripts, configs
a0ebf39 verified
import { describe, it, expect, beforeEach, vi } from 'vitest';
const store: Record<string, string> = {};
const localStorageStub = {
getItem: (k: string) => (k in store ? store[k] : null),
setItem: (k: string, v: string) => {
store[k] = String(v);
},
removeItem: (k: string) => {
delete store[k];
},
clear: () => {
for (const k of Object.keys(store)) delete store[k];
},
key: (i: number) => Object.keys(store)[i] ?? null,
get length() {
return Object.keys(store).length;
},
};
vi.stubGlobal('localStorage', localStorageStub);
vi.stubGlobal('window', { localStorage: localStorageStub });
import {
ANSWERS_KEY_PREFIX,
DRAFT_KEY_PREFIX,
RESULTS_KEY_PREFIX,
clearAllForScene,
clearSubmitted,
readAnswersForSummary,
readSubmittedState,
writeSubmittedAnswers,
writeSubmittedResults,
} from '@/lib/quiz/persistence';
import type { QuestionResult } from '@/lib/quiz/grading';
describe('quiz persistence', () => {
beforeEach(() => {
localStorageStub.clear();
});
it('readSubmittedState returns null when nothing is stored', () => {
expect(readSubmittedState('s1')).toBeNull();
});
it('returns answering state when only answers are stored', () => {
writeSubmittedAnswers('s1', { q1: 'a' });
expect(readSubmittedState('s1')).toEqual({ kind: 'answering', answers: { q1: 'a' } });
});
it('returns reviewing state when both answers and results are stored', () => {
const results: QuestionResult[] = [
{ questionId: 'q1', correct: true, status: 'correct', earned: 1 },
];
writeSubmittedAnswers('s1', { q1: 'a' });
writeSubmittedResults('s1', results);
expect(readSubmittedState('s1')).toEqual({
kind: 'reviewing',
answers: { q1: 'a' },
results,
});
});
it('falls back to answering when results array is empty', () => {
writeSubmittedAnswers('s1', { q1: 'a' });
writeSubmittedResults('s1', []);
expect(readSubmittedState('s1')).toEqual({ kind: 'answering', answers: { q1: 'a' } });
});
it('returns null on corrupt answers JSON', () => {
localStorageStub.setItem(ANSWERS_KEY_PREFIX + 's1', '{invalid');
expect(readSubmittedState('s1')).toBeNull();
});
it('clearSubmitted wipes answers + results but leaves draft intact', () => {
localStorageStub.setItem(DRAFT_KEY_PREFIX + 's1', JSON.stringify({ q1: 'b' }));
writeSubmittedAnswers('s1', { q1: 'a' });
writeSubmittedResults('s1', [
{ questionId: 'q1', correct: true, status: 'correct', earned: 1 },
]);
clearSubmitted('s1');
expect(readSubmittedState('s1')).toBeNull();
expect(localStorageStub.getItem(DRAFT_KEY_PREFIX + 's1')).not.toBeNull();
});
it('clearAllForScene wipes all three keys for a single scene', () => {
localStorageStub.setItem(DRAFT_KEY_PREFIX + 's1', '{}');
writeSubmittedAnswers('s1', { q1: 'a' });
writeSubmittedResults('s1', [
{ questionId: 'q1', correct: true, status: 'correct', earned: 1 },
]);
// unrelated scene should not be touched
writeSubmittedAnswers('s2', { q1: 'z' });
clearAllForScene('s1');
expect(localStorageStub.getItem(DRAFT_KEY_PREFIX + 's1')).toBeNull();
expect(localStorageStub.getItem(ANSWERS_KEY_PREFIX + 's1')).toBeNull();
expect(localStorageStub.getItem(RESULTS_KEY_PREFIX + 's1')).toBeNull();
expect(localStorageStub.getItem(ANSWERS_KEY_PREFIX + 's2')).not.toBeNull();
});
it('readAnswersForSummary prefers submitted over draft', () => {
localStorageStub.setItem(DRAFT_KEY_PREFIX + 's1', JSON.stringify({ q1: 'draft' }));
writeSubmittedAnswers('s1', { q1: 'submitted' });
expect(readAnswersForSummary('s1')).toEqual({ q1: 'submitted' });
});
it('readAnswersForSummary falls back to draft when no submission', () => {
localStorageStub.setItem(DRAFT_KEY_PREFIX + 's1', JSON.stringify({ q1: 'draft' }));
expect(readAnswersForSummary('s1')).toEqual({ q1: 'draft' });
});
it('readAnswersForSummary returns empty object when nothing is stored', () => {
expect(readAnswersForSummary('s1')).toEqual({});
});
it('tolerates corrupt draft JSON when no submission exists', () => {
localStorageStub.setItem(DRAFT_KEY_PREFIX + 's1', '{corrupt');
expect(readAnswersForSummary('s1')).toEqual({});
});
});