feat: unselect multiple choices type multi->single (#181)
This commit is contained in:
@@ -16,9 +16,9 @@ export const AnswersContainer = ({
|
||||
// Redux
|
||||
answers,
|
||||
addAnswer,
|
||||
updateField,
|
||||
}) => {
|
||||
const { hasSingleAnswer } = initializeAnswerContainer(problemType);
|
||||
|
||||
const { hasSingleAnswer } = initializeAnswerContainer({ answers, problemType, updateField });
|
||||
return (
|
||||
<div>
|
||||
{answers.map((answer) => (
|
||||
@@ -44,6 +44,7 @@ AnswersContainer.propTypes = {
|
||||
problemType: PropTypes.string.isRequired,
|
||||
answers: PropTypes.arrayOf(answerOptionProps).isRequired,
|
||||
addAnswer: PropTypes.func.isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
@@ -52,6 +53,7 @@ export const mapStateToProps = (state) => ({
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
addAnswer: actions.problem.addAnswer,
|
||||
updateField: actions.problem.updateField,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AnswersContainer);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { actions, selectors } from '../../../../../data/redux';
|
||||
|
||||
import * as module from './AnswersContainer';
|
||||
|
||||
jest.mock('../../../../../data/redux', () => ({
|
||||
actions: {
|
||||
problem: {
|
||||
updateField: jest.fn().mockName('actions.problem.updateField'),
|
||||
addAnswer: jest.fn().mockName('actions.problem.addAnswer'),
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
problem: {
|
||||
answers: jest.fn(state => ({ answers: state })),
|
||||
},
|
||||
},
|
||||
}));
|
||||
describe('AnswersContainer', () => {
|
||||
const props = {
|
||||
answers: [],
|
||||
updateField: jest.fn(),
|
||||
addAnswer: jest.fn(),
|
||||
};
|
||||
describe('render', () => {
|
||||
test('snapshot: renders correct default', () => {
|
||||
expect(shallow(<module.AnswersContainer {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
test('snapshot: renders correctly with answers', () => {
|
||||
expect(shallow(<module.AnswersContainer answers={[{ id: 'a', title: 'sOMetITlE', correct: true }, { id: 'b', title: 'sOMetITlE', correct: true }]} {...props} />)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { A: 'pple', B: 'anana', C: 'ucumber' };
|
||||
test('answers from problem.answers', () => {
|
||||
expect(
|
||||
module.mapStateToProps(testState).answers,
|
||||
).toEqual(selectors.problem.answers(testState));
|
||||
});
|
||||
});
|
||||
describe('mapDispatchToProps', () => {
|
||||
test('updateField from actions.problem.updateField', () => {
|
||||
expect(module.mapDispatchToProps.updateField).toEqual(actions.problem.updateField);
|
||||
});
|
||||
test('updateField from actions.problem.addAnswer', () => {
|
||||
expect(module.mapDispatchToProps.addAnswer).toEqual(actions.problem.addAnswer);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AnswersContainer render snapshot: renders correct default 1`] = `
|
||||
<div>
|
||||
<Button
|
||||
className="my-3 ml-2"
|
||||
onClick={[MockFunction]}
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AnswersContainer render snapshot: renders correctly with answers 1`] = `
|
||||
<div>
|
||||
<Button
|
||||
className="my-3 ml-2"
|
||||
onClick={[MockFunction]}
|
||||
variant="tertiary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add answer"
|
||||
description="Button text to add answer"
|
||||
id="authoring.answerwidget.answer.addAnswerButton"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react';
|
||||
import _ from 'lodash-es';
|
||||
import * as module from './hooks';
|
||||
import messages from './messages';
|
||||
import { ShowAnswerTypesKeys } from '../../../../../data/constants/problem';
|
||||
import { ProblemTypeKeys, ShowAnswerTypesKeys } from '../../../../../data/constants/problem';
|
||||
|
||||
export const state = {
|
||||
showAdvanced: (val) => useState(val),
|
||||
@@ -182,11 +182,23 @@ export const timerCardHooks = (updateSettings) => ({
|
||||
},
|
||||
});
|
||||
|
||||
export const typeRowHooks = (typeKey, updateField) => {
|
||||
export const typeRowHooks = ({
|
||||
answers,
|
||||
correctAnswerCount,
|
||||
typeKey,
|
||||
updateField,
|
||||
updateAnswer,
|
||||
}) => {
|
||||
const onClick = () => {
|
||||
if (typeKey === ProblemTypeKeys.SINGLESELECT || typeKey === ProblemTypeKeys.DROPDOWN) {
|
||||
if (correctAnswerCount > 1) {
|
||||
answers.forEach(answer => {
|
||||
updateAnswer({ ...answer, correct: false });
|
||||
});
|
||||
}
|
||||
}
|
||||
updateField({ problemType: typeKey });
|
||||
};
|
||||
|
||||
return {
|
||||
onClick,
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ jest.mock('../../../../../data/redux', () => ({
|
||||
problem: {
|
||||
updateSettings: (args) => ({ updateSettings: args }),
|
||||
updateField: (args) => ({ updateField: args }),
|
||||
updateAnswer: (args) => ({ updateAnswer: args }),
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -217,10 +218,19 @@ describe('Problem settings hooks', () => {
|
||||
|
||||
describe('Type row hooks', () => {
|
||||
test('test onClick', () => {
|
||||
const typekey = 'TEXTINPUT';
|
||||
const typekey = 'multiplechoiceresponse';
|
||||
const updateField = jest.fn();
|
||||
output = hooks.typeRowHooks(typekey, updateField);
|
||||
const updateAnswer = jest.fn();
|
||||
const answers = [{ correct: true, id: 'a' }, { correct: true, id: 'b' }];
|
||||
output = hooks.typeRowHooks({
|
||||
answers,
|
||||
correctAnswerCount: 2,
|
||||
typeKey: typekey,
|
||||
updateField,
|
||||
updateAnswer,
|
||||
});
|
||||
output.onClick();
|
||||
expect(updateAnswer).toHaveBeenCalledWith({ ...answers[1], correct: false });
|
||||
expect(updateField).toHaveBeenCalledWith({ problemType: typekey });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,9 +23,12 @@ import './index.scss';
|
||||
export const SettingsWidget = ({
|
||||
problemType,
|
||||
// redux
|
||||
answers,
|
||||
correctAnswerCount,
|
||||
settings,
|
||||
updateSettings,
|
||||
updateField,
|
||||
updateAnswer,
|
||||
}) => {
|
||||
const { isAdvancedCardsVisible, showAdvancedCards } = showAdvancedSettingsCards();
|
||||
return (
|
||||
@@ -38,7 +41,13 @@ export const SettingsWidget = ({
|
||||
<Row>
|
||||
<Col>
|
||||
<Row className="my-2">
|
||||
<TypeCard problemType={problemType} updateField={updateField} />
|
||||
<TypeCard
|
||||
answers={answers}
|
||||
correctAnswerCount={correctAnswerCount}
|
||||
problemType={problemType}
|
||||
updateField={updateField}
|
||||
updateAnswer={updateAnswer}
|
||||
/>
|
||||
</Row>
|
||||
<Row className="my-2">
|
||||
<ScoringCard scoring={settings.scoring} updateSettings={updateSettings} />
|
||||
@@ -91,7 +100,16 @@ export const SettingsWidget = ({
|
||||
};
|
||||
|
||||
SettingsWidget.propTypes = {
|
||||
answers: PropTypes.arrayOf(PropTypes.shape({
|
||||
correct: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
selectedFeedback: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
unselectedFeedback: PropTypes.string,
|
||||
})).isRequired,
|
||||
correctAnswerCount: PropTypes.number.isRequired,
|
||||
problemType: PropTypes.string.isRequired,
|
||||
updateAnswer: PropTypes.func.isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
updateSettings: PropTypes.func.isRequired,
|
||||
// eslint-disable-next-line
|
||||
@@ -100,11 +118,14 @@ SettingsWidget.propTypes = {
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
settings: selectors.problem.settings(state),
|
||||
answers: selectors.problem.answers(state),
|
||||
correctAnswerCount: selectors.problem.correctAnswerCount(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
updateSettings: actions.problem.updateSettings,
|
||||
updateField: actions.problem.updateField,
|
||||
updateAnswer: actions.problem.updateAnswer,
|
||||
};
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SettingsWidget));
|
||||
|
||||
@@ -7,8 +7,11 @@ import messages from '../messages';
|
||||
import TypeRow from './TypeRow';
|
||||
|
||||
export const TypeCard = ({
|
||||
answers,
|
||||
correctAnswerCount,
|
||||
problemType,
|
||||
updateField,
|
||||
updateAnswer,
|
||||
// inject
|
||||
intl,
|
||||
}) => {
|
||||
@@ -21,12 +24,15 @@ export const TypeCard = ({
|
||||
>
|
||||
{problemTypeKeysArray.map((typeKey, i) => (
|
||||
<TypeRow
|
||||
answers={answers}
|
||||
correctAnswerCount={correctAnswerCount}
|
||||
key={typeKey}
|
||||
typeKey={typeKey}
|
||||
label={ProblemTypes[typeKey].title}
|
||||
selected={typeKey !== problemType}
|
||||
lastRow={(i + 1) === problemTypeKeysArray.length}
|
||||
updateField={updateField}
|
||||
updateAnswer={updateAnswer}
|
||||
/>
|
||||
))}
|
||||
</SettingsOption>
|
||||
@@ -34,9 +40,19 @@ export const TypeCard = ({
|
||||
};
|
||||
|
||||
TypeCard.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
answers: PropTypes.arrayOf(PropTypes.shape({
|
||||
correct: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
selectedFeedback: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
unselectedFeedback: PropTypes.string,
|
||||
})).isRequired,
|
||||
correctAnswerCount: PropTypes.number.isRequired,
|
||||
problemType: PropTypes.string.isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
updateAnswer: PropTypes.func.isRequired,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(TypeCard);
|
||||
|
||||
@@ -6,8 +6,12 @@ import { ProblemTypeKeys } from '../../../../../../data/constants/problem';
|
||||
|
||||
describe('TypeCard', () => {
|
||||
const props = {
|
||||
answers: [],
|
||||
correctAnswerCount: 0,
|
||||
problemType: ProblemTypeKeys.TEXTINPUT,
|
||||
updateField: jest.fn().mockName('args.updateField'),
|
||||
updateAnswer: jest.fn().mockName('args.updateAnswer'),
|
||||
// injected
|
||||
intl: { formatMessage },
|
||||
};
|
||||
|
||||
|
||||
@@ -5,13 +5,22 @@ import { Check } from '@edx/paragon/icons';
|
||||
import { typeRowHooks } from '../hooks';
|
||||
|
||||
export const TypeRow = ({
|
||||
answers,
|
||||
correctAnswerCount,
|
||||
typeKey,
|
||||
label,
|
||||
selected,
|
||||
lastRow,
|
||||
updateField,
|
||||
updateAnswer,
|
||||
}) => {
|
||||
const { onClick } = typeRowHooks(typeKey, updateField);
|
||||
const { onClick } = typeRowHooks({
|
||||
answers,
|
||||
correctAnswerCount,
|
||||
typeKey,
|
||||
updateField,
|
||||
updateAnswer,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -25,10 +34,19 @@ export const TypeRow = ({
|
||||
};
|
||||
|
||||
TypeRow.propTypes = {
|
||||
answers: PropTypes.arrayOf(PropTypes.shape({
|
||||
correct: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
selectedFeedback: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
unselectedFeedback: PropTypes.string,
|
||||
})).isRequired,
|
||||
correctAnswerCount: PropTypes.number.isRequired,
|
||||
typeKey: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
selected: PropTypes.bool.isRequired,
|
||||
lastRow: PropTypes.bool.isRequired,
|
||||
updateAnswer: PropTypes.func.isRequired,
|
||||
updateField: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -10,11 +10,14 @@ jest.mock('../hooks', () => ({
|
||||
describe('TypeRow', () => {
|
||||
const typeKey = 'TEXTINPUT';
|
||||
const props = {
|
||||
answers: [],
|
||||
correctAnswerCount: 0,
|
||||
typeKey,
|
||||
label: 'Text Input Problem',
|
||||
selected: true,
|
||||
lastRow: false,
|
||||
updateField: jest.fn().mockName('args.updateField'),
|
||||
updateAnswer: jest.fn().mockName('args.updateAnswer'),
|
||||
};
|
||||
|
||||
const typeRowHooksProps = {
|
||||
@@ -26,7 +29,13 @@ describe('TypeRow', () => {
|
||||
describe('behavior', () => {
|
||||
it(' calls typeRowHooks when initialized', () => {
|
||||
shallow(<TypeRow {...props} />);
|
||||
expect(typeRowHooks).toHaveBeenCalledWith(typeKey, props.updateField);
|
||||
expect(typeRowHooks).toHaveBeenCalledWith({
|
||||
answers: props.answers,
|
||||
correctAnswerCount: props.correctAnswerCount,
|
||||
typeKey,
|
||||
updateField: props.updateField,
|
||||
updateAnswer: props.updateAnswer,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -6,51 +6,69 @@ exports[`TypeCard snapshot snapshot: renders type setting card 1`] = `
|
||||
title="Type"
|
||||
>
|
||||
<TypeRow
|
||||
answers={Array []}
|
||||
correctAnswerCount={0}
|
||||
key="multiplechoiceresponse"
|
||||
label="Single Select Problem"
|
||||
lastRow={false}
|
||||
selected={true}
|
||||
typeKey="multiplechoiceresponse"
|
||||
updateAnswer={[MockFunction args.updateAnswer]}
|
||||
updateField={[MockFunction args.updateField]}
|
||||
/>
|
||||
<TypeRow
|
||||
answers={Array []}
|
||||
correctAnswerCount={0}
|
||||
key="choiceresponse"
|
||||
label="Multi Select Problem"
|
||||
lastRow={false}
|
||||
selected={true}
|
||||
typeKey="choiceresponse"
|
||||
updateAnswer={[MockFunction args.updateAnswer]}
|
||||
updateField={[MockFunction args.updateField]}
|
||||
/>
|
||||
<TypeRow
|
||||
answers={Array []}
|
||||
correctAnswerCount={0}
|
||||
key="optionresponse"
|
||||
label="Dropdown Problem"
|
||||
lastRow={false}
|
||||
selected={true}
|
||||
typeKey="optionresponse"
|
||||
updateAnswer={[MockFunction args.updateAnswer]}
|
||||
updateField={[MockFunction args.updateField]}
|
||||
/>
|
||||
<TypeRow
|
||||
answers={Array []}
|
||||
correctAnswerCount={0}
|
||||
key="numericalresponse"
|
||||
label="Numeric Response Problem"
|
||||
lastRow={false}
|
||||
selected={true}
|
||||
typeKey="numericalresponse"
|
||||
updateAnswer={[MockFunction args.updateAnswer]}
|
||||
updateField={[MockFunction args.updateField]}
|
||||
/>
|
||||
<TypeRow
|
||||
answers={Array []}
|
||||
correctAnswerCount={0}
|
||||
key="stringresponse"
|
||||
label="Text Input Problem"
|
||||
lastRow={false}
|
||||
selected={false}
|
||||
typeKey="stringresponse"
|
||||
updateAnswer={[MockFunction args.updateAnswer]}
|
||||
updateField={[MockFunction args.updateField]}
|
||||
/>
|
||||
<TypeRow
|
||||
answers={Array []}
|
||||
correctAnswerCount={0}
|
||||
key="advanced"
|
||||
label="Advanced Problem"
|
||||
lastRow={true}
|
||||
selected={true}
|
||||
typeKey="advanced"
|
||||
updateAnswer={[MockFunction args.updateAnswer]}
|
||||
updateField={[MockFunction args.updateField]}
|
||||
/>
|
||||
</SettingsOption>
|
||||
|
||||
@@ -35,7 +35,14 @@ export const prepareEditorRef = () => {
|
||||
return { editorRef, refReady, setEditorRef };
|
||||
};
|
||||
|
||||
export const initializeAnswerContainer = (problemType) => {
|
||||
export const initializeAnswerContainer = ({ answers, problemType, updateField }) => {
|
||||
const hasSingleAnswer = problemType === ProblemTypeKeys.DROPDOWN || problemType === ProblemTypeKeys.SINGLESELECT;
|
||||
let answerCount = 0;
|
||||
answers.forEach(answer => {
|
||||
if (answer.correct) {
|
||||
answerCount += 1;
|
||||
}
|
||||
});
|
||||
updateField({ correctAnswerCount: answerCount });
|
||||
return { hasSingleAnswer };
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ const initialState = {
|
||||
problemType: null,
|
||||
question: '',
|
||||
answers: [],
|
||||
correctAnswerCount: 0,
|
||||
groupFeedbackList: [],
|
||||
additionalAttributes: {},
|
||||
settings: {
|
||||
@@ -46,8 +47,17 @@ const problem = createSlice({
|
||||
}),
|
||||
updateAnswer: (state, { payload }) => {
|
||||
const { id, hasSingleAnswer, ...answer } = payload;
|
||||
let { correctAnswerCount } = state;
|
||||
const answers = state.answers.map(obj => {
|
||||
if (obj.id === id) {
|
||||
if (_.has(answer, 'correct') && payload.correct) {
|
||||
correctAnswerCount += 1;
|
||||
return { ...obj, ...answer };
|
||||
}
|
||||
if (_.has(answer, 'correct') && payload.correct === false) {
|
||||
correctAnswerCount -= 1;
|
||||
return { ...obj, ...answer };
|
||||
}
|
||||
return { ...obj, ...answer };
|
||||
}
|
||||
// set other answers as incorrect if problem only has one answer correct
|
||||
@@ -59,14 +69,19 @@ const problem = createSlice({
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
correctAnswerCount,
|
||||
answers,
|
||||
};
|
||||
},
|
||||
deleteAnswer: (state, { payload }) => {
|
||||
const { id } = payload;
|
||||
const { id, correct } = payload;
|
||||
if (state.answers.length <= 1) {
|
||||
return state;
|
||||
}
|
||||
let { correctAnswerCount } = state;
|
||||
if (correct) {
|
||||
correctAnswerCount -= 1;
|
||||
}
|
||||
const answers = state.answers.filter(obj => obj.id !== id).map((answer, index) => {
|
||||
const newId = indexToLetterMap[index];
|
||||
if (answer.id === newId) {
|
||||
@@ -76,6 +91,7 @@ const problem = createSlice({
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
correctAnswerCount,
|
||||
answers,
|
||||
};
|
||||
},
|
||||
|
||||
147
src/editors/data/redux/problem/reducers.test.js
Normal file
147
src/editors/data/redux/problem/reducers.test.js
Normal file
@@ -0,0 +1,147 @@
|
||||
import { initialState, actions, reducer } from './reducers';
|
||||
|
||||
const testingState = {
|
||||
...initialState,
|
||||
arbitraryField: 'arbitrary',
|
||||
};
|
||||
|
||||
describe('problem reducer', () => {
|
||||
it('has initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(initialState);
|
||||
});
|
||||
|
||||
const testValue = 'roll for initiative';
|
||||
|
||||
describe('handling actions', () => {
|
||||
const setterTest = (action, target) => {
|
||||
describe(action, () => {
|
||||
it(`load ${target} from payload`, () => {
|
||||
expect(reducer(testingState, actions[action](testValue))).toEqual({
|
||||
...testingState,
|
||||
[target]: testValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
[
|
||||
['updateQuestion', 'question'],
|
||||
].map(args => setterTest(...args));
|
||||
describe('load', () => {
|
||||
it('sets answers', () => {
|
||||
const answer = {
|
||||
id: 'A',
|
||||
correct: false,
|
||||
feedback: '',
|
||||
selectedFeedback: undefined,
|
||||
title: '',
|
||||
unselectedFeedback: undefined,
|
||||
};
|
||||
expect(reducer(testingState, actions.addAnswer(answer))).toEqual({
|
||||
...testingState,
|
||||
answers: [answer],
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('setProblemType', () => {
|
||||
it('sets problemType', () => {
|
||||
const payload = { selected: 'soMePRoblEMtYPe' };
|
||||
expect(reducer(testingState, actions.setProblemType(payload))).toEqual({
|
||||
...testingState,
|
||||
problemType: 'soMePRoblEMtYPe',
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('setEnableTypeSelection', () => {
|
||||
it('sets problemType to null', () => {
|
||||
expect(reducer(testingState, actions.setEnableTypeSelection())).toEqual({
|
||||
...testingState,
|
||||
problemType: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('updateField', () => {
|
||||
it('sets given parameter', () => {
|
||||
const payload = { problemType: 'soMePRoblEMtYPe' };
|
||||
expect(reducer(testingState, actions.updateField(payload))).toEqual({
|
||||
...testingState,
|
||||
...payload,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('updateSettings', () => {
|
||||
it('sets given settings parameter', () => {
|
||||
const payload = { hints: ['soMehInt'] };
|
||||
expect(reducer(testingState, actions.updateSettings(payload))).toEqual({
|
||||
...testingState,
|
||||
settings: {
|
||||
...testingState.settings,
|
||||
...payload,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('addAnswer', () => {
|
||||
it('sets answers', () => {
|
||||
const answer = {
|
||||
id: 'A',
|
||||
correct: false,
|
||||
feedback: '',
|
||||
selectedFeedback: undefined,
|
||||
title: '',
|
||||
unselectedFeedback: undefined,
|
||||
};
|
||||
expect(reducer(testingState, actions.addAnswer(answer))).toEqual({
|
||||
...testingState,
|
||||
answers: [answer],
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('updateAnswer', () => {
|
||||
it('sets answers, as well as setting the correctAnswerCount ', () => {
|
||||
const answer = { id: 'A', correct: true };
|
||||
expect(reducer(
|
||||
{
|
||||
...testingState,
|
||||
answers: [{
|
||||
id: 'A',
|
||||
correct: false,
|
||||
}],
|
||||
},
|
||||
actions.updateAnswer(answer),
|
||||
)).toEqual({
|
||||
...testingState,
|
||||
correctAnswerCount: 1,
|
||||
answers: [{ id: 'A', correct: true }],
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('deleteAnswer', () => {
|
||||
it('sets answers, as well as setting the correctAnswerCount ', () => {
|
||||
const answer = { id: 'A' };
|
||||
expect(reducer(
|
||||
{
|
||||
...testingState,
|
||||
correctAnswerCount: 1,
|
||||
answers: [{
|
||||
id: 'A',
|
||||
correct: false,
|
||||
},
|
||||
{
|
||||
id: 'B',
|
||||
correct: true,
|
||||
}],
|
||||
},
|
||||
actions.deleteAnswer(answer),
|
||||
)).toEqual({
|
||||
...testingState,
|
||||
correctAnswerCount: 1,
|
||||
answers: [
|
||||
{
|
||||
id: 'A',
|
||||
correct: true,
|
||||
}],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@ const mkSimpleSelector = (cb) => createSelector([module.problemState], cb);
|
||||
export const simpleSelectors = {
|
||||
problemType: mkSimpleSelector(problemData => problemData.problemType),
|
||||
answers: mkSimpleSelector(problemData => problemData.answers),
|
||||
correctAnswerCount: mkSimpleSelector(problemData => problemData.correctAnswerCount),
|
||||
settings: mkSimpleSelector(problemData => problemData.settings),
|
||||
question: mkSimpleSelector(problemData => problemData.question),
|
||||
completeState: mkSimpleSelector(problemData => problemData),
|
||||
|
||||
52
src/editors/data/redux/problem/selectors.test.js
Normal file
52
src/editors/data/redux/problem/selectors.test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// import * in order to mock in-file references
|
||||
import { keyStore } from '../../../utils';
|
||||
import * as selectors from './selectors';
|
||||
|
||||
jest.mock('reselect', () => ({
|
||||
createSelector: jest.fn((preSelectors, cb) => ({ preSelectors, cb })),
|
||||
}));
|
||||
|
||||
const testState = { some: 'arbitraryValue' };
|
||||
const testValue = 'my VALUE';
|
||||
|
||||
describe('problem selectors unit tests', () => {
|
||||
const {
|
||||
problemState,
|
||||
simpleSelectors,
|
||||
} = selectors;
|
||||
describe('problemState', () => {
|
||||
it('returns the problem data', () => {
|
||||
expect(problemState({ ...testState, problem: testValue })).toEqual(testValue);
|
||||
});
|
||||
});
|
||||
describe('simpleSelectors', () => {
|
||||
const testSimpleSelector = (key) => {
|
||||
test(`${key} simpleSelector returns its value from the problem store`, () => {
|
||||
const { preSelectors, cb } = simpleSelectors[key];
|
||||
expect(preSelectors).toEqual([problemState]);
|
||||
expect(cb({ ...testState, [key]: testValue })).toEqual(testValue);
|
||||
});
|
||||
};
|
||||
const simpleKeys = keyStore(simpleSelectors);
|
||||
describe('simple selectors link their values from problem store', () => {
|
||||
[
|
||||
simpleKeys.problemType,
|
||||
simpleKeys.answers,
|
||||
simpleKeys.correctAnswerCount,
|
||||
simpleKeys.settings,
|
||||
simpleKeys.question,
|
||||
].map(testSimpleSelector);
|
||||
});
|
||||
test('simple selector completeState equals the entire state', () => {
|
||||
const { preSelectors, cb } = simpleSelectors[simpleKeys.completeState];
|
||||
expect(preSelectors).toEqual([problemState]);
|
||||
expect(cb({
|
||||
...testState,
|
||||
[simpleKeys.completeState]: testValue,
|
||||
})).toEqual({
|
||||
...testState,
|
||||
[simpleKeys.completeState]: testValue,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user