Public Access
feat: enhance testing framework with new test cases and configurations
This commit is contained in:
1 parent
a37c093cc4
commit
ef8fe6f938
18 files changed
+793
-6
No files matched your search
@@ -0,0 +1,20 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { ApiError } from './api'
|
||||
|
||||
describe('ApiError', () => {
|
||||
it('extends Error', () => {
|
||||
const err = new ApiError(404, 'Not found')
|
||||
expect(err).toBeInstanceOf(Error)
|
||||
})
|
||||
|
||||
it('sets status and message', () => {
|
||||
const err = new ApiError(500, 'Server error')
|
||||
expect(err.status).toBe(500)
|
||||
expect(err.message).toBe('Server error')
|
||||
})
|
||||
|
||||
it('sets name to ApiError', () => {
|
||||
const err = new ApiError(401, 'Unauthorized')
|
||||
expect(err.name).toBe('ApiError')
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { slugify } from './slug'
|
||||
import { slugify, getExamUrlId, extractExamSlug } from './slug'
|
||||
|
||||
describe('slugify', () => {
|
||||
it('lowercases', () => {
|
||||
@@ -24,3 +24,31 @@ describe('slugify', () => {
|
||||
expect(slugify('')).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getExamUrlId', () => {
|
||||
it('returns slug with id when id exists', () => {
|
||||
expect(getExamUrlId({ id: 'abc12345', title: 'My Exam' })).toBe('my-exam-abc12345')
|
||||
})
|
||||
|
||||
it('returns slug only when id is missing', () => {
|
||||
expect(getExamUrlId({ title: 'My Exam' })).toBe('my-exam')
|
||||
})
|
||||
})
|
||||
|
||||
describe('extractExamSlug', () => {
|
||||
it('extracts slug from slug-id format', () => {
|
||||
expect(extractExamSlug('my-exam-abc12345')).toBe('my-exam')
|
||||
})
|
||||
|
||||
it('returns full string when no dash', () => {
|
||||
expect(extractExamSlug('myexam')).toBe('myexam')
|
||||
})
|
||||
|
||||
it('returns full string when suffix is too short to be an id', () => {
|
||||
expect(extractExamSlug('my-exam-abc')).toBe('my-exam-abc')
|
||||
})
|
||||
|
||||
it('handles slug with multiple dashes', () => {
|
||||
expect(extractExamSlug('my-cool-exam-abc12345678')).toBe('my-cool-exam')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { loadState, saveState, clearState, loadHistory, appendHistory, clearHistory } from './storage'
|
||||
|
||||
const storage: Record<string, string> = {}
|
||||
|
||||
beforeEach(() => {
|
||||
Object.keys(storage).forEach((k) => delete storage[k])
|
||||
globalThis.localStorage = {
|
||||
getItem: (key: string) => storage[key] ?? null,
|
||||
setItem: (key: string, value: string) => { storage[key] = value },
|
||||
removeItem: (key: string) => { delete storage[key] },
|
||||
length: 0,
|
||||
key: () => null,
|
||||
clear: () => { Object.keys(storage).forEach((k) => delete storage[k]) },
|
||||
} as Storage
|
||||
})
|
||||
|
||||
describe('loadState / saveState / clearState', () => {
|
||||
it('returns null when nothing saved', () => {
|
||||
expect(loadState()).toBeNull()
|
||||
})
|
||||
|
||||
it('returns saved state', () => {
|
||||
const data = { exam: { id: '1', title: 'E', questions: [] }, state: { currentIdx: 0, answers: {}, flags: {}, notes: '', startedAt: Date.now(), feedbackMode: 'end' as const } }
|
||||
saveState(data)
|
||||
expect(loadState()).toEqual(data)
|
||||
})
|
||||
|
||||
it('clears saved state', () => {
|
||||
saveState({ exam: { id: '1', title: 'E', questions: [] }, state: { currentIdx: 0, answers: {}, flags: {}, notes: '', startedAt: Date.now(), feedbackMode: 'end' as const } })
|
||||
clearState()
|
||||
expect(loadState()).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for corrupted json', () => {
|
||||
localStorage.setItem('brainwave.exam.state', 'not-json')
|
||||
expect(loadState()).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('loadHistory / appendHistory / clearHistory', () => {
|
||||
it('returns empty array when nothing saved', () => {
|
||||
expect(loadHistory()).toEqual([])
|
||||
})
|
||||
|
||||
it('appends entries and limits to 30', () => {
|
||||
for (let i = 0; i < 35; i++) {
|
||||
appendHistory({ id: String(i), title: `Exam ${i}`, completedAt: Date.now(), elapsedMs: 0, gradedCount: 10, correctCount: i, totalPoints: 10, scorePoints: i, passingScore: 5, exam: { title: `Exam ${i}`, questions: [] }, state: { currentIdx: 0, answers: {}, flags: {}, notes: '', startedAt: Date.now(), feedbackMode: 'end' as const } })
|
||||
}
|
||||
const hist = loadHistory()
|
||||
expect(hist.length).toBe(30)
|
||||
expect(hist[0].id).toBe('34')
|
||||
})
|
||||
|
||||
it('clears history', () => {
|
||||
appendHistory({ id: '1', title: 'E', completedAt: Date.now(), elapsedMs: 0, gradedCount: 1, correctCount: 1, totalPoints: 1, scorePoints: 1, passingScore: 1, exam: { title: 'E', questions: [] }, state: { currentIdx: 0, answers: {}, flags: {}, notes: '', startedAt: Date.now(), feedbackMode: 'end' as const } })
|
||||
clearHistory()
|
||||
expect(loadHistory()).toEqual([])
|
||||
})
|
||||
|
||||
it('returns empty array for corrupted json', () => {
|
||||
localStorage.setItem('brainwave.exam.history', 'not-json')
|
||||
expect(loadHistory()).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { validateExam, parseExam } from './validation'
|
||||
|
||||
describe('validateExam', () => {
|
||||
it('returns error for non-object', () => {
|
||||
expect(validateExam(null)).toBe('Not a JSON object')
|
||||
expect(validateExam('string')).toBe('Not a JSON object')
|
||||
expect(validateExam(123)).toBe('Not a JSON object')
|
||||
})
|
||||
|
||||
it('returns error for missing title', () => {
|
||||
expect(validateExam({ questions: [] })).toBe('Missing "title"')
|
||||
})
|
||||
|
||||
it('returns error for missing questions array', () => {
|
||||
expect(validateExam({ title: 'Exam' })).toBe('Missing "questions" array')
|
||||
})
|
||||
|
||||
it('returns error for empty questions array', () => {
|
||||
expect(validateExam({ title: 'Exam', questions: [] })).toBe('Missing "questions" array')
|
||||
})
|
||||
|
||||
it('returns error for question missing id', () => {
|
||||
expect(validateExam({ title: 'Exam', questions: [{ type: 'essay', prompt: 'Q' }] })).toBe('Question missing id')
|
||||
})
|
||||
|
||||
it('returns error for question missing type', () => {
|
||||
expect(validateExam({ title: 'Exam', questions: [{ id: '1', prompt: 'Q' }] })).toBe('Question "1" missing type')
|
||||
})
|
||||
|
||||
it('returns error for question missing prompt', () => {
|
||||
expect(validateExam({ title: 'Exam', questions: [{ id: '1', type: 'essay' }] })).toBe('Question "1" missing prompt')
|
||||
})
|
||||
|
||||
it('returns null for valid exam', () => {
|
||||
expect(validateExam({ title: 'Exam', questions: [{ id: '1', type: 'essay', prompt: 'Q' }] })).toBe(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseExam', () => {
|
||||
it('generates id when missing', () => {
|
||||
const exam = parseExam({ title: 'T', questions: [] })
|
||||
expect(exam.id).toBeTruthy()
|
||||
expect(typeof exam.id).toBe('string')
|
||||
})
|
||||
|
||||
it('preserves existing id', () => {
|
||||
const exam = parseExam({ id: 'abc123', title: 'T', questions: [] })
|
||||
expect(exam.id).toBe('abc123')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user