Skip to content

Commit b7de3ea

Browse files
author
Ben Warzeski
authored
bug: URL fixup for studio asset relative urls
* chore: assessment tests * fix: prompt escaping static links * fix: lint nit
1 parent 04e9177 commit b7de3ea

File tree

3 files changed

+128
-64
lines changed

3 files changed

+128
-64
lines changed

src/components/Prompt/index.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
44
import { useIntl } from '@edx/frontend-platform/i18n';
55
import { Collapsible } from '@edx/paragon';
66

7-
import { useActiveStepName } from 'hooks/app';
7+
import { useActiveStepName, useORAConfigData } from 'hooks/app';
88
import { useViewStep } from 'hooks/routing';
99

1010
import messages from './messages';
@@ -19,12 +19,21 @@ const Prompt = ({ prompt, defaultOpen }) => {
1919
const title = message ? formatMessage(message) : '';
2020
const imgRegex = /img src="\/asset-v1(.*)/g;
2121
const linkRegex = /a href="\/asset-v1(.*)/g;
22+
const { baseAssetUrl } = useORAConfigData();
2223
const promptWithAssets = prompt
2324
.replaceAll(imgRegex, `img src="${process.env.LMS_BASE_URL}/asset-v1$1`)
2425
.replaceAll(linkRegex, `a href="${process.env.LMS_BASE_URL}/asset-v1$1`);
26+
27+
const staticRegex = {
28+
img: /img src="\/static(.*)/g,
29+
link: /a href="\/static(.*)/g,
30+
};
31+
const promptWithStaticAssets = promptWithAssets
32+
.replaceAll(staticRegex.img, `img src="${process.env.LMS_BASE_URL}/${baseAssetUrl}@$1`)
33+
.replaceAll(staticRegex.link, `a href="${process.env.LMS_BASE_URL}/${baseAssetUrl}@$1`);
2534
return (
2635
<Collapsible title={(<h3 className="py-3">{title}</h3>)} open={open} onToggle={toggleOpen}>
27-
<div dangerouslySetInnerHTML={{ __html: promptWithAssets }} />
36+
<div dangerouslySetInnerHTML={{ __html: promptWithStaticAssets }} />
2837
</Collapsible>
2938
);
3039
};

src/hooks/assessment.js

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ import * as lmsActions from 'data/services/lms/hooks/actions';
88
import * as routingHooks from './routing';
99

