From af4cd55390077bd02b64e6880d95ea6eb67059d7 Mon Sep 17 00:00:00 2001
From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com>
Date: Tue, 10 Jan 2023 13:25:19 -0500
Subject: [PATCH] feat: unselect multiple choices type multi->single (#181)
---
.../AnswerWidget/AnswersContainer.jsx | 6 +-
.../AnswerWidget/AnswersContainer.test.jsx | 50 ++++++
.../AnswersContainer.test.jsx.snap | 33 ++++
.../EditProblemView/SettingsWidget/hooks.js | 18 ++-
.../SettingsWidget/hooks.test.js | 14 +-
.../EditProblemView/SettingsWidget/index.jsx | 23 ++-
.../settingsComponents/TypeCard.jsx | 18 ++-
.../settingsComponents/TypeCard.test.jsx | 4 +
.../settingsComponents/TypeRow.jsx | 20 ++-
.../settingsComponents/TypeRow.test.jsx | 11 +-
.../__snapshots__/TypeCard.test.jsx.snap | 18 +++
src/editors/containers/ProblemEditor/hooks.js | 9 +-
src/editors/data/redux/problem/reducers.js | 18 ++-
.../data/redux/problem/reducers.test.js | 147 ++++++++++++++++++
src/editors/data/redux/problem/selectors.js | 1 +
.../data/redux/problem/selectors.test.js | 52 +++++++
16 files changed, 429 insertions(+), 13 deletions(-)
create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx
create mode 100644 src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap
create mode 100644 src/editors/data/redux/problem/reducers.test.js
create mode 100644 src/editors/data/redux/problem/selectors.test.js
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx
index d85b9ea2e..76bff6235 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx
@@ -16,9 +16,9 @@ export const AnswersContainer = ({
// Redux
answers,
addAnswer,
+ updateField,
}) => {
- const { hasSingleAnswer } = initializeAnswerContainer(problemType);
-
+ const { hasSingleAnswer } = initializeAnswerContainer({ answers, problemType, updateField });
return (
{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);
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx
new file mode 100644
index 000000000..f96a727f5
--- /dev/null
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx
@@ -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(
)).toMatchSnapshot();
+ });
+ test('snapshot: renders correctly with answers', () => {
+ expect(shallow(
)).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);
+ });
+ });
+});
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap
new file mode 100644
index 000000000..848eaa19e
--- /dev/null
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/__snapshots__/AnswersContainer.test.jsx.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AnswersContainer render snapshot: renders correct default 1`] = `
+
+
+
+`;
+
+exports[`AnswersContainer render snapshot: renders correctly with answers 1`] = `
+
+
+
+`;
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js
index e77ab6196..6ef3e3c76 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js
@@ -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,
};
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js
index 9d9a41b9d..b43a5046c 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js
@@ -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 });
});
});
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx
index b97c357f6..cef24d973 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx
@@ -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 = ({
-
+
@@ -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));
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx
index 06a085db2..b8d509b29 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx
@@ -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) => (
))}
@@ -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);
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx
index 1886661a5..d2a2f8518 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx
@@ -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 },
};
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx
index 71810543c..811499d43 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx
@@ -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,
};
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx
index 72c4579e8..2065e57c0 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx
@@ -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();
- expect(typeRowHooks).toHaveBeenCalledWith(typeKey, props.updateField);
+ expect(typeRowHooks).toHaveBeenCalledWith({
+ answers: props.answers,
+ correctAnswerCount: props.correctAnswerCount,
+ typeKey,
+ updateField: props.updateField,
+ updateAnswer: props.updateAnswer,
+ });
});
});
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap
index 251289f10..9b9ef8114 100644
--- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap
+++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/TypeCard.test.jsx.snap
@@ -6,51 +6,69 @@ exports[`TypeCard snapshot snapshot: renders type setting card 1`] = `
title="Type"
>
diff --git a/src/editors/containers/ProblemEditor/hooks.js b/src/editors/containers/ProblemEditor/hooks.js
index 78bf14ca6..cb0a539dd 100644
--- a/src/editors/containers/ProblemEditor/hooks.js
+++ b/src/editors/containers/ProblemEditor/hooks.js
@@ -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 };
};
diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js
index 765c26ede..41c6c3061 100644
--- a/src/editors/data/redux/problem/reducers.js
+++ b/src/editors/data/redux/problem/reducers.js
@@ -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,
};
},
diff --git a/src/editors/data/redux/problem/reducers.test.js b/src/editors/data/redux/problem/reducers.test.js
new file mode 100644
index 000000000..db1b828f2
--- /dev/null
+++ b/src/editors/data/redux/problem/reducers.test.js
@@ -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,
+ }],
+ });
+ });
+ });
+ });
+});
diff --git a/src/editors/data/redux/problem/selectors.js b/src/editors/data/redux/problem/selectors.js
index 532bc5289..a9c267600 100644
--- a/src/editors/data/redux/problem/selectors.js
+++ b/src/editors/data/redux/problem/selectors.js
@@ -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),
diff --git a/src/editors/data/redux/problem/selectors.test.js b/src/editors/data/redux/problem/selectors.test.js
new file mode 100644
index 000000000..b4d36bb13
--- /dev/null
+++ b/src/editors/data/redux/problem/selectors.test.js
@@ -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,
+ });
+ });
+ });
+});