Files
wehub-resource-sync c48612c494
CI / E2E Tests (push) Has been cancelled
CI / Lint, Typecheck & Unit Tests (push) Has been cancelled
Docs Build / Build docs site (push) Has been cancelled
Publish @openmaic packages / Build, validate & publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:23 +08:00

72 lines
2.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { gradeChoiceQuestions, isShortAnswer } from '@/lib/quiz/grading';
import type { QuizQuestion } from '@/lib/types/stage';
function q(overrides: Partial<QuizQuestion>): QuizQuestion {
return {
id: 'q1',
type: 'single',
question: 'Pick one',
options: [
{ value: 'a', label: 'A' },
{ value: 'b', label: 'B' },
],
answer: ['a'],
hasAnswer: true,
points: 1,
...overrides,
};
}
describe('gradeChoiceQuestions', () => {
it('scores a correct single-choice answer', () => {
const results = gradeChoiceQuestions([q({})], { q1: 'a' });
expect(results).toHaveLength(1);
expect(results[0]).toMatchObject({ questionId: 'q1', correct: true, earned: 1 });
});
it('scores an incorrect single-choice answer', () => {
const results = gradeChoiceQuestions([q({})], { q1: 'b' });
expect(results[0]).toMatchObject({ correct: false, earned: 0 });
});
it('treats multi-choice order-insensitively', () => {
const question = q({ id: 'm1', type: 'multiple', answer: ['a', 'b'] });
const results = gradeChoiceQuestions([question], { m1: ['b', 'a'] });
expect(results[0].correct).toBe(true);
});
it('scores missing answer as incorrect', () => {
const results = gradeChoiceQuestions([q({})], {});
expect(results[0]).toMatchObject({ correct: false, earned: 0 });
});
it('skips short-answer questions', () => {
const sa = q({ id: 's1', type: 'short_answer', answer: [], hasAnswer: false });
const results = gradeChoiceQuestions([sa], { s1: 'anything' });
expect(results).toHaveLength(0);
});
it('honors custom point values', () => {
const results = gradeChoiceQuestions([q({ points: 5 })], { q1: 'a' });
expect(results[0].earned).toBe(5);
});
});
describe('isShortAnswer', () => {
it('returns true for type=short_answer', () => {
expect(isShortAnswer(q({ type: 'short_answer' }))).toBe(true);
});
it('classifies by type only: an unanswered choice question is still a choice question', () => {
// hasAnswer / empty answer must NOT re-route a single/multiple question to
// AI grading — the explicit type wins.
expect(isShortAnswer(q({ type: 'single', hasAnswer: false, answer: [] }))).toBe(false);
expect(isShortAnswer(q({ type: 'multiple', hasAnswer: undefined, answer: [] }))).toBe(false);
});
it('returns false for a regular choice question', () => {
expect(isShortAnswer(q({}))).toBe(false);
});
});