UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

199 lines (176 loc) • 6.77 kB
import React from 'react' import {render, screen} from '@testing-library/react' import runAxeCheck from '@instructure/ui-axe-check' import reduce from 'lodash/fp/reduce' import zipAll from 'lodash/fp/zipAll' import {combination, baseNCombination, cartesianProduct} from '../../../../util/combinatorics' import MultipleAnswerResult from '../index' const props = { itemBody: 'Who was in the first cabinet of the USA?', interactionData: { choices: [ {id: 'uuid1', position: 1, itemBody: 'Thomas Jefferson'}, {id: 'uuid2', position: 2, itemBody: 'John Marshall'}, {id: 'uuid3', position: 3, itemBody: 'John Knox'}, {id: 'uuid4', position: 4, itemBody: 'Alexander Hamilton'}, {id: 'uuid5', position: 5, itemBody: 'Aaron Burr'}, {id: 'uuid6', position: 6, itemBody: 'Ben Franklin'}, ], }, scoredData: { correct: false, value: { uuid1: {resultScore: 1, userResponded: true}, uuid3: {resultScore: 1, userResponded: false}, uuid4: {resultScore: 1, userResponded: false}, }, }, } // generate all possible scoredData tuples // with all combinations of userResponded (true or false or undefined) and resultScore (1 or 0 or undefined) function generateScoredDataTuples(choiceIds) { return cartesianProduct( combination([0, 1, void 0], choiceIds.length), baseNCombination([true, false, void 0], choiceIds.length), ).map(tuple => zipAll([choiceIds, ...tuple])) } describe('Multiple Answer Result', () => { it('should render', () => { const {container} = render(<MultipleAnswerResult {...props} />) expect(container.firstChild).toBeInTheDocument() }) // TODO: this is really hard to follow. Simplify? generateScoredDataTuples(['uuid1', 'uuid2', 'uuid3']).forEach(tuples => { describe(`for scoredData tuple ${JSON.stringify(tuples)}`, () => { const scoredData = {} tuples.forEach(([id, resultScore, userResponded]) => { scoredData[id] = {resultScore, userResponded} }) it('should render checkboxes properly', () => { const {container} = render( <MultipleAnswerResult {...props} scoredData={{ value: scoredData, }} />, ) const checkboxes = container.querySelectorAll('input[type="checkbox"]') tuples.forEach(([_, __, userResponded], index) => { expect(checkboxes[index].checked).toBe(!!userResponded) }) }) // Count the number of items in these tuples that have enough data to present a FeedbackWrapper // if it isn't listed correct/incorrect or as a user response, it will be rendered plainly let feedbackNumber = reduce( (result, [_, resultScore, userResponded]) => { if (typeof resultScore !== 'undefined' && typeof userResponded !== 'undefined') { return result + 1 } return result }, 0, tuples, ) if (tuples.every(([_, __, userResponded]) => !userResponded)) { feedbackNumber += 1 // the "no answer" message } it(`should render ${feedbackNumber} FeedbackWrappers`, () => { const {container} = render( <MultipleAnswerResult {...props} scoredData={{ value: scoredData, }} />, ) // FeedbackWrapper renders with className="fs-mask" const feedbackWrappers = container.querySelectorAll('.fs-mask') expect(feedbackWrappers).toHaveLength(feedbackNumber) }) }) }) describe('answer feedback', () => { it('displays answer feedback for selected choices', () => { const propsWithFeedback = { ...props, MultipleAnswerFeedback: { uuid1: '<p>Great choice! This is correct.</p>', uuid2: '<p>Not quite right.</p>', uuid3: '<p>Good job!</p>', }, scoredData: { correct: false, value: { uuid1: {resultScore: 1, userResponded: true}, uuid2: {resultScore: 0, userResponded: true}, uuid3: {resultScore: 1, userResponded: false}, }, }, } const {container} = render(<MultipleAnswerResult {...propsWithFeedback} />) const feedbackDisplays = container.querySelectorAll( '[data-automation="sdk-answer-feedback-display"]', ) // Should show feedback for uuid1 and uuid2 (selected), but not uuid3 (not selected) expect(feedbackDisplays.length).toBe(2) expect(screen.getByText('Great choice! This is correct.')).toBeInTheDocument() expect(screen.getByText('Not quite right.')).toBeInTheDocument() expect(screen.queryByText('Good job!')).toBeNull() }) it('does not display feedback when no feedback is provided', () => { const {container} = render(<MultipleAnswerResult {...props} MultipleAnswerFeedback={{}} />) const feedbackDisplays = container.querySelectorAll( '[data-automation="sdk-answer-feedback-display"]', ) expect(feedbackDisplays.length).toBe(0) }) it('does not display feedback for unselected choices', () => { const propsWithFeedback = { ...props, MultipleAnswerFeedback: { uuid1: '<p>Feedback for uuid1</p>', uuid2: '<p>Feedback for uuid2</p>', }, scoredData: { correct: false, value: { uuid1: {resultScore: 1, userResponded: false}, uuid2: {resultScore: 0, userResponded: false}, }, }, } const {container} = render(<MultipleAnswerResult {...propsWithFeedback} />) const feedbackDisplays = container.querySelectorAll( '[data-automation="sdk-answer-feedback-display"]', ) // No feedback should be displayed since no choices were selected expect(feedbackDisplays.length).toBe(0) }) it('displays "Feedback" heading for each answer feedback', () => { const propsWithFeedback = { ...props, MultipleAnswerFeedback: { uuid1: '<p>Great choice!</p>', }, scoredData: { correct: true, value: { uuid1: {resultScore: 1, userResponded: true}, }, }, } render(<MultipleAnswerResult {...propsWithFeedback} />) // Should display the "Feedback" heading const feedbackHeadings = screen.getAllByText('Feedback') expect(feedbackHeadings.length).toBeGreaterThan(0) }) }) describe('a11y tests', () => { it('should meet a11y standards', async () => { const {container} = render(<MultipleAnswerResult {...props} />) const result = await runAxeCheck(container) expect(result).toBe(true) }) }) })