File size: 2,574 Bytes
9aee359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import { describe, expect, it } from 'vitest'
import { extractQuestions, parseQuestionsJson, readJsonFile } from './JSONUploadForm'

const validMCQ = {
  question_type: 'mcq',
  question_text: 'What is binary search complexity?',
  options: ['O(n)', 'O(log n)', 'O(n log n)', 'O(1)'],
  correct_answer: 'B',
  marks: 1,
  negative_marks: 0.33,
  subject: 'Algorithms',
}

describe('JSONUploadForm JSON parsing', () => {
  it('parses the expected object schema and normalizes question values', () => {
    const questions = parseQuestionsJson(JSON.stringify({ questions: [validMCQ] }))

    expect(questions).toHaveLength(1)
    expect(questions[0]).toMatchObject({
      question_type: 'mcq',
      question_text: validMCQ.question_text,
      options: validMCQ.options,
      correct_answer: 'B',
      marks: 1,
      negative_marks: 0.33,
    })
  })

  it('accepts an array root for pasted questions', () => {
    const questions = extractQuestions([validMCQ])

    expect(questions).toHaveLength(1)
    expect(questions[0].question_text).toBe(validMCQ.question_text)
  })

  it('normalizes MSQ answers and removes negative marks for non-MCQ questions', () => {
    const questions = extractQuestions([
      {
        ...validMCQ,
        question_type: 'msq',
        correct_answer: 'A, C',
        negative_marks: 0.66,
      },
    ])

    expect(questions[0].correct_answer).toBe('A,C')
    expect(questions[0].negative_marks).toBe(0)
  })

  it('rejects malformed JSON with a clear error', () => {
    expect(() => parseQuestionsJson('{ "questions": [ }')).toThrow(/Invalid JSON:/)
  })

  it('rejects roots that do not match the expected schema', () => {
    expect(() => extractQuestions({ items: [validMCQ] })).toThrow(
      'JSON root must be an array or an object with a "questions" array',
    )
  })

  it('rejects empty question arrays', () => {
    expect(() => extractQuestions({ questions: [] })).toThrow('JSON does not contain any questions')
  })

  it('rejects question objects with invalid fields', () => {
    expect(() => extractQuestions([{ ...validMCQ, options: ['A', 'B'] }])).toThrow(
      'Question 1 options must contain exactly 4 non-empty strings',
    )
  })

  it('reads a JSON file before validation', async () => {
    const file = new File([JSON.stringify({ questions: [validMCQ] })], 'questions.json', {
      type: 'application/json',
    })

    const text = await readJsonFile(file)
    const questions = parseQuestionsJson(text)

    expect(questions).toHaveLength(1)
    expect(questions[0].correct_answer).toBe('B')
  })
})