1010
export const hooks = {
11+
useIsTrainingSelectionValid: () => {
12+
const assessment = reduxHooks.useFormFields();
13+
const expected = (lmsSelectors.useStepInfo()?.studentTraining || {}).expectedRubricSelections;
14+
if (!expected || !assessment) {
15+
return true;
16+
}
17+
return assessment.criteria.every(
18+
(criterion, criterionIndex) => `${expected[criterionIndex]}` === criterion.selectedOption,
19+
);
20+
},
21+
22+
useInitializeAssessment: () => {
23+
const emptyRubric = lmsSelectors.useEmptyRubric();
24+
const setFormFields = reduxHooks.useSetFormFields();
25+
const setResponse = reduxHooks.useSetResponse();
26+
const response = lmsSelectors.useResponseData();
27+
React.useEffect(() => {
28+
setResponse(response);
29+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
30+
31+
return React.useCallback(() => {
32+
setFormFields(emptyRubric);
33+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
34+
},
35+
1136
useIsCriterionFeedbackInvalid: () => {
1237
const viewStep = routingHooks.useViewStep();
1338
const criteriaConfig = lmsSelectors.useCriteriaConfig();
@@ -19,13 +44,10 @@ export const hooks = {
1944
};
2045
},
2146

22-
useTrainingOptionValidity: (criterionIndex) => {
23-
const value = reduxHooks.useCriterionOption(criterionIndex);
24-
const expected = (lmsSelectors.useStepInfo().studentTraining || {}).expectedRubricSelections;
25-
if (!value || !expected || expected[criterionIndex] === null) {
26-
return null;
27-
}
28-
return `${expected[criterionIndex]}` === value ? 'valid' : 'invalid';
47+
useOverallFeedbackFormFields: () => {
48+
const value = reduxHooks.useOverallFeedbackValue();
49+
const setFeedback = reduxHooks.useSetOverallFeedback();
50+
return { value, onChange: (e) => setFeedback(e.target.value) };
2951
},
3052

3153
useResetAssessment: () => {
@@ -38,40 +60,26 @@ export const hooks = {
3860
};
3961
},
4062

41-
useOverallFeedbackFormFields: () => {
42-
const value = reduxHooks.useOverallFeedbackValue();
43-
const setFeedback = reduxHooks.useSetOverallFeedback();
44-
return { value, onChange: (e) => setFeedback(e.target.value) };
45-
},
46-
47-
useCheckTrainingSelection: () => {
48-
const assessment = reduxHooks.useFormFields();
63+
useTrainingOptionValidity: (criterionIndex) => {
64+
const value = reduxHooks.useCriterionOption(criterionIndex);
4965
const expected = (lmsSelectors.useStepInfo().studentTraining || {}).expectedRubricSelections;
50-
if (!expected || !assessment) {
51-
return true;
66+
if (!value || !expected || expected[criterionIndex] === null) {
67+
return null;
5268
}
53-
return assessment.criteria.every(
54-
(criterion, criterionIndex) => (
55-
!expected || `${expected[criterionIndex]}` === criterion.selectedOption
56-
),
57-
);
58-
},
59-
60-
useInitializeAssessment: () => {
61-
const emptyRubric = lmsSelectors.useEmptyRubric();
62-
const setFormFields = reduxHooks.useSetFormFields();
63-
const setResponse = reduxHooks.useSetResponse();
64-
const response = lmsSelectors.useResponseData();
65-
React.useEffect(() => {
66-
setResponse(response);
67-
}, []); // eslint-disable-line react-hooks/exhaustive-deps
68-
69-
return React.useCallback(() => {
70-
setFormFields(emptyRubric);
71-
}, []); // eslint-disable-line react-hooks/exhaustive-deps
69+
return `${expected[criterionIndex]}` === value ? 'valid' : 'invalid';
7270
},
7371
};
72+
7473
Object.assign(hooks, {
74+
useCriterionFeedbackFormFields: (criterionIndex) => {
75+
const value = reduxHooks.useCriterionFeedback(criterionIndex);
76+
const setFeedback = reduxHooks.useSetCriterionFeedback(criterionIndex);
77+
const isInvalid = hooks.useIsCriterionFeedbackInvalid()({
78+
value, criterionIndex,
79+
});
80+
return { value, onChange: (e) => setFeedback(e.target.value), isInvalid };
81+
},
82+
7583
useCriterionOptionFormFields: (criterionIndex) => {
7684
const value = reduxHooks.useCriterionOption(criterionIndex);
7785
const setOption = reduxHooks.useSetCriterionOption(criterionIndex);
@@ -93,15 +101,6 @@ Object.assign(hooks, {
93101
};
94102
},
95103

96-
useCriterionFeedbackFormFields: (criterionIndex) => {
97-
const value = reduxHooks.useCriterionFeedback(criterionIndex);
98-
const setFeedback = reduxHooks.useSetCriterionFeedback(criterionIndex);
99-
const isInvalid = hooks.useIsCriterionFeedbackInvalid()({
100-
value, criterionIndex,
101-
});
102-
return { value, onChange: (e) => setFeedback(e.target.value), isInvalid };
103-
},
104-
105104
useIsAssessmentInvalid: () => {
106105
const assessment = reduxHooks.useFormFields();
107106
const criteriaConfig = lmsSelectors.useCriteriaConfig();
@@ -129,7 +128,7 @@ Object.assign(hooks, {
129128
const setHasSubmitted = reduxHooks.useSetHasSubmitted();
130129

131130
const isInvalid = hooks.useIsAssessmentInvalid();
132-
const checkTrainingSelection = hooks.useCheckTrainingSelection();
131+
const isTrainingSelectionValid = hooks.useIsTrainingSelectionValid();
133132

134133
const viewStep = routingHooks.useViewStep();
135134
const formFields = reduxHooks.useFormFields();
@@ -140,7 +139,7 @@ Object.assign(hooks, {
140139
if (isInvalid) {
141140
return setShowValidation(true);
142141
}
143-
if (viewStep === stepNames.studentTraining && !checkTrainingSelection) {
142+
if (viewStep === stepNames.studentTraining && !isTrainingSelectionValid) {
144143
return setShowTrainingError(true);
145144
}
146145
return submitAssessmentMutation.mutateAsync({
@@ -155,7 +154,7 @@ Object.assign(hooks, {
155154
formFields,
156155
isInvalid,
157156
setShowValidation,
158-
checkTrainingSelection,
157+
isTrainingSelectionValid,
159158
submitAssessmentMutation,
160159
setAssessment,
161160
setShowTrainingError,
@@ -174,6 +173,7 @@ export const {
174173
useCriterionOptionFormFields,
175174
useCriterionFeedbackFormFields,
176175
useIsAssessmentInvalid,
176+
useIsTrainingSelectionValid,
177177
useOnSubmit,
178178
} = hooks;
179179

src/hooks/assessment.test.js

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,72 @@ const testCriteriaConfig = [
3939
{ feedbackRequired: false },
4040
{ feedbackRequired: true },
4141
];
42-
const testValue0 = 'test-value-0';
43-
const testValue1 = 'test-value-1';
42+
const testAssessment = {
43+
criteria: [
44+
{ selectedOption: '1' },
45+
{ selectedOption: '2' },
46+
{ selectedOption: '3' },
47+
],
48+
};
4449
const testStepInfo = {
4550
studentTraining: {
46-
expectedRubricSelections: [testValue0, testValue1],
51+
expectedRubricSelections: [1, 2, 3],
4752
},
4853
};
4954
describe('Assessment hooks', () => {
5055
beforeEach(() => {
5156
jest.clearAllMocks();
5257
});
58+
59+
describe('useCheckTrainingSelection', () => {
60+
const prepHook = ({ assessment = testAssessment, stepInfo = testStepInfo } = {}) => {
61+
when(reduxHooks.useFormFields).calledWith().mockReturnValueOnce(assessment);
62+
when(lmsSelectors.useStepInfo).calledWith().mockReturnValueOnce(stepInfo);
63+
out = hooks.useIsTrainingSelectionValid();
64+
};
65+
describe('behavior', () => {
66+
it('loads assessment info and expected values from hooks', () => {
67+
prepHook();
68+
expect(reduxHooks.useFormFields).toHaveBeenCalledWith();
69+
expect(lmsSelectors.useStepInfo).toHaveBeenCalledWith();
70+
});
71+
});
72+
describe('output', () => {
73+
describe('variable safety-catches', () => {
74+
it('returns true if assessment is null', () => {
75+
prepHook({ assessment: null });
76+
expect(out).toEqual(true);
77+
});
78+
it('returns true if step info is null', () => {
79+
prepHook({ stepInfo: null });
80+
expect(out).toEqual(true);
81+
});
82+
it('returns true if step info is missing studentTraining', () => {
83+
prepHook({ stepInfo: {} });
84+
expect(out).toEqual(true);
85+
});
86+
it('returns true if step info is missing expectedRubricSelections', () => {
87+
prepHook({ stepInfo: { studentTraining: {} } });
88+
expect(out).toEqual(true);
89+
});
90+
it('returns true if there are no assessment criteria', () => {
91+
prepHook({ assessment: { criteria: [] } });
92+
expect(out).toEqual(true);
93+
});
94+
});
95+
it('returns true if the assessment is valid', () => {
96+
prepHook();
97+
expect(out).toEqual(true);
98+
});
99+
100+
it('returns false if any of the selected options to not match expected values', () => {
101+
prepHook({ studentTraining: { expectedRubricSelections: [1, 2, 4] } });
102+
expect(out).toEqual(true);
103+
});
104+
});
105+
});
106+
describe('useInitializeAssessment', () => {
107+
});
53108
describe('useIsCriterionFeedbackInvalid', () => {
54109
const prepHook = ({ viewStep, criteriaConfig }) => {
55110
routingHooks.useViewStep.mockReturnValueOnce(viewStep);
@@ -78,6 +133,10 @@ describe('Assessment hooks', () => {
78133
});
79134
});
80135
});
136+
describe('useOverallFeedbackFormFields', () => {
137+
});
138+
describe('useResetAssessment', () => {
139+
});
81140
describe('useTrainingOptionValidity', () => {
82141
const prepHook = ({ value, stepInfo, criterionIndex = 1 }) => {
83142
when(reduxHooks.useCriterionOption).calledWith(criterionIndex).mockReturnValue(value);
@@ -104,7 +163,7 @@ describe('Assessment hooks', () => {
104163
});
105164
describe('value is set and expected value is not null', () => {
106165
it('returns "valid" if value matches expected value', () => {
107-
prepHook({ value: testValue0, stepInfo: testStepInfo, criterionIndex: 0 });
166+
prepHook({ value: '1', stepInfo: testStepInfo, criterionIndex: 0 });
108167
expect(hooks.useTrainingOptionValidity(0)).toEqual('valid');
109168
});
110169
it('returns "invalid" if value does not match expected value', () => {
@@ -113,6 +172,9 @@ describe('Assessment hooks', () => {
113172
});
114173
});
115174
});
175+
176+
describe('useCriterionFeedbackFormFields', () => {
177+
});
116178
describe('useCriterionOptionFormFields', () => {
117179
const hook = hooks.useCriterionOptionFormFields;
118180
let spy;
@@ -178,22 +240,15 @@ describe('Assessment hooks', () => {
178240
});
179241
});
180242
});
181-
describe('useCriterionFeedbackFormFields', () => {
182-
});
183-
describe('useOverallFeedbackFormFields', () => {
184-
});
185243
describe('useIsAssessmentInvalid', () => {
186244
});
187-
describe('useCheckTrainingSelection', () => {
188-
});
189-
describe('useInitializeAssessment', () => {
190-
});
245+
191246
describe('useOnSubmit', () => {
192247
});
193-
describe('useResetAssessment', () => {
194-
});
248+
195249
describe('forwarded from reduxHooks', () => {
196250
});
251+
197252
describe('forwarded from lms selectors', () => {
198253
});
199254
});

0 commit comments

Comments
 (0)