@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
318 lines (285 loc) • 9.41 kB
JavaScript
import React from 'react'
import {render} from '@testing-library/react'
import runAxeCheck from '@instructure/ui-axe-check'
import update from 'immutability-helper'
import omit from 'lodash/omit'
import words from 'lodash/words'
import difference from 'lodash/difference'
import RichFillBlankResult from '../index'
const defaultScoredData = {
value: {
fitb_uuid1: {
resultScore: 1,
userResponse: 'Christopfer',
correctAnswer: 'Christopher',
},
fitb_uuid2: {
resultScore: 1,
userResponse: '1492',
},
fitb_uuid3: {
value: {
Spain: {
resultScore: 1,
userResponded: false,
},
Espana: {
resultScore: 1,
userResponded: false,
},
'Kingdom of Spain': {
resultScore: 1,
userResponded: false,
},
Portugal: {
resultScore: 0,
userResponded: true,
},
},
},
fitb_uuid4: {
resultScore: 1,
userResponse: 'Asia',
correctAnswer: 'Europe',
},
fitb_uuid5: {
resultScore: 1,
correctAnswer: 'Atlantic',
userResponse: 'Atlantic Ocean',
},
fitb_uuid6: {
value: {
choice_uuid13_america: {
resultScore: 1,
userResponded: true,
},
},
},
fitb_uuid7: {
value: {
choice_uuid13_confused: {
resultScore: 1,
userResponded: false,
},
choice_uuid11_peaceful: {
resultScore: 0,
userResponded: true,
},
},
},
},
}
const props = {
itemBody: `<p><span id="blank_fitb_uuid1"></span> Columbus sailed in
<span id="blank_fitb_uuid2"></span> from the country of
<span id="blank_fitb_uuid3"></span> on the continent of
<span id="blank_fitb_uuid4"></span> across the
<span id="blank_fitb_uuid5"></span> and found...
<span id="blank_fitb_uuid6"></span>!!! Where the people were "
<span id="blank_fitb_uuid7"></span>"</p>`,
interactionData: {
blanks: [
{
id: 'fitb_uuid1',
answerType: 'openEntry',
},
{
id: 'fitb_uuid2',
answerType: 'openEntry',
},
{
id: 'fitb_uuid3',
answerType: 'openEntry',
},
{
id: 'fitb_uuid4',
answerType: 'openEntry',
},
{
id: 'fitb_uuid5',
answerType: 'openEntry',
},
{
id: 'fitb_uuid6',
choices: [
{id: 'choice_uuid11_brazil', position: 1, itemBody: 'Brazil'},
{id: 'choice_uuid12_austria', position: 2, itemBody: 'Austria'},
{id: 'choice_uuid13_america', position: 3, itemBody: 'America'},
],
answerType: 'wordbank',
},
{
id: 'fitb_uuid7',
choices: [
{id: 'choice_uuid11_peaceful', position: 1, itemBody: 'peaceful'},
{id: 'choice_uuid12_war-torn', position: 2, itemBody: 'war-torn'},
{id: 'choice_uuid13_confused', position: 3, itemBody: 'confused'},
],
answerType: 'dropdown',
},
],
},
scoredData: defaultScoredData,
}
// A dumb helper for stripping some keys from the complex scoredData above
const cloneWithoutKey = (obj, key) => {
let objCopy = obj.constructor()
let prop
for (prop in obj) {
if (obj.hasOwnProperty(prop) && prop !== key) {
if (typeof obj[prop] === 'object') {
objCopy[prop] = cloneWithoutKey(obj[prop], key)
} else {
objCopy[prop] = obj[prop]
}
}
}
return objCopy
}
describe('Rich Fill in the Blank Result', () => {
it('should render', () => {
const {container} = render(<RichFillBlankResult {...props} />)
expect(container.firstChild).toBeInTheDocument()
})
describe('blanks', () => {
it('renders text content for each blank item', () => {
const {container} = render(<RichFillBlankResult {...props} />)
const showingText = container.textContent
expect(showingText).toContain('Columbus sailed in')
expect(showingText).toContain('and found')
})
describe('blanks', () => {
it('renders a Feedback wrapper for each blank item', () => {
const {container} = render(<RichFillBlankResult {...props} />)
// FeedbackWrapper renders elements with data-automation attributes for status
// Each blank renders a user response text within the component
// With 7 blanks, we should see 7 user response areas
const blankElements = container.querySelectorAll('[id^="blank_"]')
expect(blankElements).toHaveLength(7)
})
it('renders the correct user response and correct answer for the first blank', () => {
const {container} = render(<RichFillBlankResult {...props} />)
// The first blank should show user response 'Christopfer'
expect(container.textContent).toContain('Christopfer')
// The correct answer label should be present
expect(container.textContent).toContain('Correct answer:')
})
})
})
describe('scoredData variants', () => {
it("renders if wordbank choice id is not in blank's choice list", () => {
const newVal = {
choice_uuid13_confused: {
resultScore: 1,
userResponded: false,
},
// changed to match other blank's choice
choice_uuid11_brazil: {
resultScore: 0,
userResponded: true,
},
}
const scoredDataWithWordbankChoice = update(defaultScoredData, {
value: {fitb_uuid7: {value: {$set: newVal}}},
})
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataWithWordbankChoice} />,
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders if wordbank choice id does not match option', () => {
const newVal = {
choice_uuid13_confused: {
resultScore: 1,
userResponded: false,
},
some_key_that_does_not_exist: {
resultScore: 0,
userResponded: true,
},
}
const scoredDataWithUnmatchedWordbankChoice = update(defaultScoredData, {
value: {fitb_uuid7: {value: {$set: newVal}}},
})
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataWithUnmatchedWordbankChoice} />,
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders if wordbank was unanswered', () => {
const newVal = {
choice_uuid13_confused: {
resultScore: 1,
userResponded: false,
},
}
const scoredDataWithNoWordbankChoice = update(defaultScoredData, {
value: {fitb_uuid7: {value: {$set: newVal}}},
})
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataWithNoWordbankChoice} />,
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders if value is completely missing', () => {
const scoredDataWithNonResponse = update(defaultScoredData, {
value: {$apply: val => omit(val, ['fitb_uuid3'])},
})
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataWithNonResponse} />,
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders with missing scores', () => {
const scoredDataMissingScores = cloneWithoutKey(defaultScoredData, 'resultScore')
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataMissingScores} />,
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders with no user responses', () => {
const scoredDataMissingScores = cloneWithoutKey(defaultScoredData, 'userResponse')
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataMissingScores} />,
)
expect(container.firstChild).toBeInTheDocument()
})
})
describe('results display', () => {
it('renders the correctAnswer if given and incorrect', () => {
const withAtlanticWrong = update(defaultScoredData, {
value: {fitb_uuid5: {resultScore: {$set: 0}}},
})
const {container} = render(<RichFillBlankResult {...props} scoredData={withAtlanticWrong} />)
const innerTextWords = words(container.textContent)
const expectedWords = words('Correct Answer:Atlantic')
expect(difference(expectedWords, innerTextWords).length).toBe(0)
})
it('renders correctAnswer if multiple choice blank was unanswered', () => {
const newVal = {
choice_uuid13_confused: {
resultScore: 1,
userResponded: false,
},
}
const scoredDataWithNoWordbankChoice = update(defaultScoredData, {
value: {fitb_uuid7: {value: {$set: newVal}}},
})
const {container} = render(
<RichFillBlankResult {...props} scoredData={scoredDataWithNoWordbankChoice} />,
)
// The unanswered dropdown blank should show 'confused' as the correct answer
expect(container.textContent).toContain('confused')
})
})
describe('a11y tests', () => {
it('should meet a11y standards', async () => {
render(
<main>
<RichFillBlankResult {...props} />
</main>,
)
expect(await runAxeCheck(document.body)).toBe(true)
})
})
})