@coorpacademy/progression-engine
Version:
471 lines • 10.9 kB
JavaScript
import _concat from "lodash/fp/concat";
import _filter from "lodash/fp/filter";
import test from 'ava';
import { getConfig } from '../../config';
import { computeNextStep, nextSlidePool, computeNextStepForNewChapter, prepareStateToSwitchChapters, computeNextStepForReview } from '../compute-next-step';
import allSlides from './fixtures/slides';
import { stateBeforeGettingNextContent, lastStepProgressionState } from './fixtures/states';
const config = getConfig({
ref: 'learner',
version: '1'
});
const availableContent = [{
ref: '1.A1',
slides: _filter({
chapter_id: '1.A1'
}, allSlides),
rules: null
}];
test('should return a success ExitNode if there is no available content with one chapter', t => {
const availableContenta = [{
ref: '1.A1',
slides: _concat(_filter({
_id: '1.A1.1'
}, allSlides), _filter({
_id: '1.A1.2'
}, allSlides)),
rules: null
}];
const state = Object.freeze(stateBeforeGettingNextContent);
// $FlowFixMe
const action = computeNextStep(config, state, availableContenta, {
type: 'answer',
payload: {
answer: [],
content: {
ref: '1.A1.2',
type: 'slide'
},
godMode: false,
isCorrect: true
}
});
// $FlowFixMe
t.deepEqual(action.nextContent, {
type: 'success',
ref: 'successExitNode'
});
});
test('should return a success ExitNode if there is no available content with any empty chapters', t => {
const availableContenta = [{
ref: '1.A1',
slides: _concat(_filter({
_id: '1.A1.1'
}, allSlides), _filter({
_id: '1.A1.2'
}, allSlides)),
rules: null
}, {
ref: '2.A1',
slides: [],
rules: null
}, {
ref: '3.A1',
slides: [],
rules: null
}];
const state = Object.freeze(stateBeforeGettingNextContent);
// $FlowFixMe
const action = computeNextStep(config, state, availableContenta, {
type: 'answer',
payload: {
answer: [],
content: {
ref: '1.A1.2',
type: 'slide'
},
godMode: false,
isCorrect: true
}
});
// $FlowFixMe
t.deepEqual(action.nextContent, {
type: 'success',
ref: 'successExitNode'
});
});
test('should return next slide if there is no available content in current chapter', t => {
const availableContenta = [{
ref: '1.A1',
slides: _concat(_filter({
_id: '1.A1.1'
}, allSlides), _filter({
_id: '1.A1.2'
}, allSlides)),
rules: null
}, {
ref: '2.A1',
slides: _filter({
chapter_id: '2.A1'
}, allSlides),
rules: null
}];
const state = Object.freeze(stateBeforeGettingNextContent);
// $FlowFixMe
const action = computeNextStep(config, state, availableContenta, {
type: 'answer',
payload: {
answer: [],
content: {
ref: '1.A1.2',
type: 'slide'
},
godMode: false,
isCorrect: true
}
});
// $FlowFixMe
t.regex(action.nextContent.ref, /^2\.A1\.[1-9]+$/);
});
test('should return next slide if there is available content in current chapter', t => {
const availableContentbb = [{
ref: '1.A1',
slides: [..._filter({
_id: '1.A1.1'
}, allSlides), ..._filter({
_id: '1.A1.2'
}, allSlides), ..._filter({
_id: '1.A1.3'
}, allSlides), ..._filter({
_id: '1.A1.4'
}, allSlides)],
rules: null
}];
const state = Object.freeze(lastStepProgressionState);
// $FlowFixMe
const action = computeNextStep(config, state, availableContentbb, {
type: 'answer',
payload: {
answer: [],
content: {
ref: '1.A1.3',
type: 'slide'
},
godMode: false,
isCorrect: true
}
});
// $FlowFixMe
t.is(action.nextContent.ref, '1.A1.4');
});
test('should return null if there is no available content', t => {
const state = Object.freeze(stateBeforeGettingNextContent);
// $FlowFixMe
const action = computeNextStep(config, state, availableContent, {
type: 'foo'
});
// $FlowFixMe
t.regex(action.nextContent.ref, /^1\.A1\.[1-9]+$/);
});
test('nextSlidePool --> should handle next chapter when no present within availableContent', t => {
const state = Object.freeze({
nextContent: {
ref: '1.A2',
type: 'chapter'
},
lives: 1,
livesDisabled: false,
stars: 0,
slides: [],
requestedClues: [],
viewedResources: [],
step: {
current: 0
},
isCorrect: null,
remainingLifeRequests: 1,
hasViewedAResourceAtThisStep: false,
allAnswers: [],
variables: {},
pendingSlides: []
});
const result = nextSlidePool(config, availableContent, state);
t.deepEqual(result, {
currentChapterContent: null,
nextChapterContent: null,
temporaryNextContent: {
type: 'slide',
ref: ''
}
});
});
test('prepareStateToSwitchChapters --> should return null when state is null', t => {
// $FlowFixMe
t.is(prepareStateToSwitchChapters({}, null), null);
});
test('computeNextStepForNewChapter --> should return null when computeNextStep returns null', t => {
const state = Object.freeze({
nextContent: {
ref: '1.A2',
type: 'chapter'
},
lives: 1,
livesDisabled: false,
stars: 0,
slides: [],
requestedClues: [],
viewedResources: [],
step: {
current: 0
},
isCorrect: null,
remainingLifeRequests: 1,
hasViewedAResourceAtThisStep: false,
allAnswers: [],
variables: {},
pendingSlides: []
});
const chapterRule = {
source: {
type: 'slide',
ref: '1.A1.7'
},
destination: {
type: 'chapter',
ref: '2.A1'
},
instructions: [{
field: 'bar',
type: 'set',
value: 'I was in 1.A1.7'
}],
conditions: [],
priority: 10
};
const result = computeNextStepForNewChapter(config, state, chapterRule, false, availableContent);
t.is(result, null);
});
test('computeNextStepForReview --> should return the next slide for an init state', t => {
const _config = getConfig({
ref: 'review',
version: '1'
});
const _availableContent = [{
ref: 'skill_ref',
slides: [allSlides[3],
// _id: '1.A1.5'
allSlides[4] // _id: '1.A1.4'
],
rules: null
}];
const result = computeNextStepForReview(_config, null, _availableContent, null);
t.deepEqual(result, {
nextContent: {
type: 'slide',
ref: '1.A1.5'
},
instructions: null,
isCorrect: false
});
});
test('computeNextStepForReview --> should return the next slide', t => {
const state = Object.freeze({
livesDisabled: true,
isCorrect: false,
slides: ['1.A1.1', '1.A1.2'],
lives: 0,
step: {
current: 3
},
stars: 0,
requestedClues: [],
viewedResources: [],
remainingLifeRequests: 0,
hasViewedAResourceAtThisStep: false,
content: {
ref: '1.A1.2',
type: 'slide'
},
nextContent: {
ref: '1.A1.3',
type: 'slide'
},
allAnswers: [{
slideRef: '1.A1.1',
isCorrect: true,
answer: ['d']
}, {
slideRef: '1.A1.2',
isCorrect: true,
answer: ['d']
}],
pendingSlides: [],
variables: {}
});
const _config = getConfig({
ref: 'review',
version: '1'
});
const _availableContent = [{
ref: 'skill_ref',
slides: [allSlides[3],
// _id: '1.A1.5'
allSlides[4] // _id: '1.A1.4'
],
rules: null
}];
const result = computeNextStepForReview(_config, state, _availableContent, {
type: 'answer',
payload: {
answer: ['answer'],
content: {
ref: '1.A1.3',
type: 'slide'
},
godMode: false,
isCorrect: false
}
});
t.deepEqual(result, {
nextContent: {
type: 'slide',
ref: '1.A1.5'
},
instructions: null,
isCorrect: false
});
});
test('computeNextStepForReview --> should return the same slide if you fail the last question', t => {
const state = Object.freeze({
livesDisabled: true,
isCorrect: true,
slides: ['1.A1.1', '1.A1.2', '1.A1.3', '1.A1.4'],
lives: 0,
step: {
current: 5
},
stars: 32,
requestedClues: [],
viewedResources: [],
remainingLifeRequests: 0,
hasViewedAResourceAtThisStep: false,
content: {
ref: '1.A1.4',
type: 'slide'
},
nextContent: {
ref: '1.A1.5',
type: 'slide'
},
allAnswers: [{
slideRef: '1.A1.1',
isCorrect: true,
answer: ['d']
}, {
slideRef: '1.A1.2',
isCorrect: true,
answer: ['d']
}, {
slideRef: '1.A1.3',
isCorrect: true,
answer: ['d']
}, {
slideRef: '1.A1.4',
isCorrect: true,
answer: ['d']
}],
pendingSlides: [],
variables: {}
});
const _config = getConfig({
ref: 'review',
version: '1'
});
const _availableContent = [{
ref: 'skill_ref',
slides: [],
rules: null
}];
const result = computeNextStepForReview(_config, state, _availableContent, {
type: 'answer',
payload: {
answer: ['answer'],
content: {
ref: '1.A1.5',
type: 'slide'
},
godMode: false,
isCorrect: false
}
});
t.deepEqual(result, {
nextContent: {
type: 'slide',
ref: '1.A1.5'
},
instructions: null,
isCorrect: false
});
});
test('computeNextStepForReview --> should avoid to return the already answered slide if there are lag on review lambda and available content are not updated', t => {
const state = Object.freeze({
livesDisabled: true,
isCorrect: false,
slides: ['1.A1.1', '1.A1.2'],
lives: 0,
step: {
current: 3
},
stars: 0,
requestedClues: [],
viewedResources: [],
remainingLifeRequests: 0,
hasViewedAResourceAtThisStep: false,
content: {
ref: '1.A1.2',
type: 'slide'
},
nextContent: {
ref: '1.A1.3',
type: 'slide'
},
allAnswers: [{
slideRef: '1.A1.1',
isCorrect: true,
answer: ['d']
}, {
slideRef: '1.A1.2',
isCorrect: true,
answer: ['d']
}],
pendingSlides: [],
variables: {}
});
const _config = getConfig({
ref: 'review',
version: '1'
});
const _availableContent = [{
ref: 'skill_ref',
slides: [allSlides[1],
// _id: '1.A1.2'
allSlides[2],
// _id: '1.A1.3'
allSlides[3],
// _id: '1.A1.5'
allSlides[4] // _id: '1.A1.4'
],
rules: null
}];
const result = computeNextStepForReview(_config, state, _availableContent, {
type: 'answer',
payload: {
answer: ['answer'],
content: {
ref: '1.A1.3',
type: 'slide'
},
godMode: false,
isCorrect: false
}
});
t.deepEqual(result, {
nextContent: {
type: 'slide',
ref: '1.A1.5'
},
instructions: null,
isCorrect: false
});
});
//# sourceMappingURL=compute-next-step.js.map