@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
261 lines (208 loc) • 9.23 kB
JavaScript
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()
})
})
})
})