@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
370 lines (332 loc) • 11.8 kB
JavaScript
import {expect} from '@instructure/ui-test-utils'
import FillBlankInteractionType from '../fill_blank'
import checkUserResponseTransform from '../../__tests__/sharedRecordTests'
describe('FillBlankInteractionType', () => {
const FITBRecordType = FillBlankInteractionType
const intType = new FITBRecordType()
describe('#getDefaultInteractionData', () => {
it('returns one stemItem in stemItems when slug is "fill-blank" ', () => {
const stemItems = intType.getDefaultInteractionData().stemItems
expect(stemItems.length).to.be.equal(1)
expect(stemItems[0].type).to.be.equal('text')
expect(stemItems[0].value).to.be.equal('')
expect(stemItems[0].position).to.be.equal(1)
})
})
describe('#getDefaultScoringData', () => {
it('returns the correct data', () => {
expect(intType.getDefaultScoringData()).to.deep.eq({value: []})
})
})
describe('#getDefaultUserResponse', () => {
it('returns the correct data', () => {
expect(intType.getDefaultUserResponse()).to.deep.eq({value: []})
})
})
describe('#errors', () => {
it('is valid with good data', () => {
const validItemData = {
itemBody: 'some item body',
interactionData: {
blanks: [{id: '1', itemBody: 'something'}],
stemItems: [],
},
scoringData: {},
}
expect(intType.getErrors(validItemData)).to.be.empty()
})
it('is invalid without blanks', () => {
const dataWithoutBlanks = {
itemBody: 'some item body',
interactionData: {
blanks: [],
stemItems: [],
},
scoringData: {},
}
expect(intType.getErrors(dataWithoutBlanks).interactionData.blanks.$errors).to.be.eql([
'You must have a blank. e.g.: "Roses are `red`, violets are `blue`"',
])
})
it('is invalid with a single choice in a dropdown', () => {
const dataWithSingleBlank = {
itemBody: 'some item body',
interactionData: {
blanks: [
{
choices: [{itemBody: 'a single choice'}],
},
],
stemItems: [],
},
scoringData: {},
}
expect(
intType.getErrors(dataWithSingleBlank).interactionData.blanks[0].choices.$errors,
).to.be.eql(['You must have more than one choice'])
})
it('is invalid with a possible choice that is an empty string', () => {
const dataWithBodylessBlank = {
itemBody: 'some item body',
interactionData: {
blanks: [
{
choices: [{itemBody: 'a single choice'}, {itemBody: ''}],
},
],
stemItems: [],
},
scoringData: {},
}
expect(
intType.getErrors(dataWithBodylessBlank).interactionData.blanks[0].choices[1].itemBody[0],
).to.be.eql('Choice cannot be blank')
})
describe('Levenshtein Distance', () => {
const makeData = distanceVal => ({
itemBody: 'some item body',
interactionData: {
blanks: [{id: '1', itemBody: 'something'}],
stemItems: [],
},
scoringData: {
value: [
{
scoringAlgorithm: 'TextCloseEnough',
scoringData: {
blankText: 'correct answer',
editDistance: distanceVal,
},
},
],
},
})
it('is invalid with a non numeric editDistance', () => {
const nonNumericData = makeData('some non numeric value')
expect(
intType.getErrors(nonNumericData).scoringData.value[0].scoringData.editDistance[0],
).to.be.eql('Levenshtein Distance must be a number')
})
it('is invalid with a editDistance below 1', () => {
const zeroDistanceData = makeData('0')
expect(
intType.getErrors(zeroDistanceData).scoringData.value[0].scoringData.editDistance[0],
).to.be.eql('Levenshtein Distance must be greater than zero')
})
it('is invalid with a non-integer editDistance', () => {
const nonIntData = makeData('1.2')
expect(
intType.getErrors(nonIntData).scoringData.value[0].scoringData.editDistance[0],
).to.be.eql('Levenshtein Distance must be an integer')
})
it('is invalid with an editDistance greater than length of break content', () => {
const nonIntData = makeData('100')
expect(
intType.getErrors(nonIntData).scoringData.value[0].scoringData.editDistance[0],
).to.be.eql('Levenshtein Distance must be less than the length of the blank text')
})
})
it('is invalid without a correct choice for blank', () => {
const withoutCorrectChoice = {
itemBody: 'some item body',
interactionData: {
blanks: [{id: '1', itemBody: 'something'}],
stemItems: [],
},
scoringData: {
value: [
{
scoringAlgorithm: 'TextInChoices',
scoringData: {
blankText: 'correct answer',
value: [],
},
},
],
},
}
expect(
intType.getErrors(withoutCorrectChoice).scoringData.value[0].scoringData.value.$errors,
).to.be.eql(['You must have an answer'])
})
it('is invalid with an empty correct choice for blank', () => {
const withNullCorrectChoice = {
itemBody: 'some item body',
interactionData: {
blanks: [{id: '1', itemBody: 'something'}],
stemItems: [],
},
scoringData: {
value: [
{
scoringAlgorithm: 'TextEntryMatches',
scoringData: {
blankText: 'correct answer',
value: null,
},
},
],
},
}
expect(
intType.getErrors(withNullCorrectChoice).scoringData.value[0].scoringData.value.$errors[0],
).to.be.eql('Correct value cannot be blank')
})
it('is invalid with a specific correct choice that is an empty string', () => {
const withEmptyCorrectChoice = {
itemBody: 'some item body',
interactionData: {
blanks: [{id: '1', itemBody: 'something'}],
stemItems: [],
},
scoringData: {
value: [
{
scoringAlgorithm: 'TextInChoices',
scoringData: {
blankText: 'also valid',
value: ['valid', 'also valid', ''],
},
},
],
},
}
expect(
intType.getErrors(withEmptyCorrectChoice).scoringData.value[0].scoringData.value[2][0],
).to.be.eql('Correct value cannot be blank')
})
it('does not run TextInChoices rules for non-TextInChoices', () => {
const data = {
itemBody: 'some item body',
interactionData: {
blanks: [{id: '1', itemBody: 'something'}],
stemItems: [],
},
scoringData: {
value: [
{
scoringAlgorithm: 'TextCloseEnough',
scoringData: {
blankText: 'correct answer',
editDistance: 33,
value: '',
},
},
],
},
}
expect(intType.getErrors(data).scoringData.value[0].scoringData.value).to.eql({
$errors: ['Correct value cannot be blank'],
})
})
})
describe('#hasResponse', () => {
it('returns true when all blanks have a response', () => {
const resp = [
{id: 1, value: 'one'},
{id: 2, value: 'two'},
{id: 3, value: 'three'},
]
const intData = {blanks: [1, 2, 3]}
expect(intType.hasResponse(resp, intData)).to.be.true()
})
it('returns false when there is no response', () => {
const resp = []
const intData = {blanks: [1, 2, 3]}
expect(intType.hasResponse(resp, intData)).to.be.false()
})
it('returns false when only partially answered', () => {
const resp = [
{id: 1, value: 'one'},
{id: 2, value: 'two'},
]
const intData = {blanks: [1, 2, 3]}
expect(intType.hasResponse(resp, intData)).to.be.false()
})
it('returns false when previously answered, but deleted', () => {
const resp = [
{id: 1, value: ''},
{id: 2, value: ''},
{id: 3, value: ''},
]
const intData = {blanks: [1, 2, 3]}
expect(intType.hasResponse(resp, intData)).to.be.false()
})
})
const interactionData = {
stemItems: [
{id: 'stem_uuid-1', position: 1, type: 'text', value: ' '},
{id: 'stem_uuid0', position: 2, type: 'blank', blankId: 'fitb_uuid1'},
{id: 'stem_uuid1', position: 3, type: 'text', value: ' Columbus sailed in '},
{id: 'stem_uuid2', position: 4, type: 'blank', blankId: 'fitb_uuid2'},
{id: 'stem_uuid3', position: 5, type: 'text', value: ' from the country of '},
{id: 'stem_uuid4', position: 6, type: 'blank', blankId: 'fitb_uuid3'},
{id: 'stem_uuid5', position: 7, type: 'text', value: ' on the continent of '},
{id: 'stem_uuid6', position: 8, type: 'blank', blankId: 'fitb_uuid4'},
{id: 'stem_uuid7', position: 9, type: 'text', value: ' across the '},
{id: 'stem_uuid8', position: 10, type: 'blank', blankId: 'fitb_uuid5'},
{id: 'stem_uuid9', position: 11, type: 'text', value: ' and found '},
{id: 'stem_uuid10', position: 12, type: 'blank', blankId: 'fitb_uuid6'},
{id: 'stem_uuid11', position: 13, type: 'text', value: '.'},
{id: 'stem_uuid12', position: 14, type: 'text', value: ' Where the people were '},
{id: 'stem_uuid13', position: 15, type: 'blank', blankId: 'fitb_uuid7'},
{id: 'stem_uuid14', position: 16, type: 'text', value: '.'},
],
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',
},
],
}
const validResponse = [{id: 'fitb_uuid1', value: 'Christopher', type: 'Text'}]
const invalidResponse = [{id: 'uuid1', value: 'Christopher', type: 'Text'}]
const validResponse2 = [{id: 'fitb_uuid7', value: 'choice_uuid11_peaceful', type: 'Text'}]
const invalidResponse2 = [{id: 'fitb_uuid7', value: 'choice_uuid11_peaceful_not', type: 'Text'}]
checkUserResponseTransform(
new FITBRecordType(),
{response: validResponse, interactionData, expected: 'Blank 1: Christopher'},
{response: invalidResponse, interactionData, expected: 'Blank 0: '},
out => out.props.children,
)
checkUserResponseTransform(
new FITBRecordType(),
{response: validResponse2, interactionData, expected: 'Blank 7: peaceful'},
{response: invalidResponse2, interactionData, expected: 'Blank 7: '},
out => out.props.children,
)
})