UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

261 lines (208 loc) • 9.23 kB
import {v4 as uuid} from 'uuid' import {afterEach, describe, expect, it, vi} from 'vitest' import update from 'immutability-helper' import range from 'lodash/range' import React from 'react' import {render} from '@testing-library/react' import { CategorizationPresenterProvider, useCategorizationPresenter, } from '../CategorizationPresenter' const uuids = range(10).map(() => uuid()) const changeItemStateStub = vi.fn() const categories = { [uuids[0]]: {id: uuids[0], itemBody: 'Planet'}, [uuids[1]]: {id: uuids[1], itemBody: 'Moon'}, } const distractors = { [uuids[2]]: {id: uuids[2], itemBody: 'Mars'}, [uuids[3]]: {id: uuids[3], itemBody: 'Europa'}, [uuids[4]]: {id: uuids[4], itemBody: 'Venus'}, [uuids[5]]: {id: uuids[5], itemBody: 'Phobos'}, [uuids[6]]: {id: uuids[6], itemBody: 'Deimos'}, [uuids[7]]: {id: uuids[7], itemBody: 'Jupiter'}, [uuids[8]]: {id: uuids[8], itemBody: 'America'}, [uuids[9]]: {id: uuids[9], itemBody: 'Asia'}, } const categoryOrder = [uuids[0], uuids[1]] const defaultPresenterProps = { itemBody: 'Match the name of the celestial body on the left with its correct classification:', interactionData: {categoryOrder, categories, distractors}, scoringData: { value: [ { id: uuids[0], scoringAlgorithm: 'AllOrNothing', scoringData: { value: [uuids[2], uuids[4], uuids[7]], }, }, { id: uuids[1], scoringAlgorithm: 'AllOrNothing', scoringData: { value: [uuids[3], uuids[5], uuids[6]], }, }, ], }, changeItemState: changeItemStateStub, newId: uuid, } describe('Categorization Presenter', () => { function TestConsumer({presenterRef}) { // eslint-disable-next-line no-param-reassign presenterRef.current = useCategorizationPresenter() return null } const makePresenter = (propOverrides = {}) => { const props = Object.assign({}, defaultPresenterProps, propOverrides) const presenterRef = {current: null} render( <CategorizationPresenterProvider {...props}> <TestConsumer presenterRef={presenterRef} /> </CategorizationPresenterProvider>, ) return presenterRef.current } describe('actions', () => { const presenter = makePresenter() afterEach(() => { changeItemStateStub.mockReset() }) describe('onCategoryInputChange', () => { it('calls changes the itemBody of the correct category', () => { presenter.onCategoryInputChange(uuids[1], {target: {value: 'new val'}}) const newChoice = changeItemStateStub.mock.calls[0][0].interactionData.categories[uuids[1]] expect(newChoice.itemBody).toBe('new val') }) }) describe('onCreateCategory', () => { it('adds a new category to interactionData', () => { presenter.onCreateCategory() const newState = changeItemStateStub.mock.calls[0][0] const newLength = Object.values(newState.interactionData.categories).length const oldLength = Object.values(defaultPresenterProps.interactionData.categories).length expect(newLength - oldLength).toBe(1) }) it('adds the new categoryId to the end of the categoryOrder array', () => { presenter.onCreateCategory() const newState = changeItemStateStub.mock.calls[0][0] const oldLength = defaultPresenterProps.interactionData.categoryOrder.length const newLength = newState.interactionData.categoryOrder.length expect(newLength - oldLength).toBe(1) }) it('adds a new category to scoringData', () => { presenter.onCreateCategory() const newState = changeItemStateStub.mock.calls[0][0] const newLength = Object.values(newState.scoringData.value).length const oldLength = Object.values(defaultPresenterProps.scoringData.value).length expect(newLength - oldLength).toBe(1) }) it('creates new categoryOrder if none existed', () => { makePresenter({ interactionData: {categories, distractors}, }).onCreateCategory() const newState = changeItemStateStub.mock.calls[0][0] expect(newState.interactionData.categoryOrder).toHaveLength(3) }) }) describe('onRemoveCategory', () => { it('removes the corresponding category from the interactionData', () => { presenter.onRemoveCategory(uuids[1]) const newState = changeItemStateStub.mock.calls[0][0] const removedItem = newState.interactionData.categories[uuids[1]] expect(removedItem).toBeUndefined() }) it('removes the corresponding categoryId from the categoryOrder', () => { presenter.onRemoveCategory(uuids[1]) const newState = changeItemStateStub.mock.calls[0][0] const removedCategoryPosition = newState.interactionData.categoryOrder.indexOf(uuids[1]) expect(removedCategoryPosition).toBe(-1) }) it('removes the corresponding category from the scoringData', () => { presenter.onRemoveCategory(uuids[1]) const newState = changeItemStateStub.mock.calls[0][0] const newScoringDataArray = newState.scoringData.value.filter(item => item.id === uuids[1]) expect(newScoringDataArray).toHaveLength(0) }) it('removes the corresponding category answers from the interactionData distractor', () => { presenter.onRemoveCategory(uuids[0]) const newState = changeItemStateStub.mock.calls[0][0] const oldCategoryDistractors = defaultPresenterProps.scoringData.value[0].scoringData.value oldCategoryDistractors.forEach(item => { const removedDistractor = newState.interactionData.distractors[item] expect(removedDistractor).toBeUndefined() }) }) it('creates new categoryOrder if none existed', () => { makePresenter({ interactionData: {categories, distractors}, }).onRemoveCategory(uuids[0]) const newState = changeItemStateStub.mock.calls[0][0] expect(newState.interactionData.categoryOrder).toHaveLength(1) }) }) describe('onCreateAnswer', () => { it('adds a new answer to interactionData distractors', () => { presenter.onCreateAnswer(uuids[1]) const newState = changeItemStateStub.mock.calls[0][0] const newLength = Object.values(newState.interactionData.distractors).length const oldLength = Object.values(defaultPresenterProps.interactionData.distractors).length expect(newLength - oldLength).toBe(1) }) it('adds a new answer to scoringData on respective category', () => { presenter.onCreateAnswer(uuids[0]) const newState = changeItemStateStub.mock.calls[0][0] const newLength = Object.values(newState.scoringData.value[0].scoringData.value).length const oldLength = Object.values( defaultPresenterProps.scoringData.value[0].scoringData.value, ).length expect(newLength - oldLength).toBe(1) }) it('adds a new answer if there are none and the value is null', () => { const scoringDataWithNullAnswerField = update(defaultPresenterProps.scoringData, { value: {0: {scoringData: {value: {$set: null}}}}, }) const presenterWithNullAnswerField = makePresenter({ scoringData: scoringDataWithNullAnswerField, }) presenterWithNullAnswerField.onCreateAnswer(uuids[0]) const newState = changeItemStateStub.mock.calls[0][0] const newAnswersList = Object.values(newState.scoringData.value[0].scoringData.value) expect(newAnswersList).toHaveLength(1) }) }) describe('onRemoveAnswer', () => { it('removes the corresponding answer from the interactionData distractors', () => { presenter.onRemoveAnswer(uuids[3], uuids[1]) const newState = changeItemStateStub.mock.calls[0][0] const removedItem = newState.interactionData.distractors[uuids[3]] expect(removedItem).toBeUndefined() }) it('removed the answer from scoringData on respective category', () => { presenter.onRemoveAnswer(uuids[3], uuids[1]) const newState = changeItemStateStub.mock.calls[0][0] const newScoringDataArray = newState.scoringData.value[0].scoringData.value.filter( item => item.id === uuids[3], ) expect(newScoringDataArray).toHaveLength(0) }) }) describe('onDistractorInputChange', () => { it('calls changes the itemBody of the correct distractor', () => { presenter.onDistractorInputChange(uuids[3], {target: {value: 'new val'}}) const newChoice = changeItemStateStub.mock.calls[0][0].interactionData.distractors[uuids[3]] expect(newChoice.itemBody).toBe('new val') }) }) describe('onRemoveDistractor', () => { it('removes the corresponding distractor from the interactionData', () => { presenter.onRemoveDistractor(uuids[3]) const newState = changeItemStateStub.mock.calls[0][0] const removedItem = newState.interactionData.distractors[uuids[3]] expect(removedItem).toBeUndefined() }) }) }) })