const { cleanFields, cleanAnswer, summarise } = require('../src/utils/feedback'); describe('cleanFields', () => { test('keeps labelled questions in order, tidying choices', () => { const out = cleanFields([ { type: 'rating', label: ' Overall ', isRequired: true }, { type: 'text', label: '' }, { type: 'multiple_choice', label: 'Best part', options: ['Worship', ' Talks ', 'Worship', ''] }, { type: 'statement', label: 'Thanks!', isRequired: true }, ]); expect(out.map(f => [f.type, f.label, f.order])).toEqual([['rating', 'Overall', 0], ['multiple_choice', 'Best part', 1], ['statement', 'Thanks!', 2]]); expect(out[1].options).toEqual(['Worship', 'Talks']); expect(out[2].isRequired).toBe(false); }); test('rejects bad forms', () => { expect(() => cleanFields([{ type: 'stars', label: 'X' }])).toThrow(/unknown type/); expect(() => cleanFields([{ type: 'multiple_choice', label: 'X', options: ['only one'] }])).toThrow(/two choices/); expect(() => cleanFields([{ type: 'statement', label: 'Hello' }])).toThrow(/at least one question/); }); }); describe('cleanAnswer', () => { test('ratings are whole stars from 1 to 5', () => { expect(cleanAnswer({ type: 'rating', label: 'R' }, '4')).toBe('4'); expect(() => cleanAnswer({ type: 'rating', label: 'R' }, '6')).toThrow(); expect(() => cleanAnswer({ type: 'rating', label: 'R' }, '3.5')).toThrow(); }); test('multiple choice must be one of the options', () => { const f = { type: 'multiple_choice', label: 'M', options: ['A', 'B'] }; expect(cleanAnswer(f, 'B')).toBe('B'); expect(() => cleanAnswer(f, 'C')).toThrow(); }); test('required questions need an answer; optional ones may be blank', () => { expect(() => cleanAnswer({ type: 'text', label: 'Why?', isRequired: true }, ' ')).toThrow(/Please answer/); expect(cleanAnswer({ type: 'text', label: 'Why?' }, '')).toBeNull(); expect(cleanAnswer({ type: 'yes_no', label: 'Again?' }, 'YES')).toBe('yes'); }); }); describe('summarise', () => { const fields = [ { id: 'r', type: 'rating', label: 'Overall' }, { id: 'm', type: 'multiple_choice', label: 'Best part', options: ['Worship', 'Talks'] }, { id: 't', type: 'text', label: 'Comments' }, { id: 's', type: 'statement', label: 'Thanks' }, ]; const answers = [ { fieldId: 'r', value: '5' }, { fieldId: 'r', value: '4' }, { fieldId: 'r', value: '4' }, { fieldId: 'm', value: 'Talks' }, { fieldId: 'm', value: 'Talks' }, { fieldId: 'm', value: 'Worship' }, { fieldId: 't', value: 'Loved it', name: 'Ann' }, ]; test('averages ratings with a star distribution', () => { const [r] = summarise(fields, answers); expect(r.average).toBe(4.3); expect(r.distribution.map(d => d.value)).toEqual([0, 0, 0, 2, 1]); }); test('counts choices and lists text answers; skips statements', () => { const out = summarise(fields, answers); expect(out.length).toBe(3); expect(out[1].counts).toEqual([{ label: 'Worship', value: 1 }, { label: 'Talks', value: 2 }]); expect(out[2].texts).toEqual([{ value: 'Loved it', name: 'Ann' }]); }); });