Skip to content

Commit

Permalink
fix: parser not saving unlimited attempts (#483)
Browse files Browse the repository at this point in the history
* fix: default settings not loading for new problems

* fix: unlimited attempts not saving
  • Loading branch information
KristinAoki authored Jun 4, 2024
1 parent a959c05 commit e543ccc
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ describe('EditProblemView hooks parseState', () => {
});
});

it('returned parseState content.settings should not include default values', () => {
it('returned parseState content.settings should not include default values (not including maxAttempts)', () => {
const problem = {
...problemState,
problemType: ProblemTypeKeys.NUMERIC,
Expand All @@ -326,6 +326,7 @@ describe('EditProblemView hooks parseState', () => {
openSaveWarningModal,
});
expect(settings).toEqual({
max_attempts: '',
attempts_before_showanswer_button: 0,
submission_wait_seconds: 0,
weight: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/
import messages from './messages';
import hooks from '../hooks';

import { actions } from '../../../../../data/redux';
import { actions, selectors } from '../../../../../data/redux';

export const SelectTypeFooter = ({
onCancel,
selected,
// redux
defaultSettings,
updateField,
setBlockTitle,
// injected,
Expand All @@ -35,7 +36,12 @@ export const SelectTypeFooter = ({
</Button>
<Button
aria-label={intl.formatMessage(messages.selectButtonAriaLabel)}
onClick={hooks.onSelect({ selected, updateField, setBlockTitle })}
onClick={hooks.onSelect({
selected,
updateField,
setBlockTitle,
defaultSettings,
})}
disabled={!selected}
>
<FormattedMessage {...messages.selectButtonLabel} />
Expand All @@ -50,6 +56,12 @@ SelectTypeFooter.defaultProps = {
};

SelectTypeFooter.propTypes = {
defaultSettings: PropTypes.shape({
maxAttempts: PropTypes.number,
rerandomize: PropTypes.string,
showResetButton: PropTypes.bool,
showanswer: PropTypes.string,
}).isRequired,
onCancel: PropTypes.func.isRequired,
selected: PropTypes.string,
updateField: PropTypes.func.isRequired,
Expand All @@ -58,7 +70,8 @@ SelectTypeFooter.propTypes = {
intl: intlShape.isRequired,
};

export const mapStateToProps = () => ({
export const mapStateToProps = (state) => ({
defaultSettings: selectors.problem.defaultSettings(state),
});

export const mapDispatchToProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe('SelectTypeFooter', () => {
onCancel: jest.fn().mockName('onCancel'),
selected: null,
// redux
defaultSettings: {},
updateField: jest.fn().mockName('UpdateField'),
// inject
intl: { formatMessage },
Expand Down Expand Up @@ -45,7 +46,7 @@ describe('SelectTypeFooter', () => {

describe('mapStateToProps', () => {
test('is empty', () => {
expect(module.mapStateToProps()).toEqual({});
expect(module.mapDispatchToProps.defaultSettings).toEqual(actions.problem.defaultSettings);
});
});
describe('mapDispatchToProps', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import {
AdvanceProblemKeys, AdvanceProblems, ProblemTypeKeys, ProblemTypes,
} from '../../../../data/constants/problem';
import { StrictDict } from '../../../../utils';
import { StrictDict, snakeCaseKeys } from '../../../../utils';
import * as module from './hooks';
import { getDataFromOlx } from '../../../../data/redux/thunkActions/problem';

Expand All @@ -19,14 +19,28 @@ export const selectHooks = () => {
};
};

export const onSelect = ({ selected, updateField, setBlockTitle }) => () => {
export const onSelect = ({
selected,
updateField,
setBlockTitle,
defaultSettings,
}) => () => {
if (Object.values(AdvanceProblemKeys).includes(selected)) {
updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOLX: AdvanceProblems[selected].template });
setBlockTitle(AdvanceProblems[selected].title);
} else {
const newOLX = ProblemTypes[selected].template;
const { settings, ...newState } = getDataFromOlx({ rawOLX: newOLX, rawSettings: {}, defaultSettings: {} });
updateField({ ...newState });
const newState = getDataFromOlx({
rawOLX: newOLX,
rawSettings: {
weight: 1,
attempts_before_showanswer_button: 0,
show_reset_button: null,
showanswer: null,
},
defaultSettings: snakeCaseKeys(defaultSettings),
});
updateField(newState);
setBlockTitle(ProblemTypes[selected].title);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
import { MockUseState } from '../../../../../testUtils';
import * as module from './hooks';
import { AdvanceProblems, ProblemTypeKeys, ProblemTypes } from '../../../../data/constants/problem';
import { OLXParser } from '../../data/OLXParser';
import { getDataFromOlx } from '../../../../data/redux/thunkActions/problem';

jest.mock('react', () => ({
...jest.requireActual('react'),
Expand All @@ -17,6 +17,12 @@ const mockSelected = 'multiplechoiceresponse';
const mockAdvancedSelected = 'circuitschematic';
const mockSetSelected = jest.fn().mockName('setSelected');
const mocksetBlockTitle = jest.fn().mockName('setBlockTitle');
const mockDefaultSettings = {
max_attempts: null,
rerandomize: 'never',
showR_reset_button: false,
showanswer: 'always',
};

let hook;

Expand Down Expand Up @@ -58,13 +64,24 @@ describe('SelectTypeModal hooks', () => {
expect(mocksetBlockTitle).toHaveBeenCalledWith(AdvanceProblems[mockAdvancedSelected].title);
});
test('updateField is called with selected on visual propblems', () => {
module.onSelect({ selected: mockSelected, updateField: mockUpdateField, setBlockTitle: mocksetBlockTitle })();
const testOlXParser = new OLXParser(ProblemTypes[mockSelected].template);
const { settings, ...testState } = testOlXParser.getParsedOLXData();
expect(mockUpdateField).toHaveBeenCalledWith({
...testState,
module.onSelect({
selected: mockSelected,
updateField: mockUpdateField,
setBlockTitle: mocksetBlockTitle,
defaultSettings: mockDefaultSettings,
})();
// const testOlXParser = new OLXParser(ProblemTypes[mockSelected].template);
const testState = getDataFromOlx({
rawOLX: ProblemTypes[mockSelected].template,
});
rawSettings: {
weight: 1,
attempts_before_showanswer_button: 0,
show_reset_button: null,
showanswer: null,
},
defaultSettings: mockDefaultSettings,
});
expect(mockUpdateField).toHaveBeenCalledWith(testState);
expect(mocksetBlockTitle).toHaveBeenCalledWith(ProblemTypes[mockSelected].title);
});
});
Expand Down
4 changes: 3 additions & 1 deletion src/editors/containers/ProblemEditor/data/SettingsParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export const popuplateItem = (parentObject, itemName, statekey, metadata, defaul
let parent = parentObject;
const item = _.get(metadata, itemName, null);
const equalsDefault = item === defaultValue;
if ((!_.isNil(item) || allowNull) && !equalsDefault) {
if (allowNull) {
parent = { ...parentObject, [statekey]: item };
} else if (!_.isNil(item) && !equalsDefault) {
parent = { ...parentObject, [statekey]: item };
}
return parent;
Expand Down
2 changes: 1 addition & 1 deletion src/editors/data/redux/problem/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const initialState = {
scoring: {
weight: 1,
attempts: {
unlimited: false,
unlimited: true,
number: null,
},
},
Expand Down
1 change: 1 addition & 0 deletions src/editors/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { default as keyStore } from './keyStore';
export { default as camelizeKeys } from './camelizeKeys';
export { default as removeItemOnce } from './removeOnce';
export { default as formatDuration } from './formatDuration';
export { default as snakeCaseKeys } from './snakeCaseKeys';
15 changes: 15 additions & 0 deletions src/editors/utils/snakeCaseKeys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { snakeCase } from 'lodash-es';

const snakeCaseKeys = (obj) => {
if (Array.isArray(obj)) {
return obj.map(v => snakeCaseKeys(v));
}
if (obj != null && obj.constructor === Object) {
return Object.keys(obj).reduce(
(result, key) => ({ ...result, [snakeCase(key)]: snakeCaseKeys(obj[key]) }),
{},
);
}
return obj;
};
export default snakeCaseKeys;
32 changes: 32 additions & 0 deletions src/editors/utils/snakeCaseKeys.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { camelizeKeys } from './index';

const snakeCaseObject = {
some_attribute:
{
another_attribute: [
{ a_list: 'a lIsT' },
{ of_attributes: 'iN diFferent' },
{ different_cases: 'to Test' },
],
},
a_final_attribute: null,
a_last_one: undefined,
};
const camelCaseObject = {
someAttribute:
{
anotherAttribute: [
{ aList: 'a lIsT' },
{ ofAttributes: 'iN diFferent' },
{ differentCases: 'to Test' },
],
},
aFinalAttribute: null,
aLastOne: undefined,
};

describe('camelizeKeys', () => {
it('converts keys of objects to be camelCase', () => {
expect(camelizeKeys(snakeCaseObject)).toEqual(camelCaseObject);
});
});

0 comments on commit e543ccc

Please sign in to comment.