chore: refactor OLXParser and related doc strings (#330)
This commit is contained in:
@@ -105,6 +105,19 @@ export class OLXParser {
|
||||
}
|
||||
}
|
||||
|
||||
/** parseMultipleChoiceAnswers(problemType, widgetName, option)
|
||||
* parseMultipleChoiceAnswers takes a problemType, widgetName, and a valid option. The
|
||||
* olx for the given problem type and widget is parsed. Depending on the problem
|
||||
* type, the title for an answer will be parsed differently because of single select and multiselect
|
||||
* problems are rich text while dropdown answers are plain text. The rich text is parsed into an object
|
||||
* and is converted back into a string before being added to the answer object. The parsing returns a
|
||||
* data object with an array of answer objects. If the olx has grouped feedback, this will also be
|
||||
* included in the data object.
|
||||
* @param {string} problemType - string of the olx problem type
|
||||
* @param {string} widgetName - string of the wrapping tag name (optioninput, choicegroup, checkboxgroup)
|
||||
* @param {string} option - string of the type of answers (choice or option)
|
||||
* @return {object} object containing an array of answer objects and possibly an array of grouped feedback
|
||||
*/
|
||||
parseMultipleChoiceAnswers(problemType, widgetName, option) {
|
||||
const answers = [];
|
||||
let data = {};
|
||||
@@ -167,6 +180,14 @@ export class OLXParser {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** getAnswerFeedback(choice, hintKey)
|
||||
* getAnswerFeedback takes a choice and a valid option. The choice object is checked for
|
||||
* selected and unselected feedback. The respective values are added to the feedback object.
|
||||
* The feedback object is returned.
|
||||
* @param {object} choice - object of an answer choice
|
||||
* @param {string} hintKey - string of the wrapping tag name (optionhint or choicehint)
|
||||
* @return {object} object containing selected and unselected feedback
|
||||
*/
|
||||
getAnswerFeedback(choice, hintKey) {
|
||||
let feedback = {};
|
||||
let feedbackKeys = 'selectedFeedback';
|
||||
@@ -194,6 +215,12 @@ export class OLXParser {
|
||||
return feedback;
|
||||
}
|
||||
|
||||
/** getGroupedFeedback(choices)
|
||||
* getGroupedFeedback takes choices. The choices with the attribute compoundhint are parsed for
|
||||
* the text value and the answers associated with the feedback. The groupFeedback array is returned.
|
||||
* @param {object} choices - object of problem's subtags
|
||||
* @return {array} array containing objects of feedback and associated answer ids
|
||||
*/
|
||||
getGroupedFeedback(choices) {
|
||||
const groupFeedback = [];
|
||||
if (_.has(choices, 'compoundhint')) {
|
||||
@@ -219,6 +246,15 @@ export class OLXParser {
|
||||
return groupFeedback;
|
||||
}
|
||||
|
||||
/** parseStringResponse()
|
||||
* The OLX saved to the class constuctor is parsed for text input answers. There are two
|
||||
* types of tags with the answer attribute, stringresponse (the problem wrapper) and
|
||||
* additional_answer. Looping through each tag, the associated title and feedback are added
|
||||
* to the answers object and appended to the answers array. The array returned in an object
|
||||
* with the key "answers". The object also conatins additional attributes that belong to the
|
||||
* string response tag.
|
||||
* @return {object} object containing an array of answer objects and object of additionalStringAttributes
|
||||
*/
|
||||
parseStringResponse() {
|
||||
const { stringresponse } = this.problem;
|
||||
const answers = [];
|
||||
@@ -295,6 +331,14 @@ export class OLXParser {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** parseNumericResponse()
|
||||
* The OLX saved to the class constuctor is parsed for numeric answers. There are two
|
||||
* types of tags for numeric answers, responseparam and additional_answer. Looping through
|
||||
* each tag, the associated title and feedback and if the answer is an answer range are
|
||||
* added to the answers object and appended to the answers array. The array returned in
|
||||
* an object with the key "answers".
|
||||
* @return {object} object containing an array of answer objects
|
||||
*/
|
||||
parseNumericResponse() {
|
||||
const { numericalresponse } = this.problem;
|
||||
let answerFeedback = '';
|
||||
@@ -343,6 +387,15 @@ export class OLXParser {
|
||||
return { answers };
|
||||
}
|
||||
|
||||
/** parseQuestions(problemType)
|
||||
* parseQuestions takes a problemType. The problem type is used to determine where the
|
||||
* text for the question lies (sibling or child to warpping problem type tags).
|
||||
* Using the XMLBuilder, the question is built with its proper children (including label
|
||||
* and description). The string version of the OLX is return, replacing the description
|
||||
* tags with italicized tags for styling purposes.
|
||||
* @param {string} problemType - string of the olx problem type
|
||||
* @return {string} string of OLX
|
||||
*/
|
||||
parseQuestions(problemType) {
|
||||
const options = {
|
||||
ignoreAttributes: false,
|
||||
@@ -380,6 +433,12 @@ export class OLXParser {
|
||||
return questionString.replace(/<description>/gm, '<em>').replace(/<\/description>/gm, '</em>');
|
||||
}
|
||||
|
||||
/** getHints()
|
||||
* The OLX saved to the class constuctor is parsed for demand hint tags with hint subtags. An empty array is returned
|
||||
* if there are no hints in the OLX. Otherwise the hint tag is parsed and appended to the hintsObject arrary. After
|
||||
* going through all the hints the hintsObject array is returned.
|
||||
* @return {array} array of hint objects
|
||||
*/
|
||||
getHints() {
|
||||
const hintsObject = [];
|
||||
if (_.has(this.problem, 'demandhint.hint')) {
|
||||
@@ -403,6 +462,14 @@ export class OLXParser {
|
||||
return hintsObject;
|
||||
}
|
||||
|
||||
/** parseQuestions(problemType)
|
||||
* parseQuestions takes a problemType. The problem type is used to determine where the
|
||||
* text for the solution lies (sibling or child to warpping problem type tags).
|
||||
* Using the XMLBuilder, the solution is built removing the redundant "explanation" that is
|
||||
* appended for Studio styling purposes. The string version of the OLX is return.
|
||||
* @param {string} problemType - string of the olx problem type
|
||||
* @return {string} string of OLX
|
||||
*/
|
||||
getSolutionExplanation(problemType) {
|
||||
if (!_.has(this.problem, `${problemType}.solution`) && !_.has(this.problem, 'solution')) { return null; }
|
||||
let solution = _.get(this.problem, `${problemType}.solution`, null) || _.get(this.problem, 'solution', null);
|
||||
@@ -433,6 +500,13 @@ export class OLXParser {
|
||||
return solutionString;
|
||||
}
|
||||
|
||||
/** getFeedback(xmlElement)
|
||||
* getFeedback takes xmlElement. The xmlElement is searched for the attribute correcthint.
|
||||
* An empty string is returned if the parameter is not present. Otherwise a string of the feedback
|
||||
* is returned.
|
||||
* @param {object} xmlElement - object of answer attributes
|
||||
* @return {string} string of feedback
|
||||
*/
|
||||
getFeedback(xmlElement) {
|
||||
if (!_.has(xmlElement, 'correcthint')) { return ''; }
|
||||
const feedback = _.get(xmlElement, 'correcthint');
|
||||
@@ -440,6 +514,13 @@ export class OLXParser {
|
||||
return feedbackString;
|
||||
}
|
||||
|
||||
/** getProblemType()
|
||||
* The OLX saved to the class constuctor is parsed for a valid problem type (referencing problemKeys).
|
||||
* For blank problems, it returns null. For OLX problems tags not defined in problemKeys or OLX with
|
||||
* multiple problem tags, it returns advanced. For defined, single problem tag, it returns the
|
||||
* associated problem type.
|
||||
* @return {string} problem type
|
||||
*/
|
||||
getProblemType() {
|
||||
const problemKeys = Object.keys(this.problem);
|
||||
const problemTypeKeys = problemKeys.filter(key => Object.values(ProblemTypeKeys).indexOf(key) !== -1);
|
||||
@@ -462,6 +543,14 @@ export class OLXParser {
|
||||
return problemType;
|
||||
}
|
||||
|
||||
/** getGeneralFeedback({ answers, problemType })
|
||||
* getGeneralFeedback takes answers and problemType. The problem type determines if the problem should be checked
|
||||
* for general feedback. The incorrect answers are checked to seee if all of their feedback is the same and
|
||||
* returns the first incorrect answer's feedback if true. When conditions are unmet, it returns and empty string.
|
||||
* @param {array} answers - array of answer objects
|
||||
* @param {string} problemType - string of string of the olx problem type
|
||||
* @return {string} text for incorrect feedback
|
||||
*/
|
||||
getGeneralFeedback({ answers, problemType }) {
|
||||
/* Feedback is Generalized for a Problem IFF:
|
||||
1. The problem is of Types: Single Select or Dropdown.
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
multipleChoiceWithFeedbackAndHintsOLX,
|
||||
textInputWithFeedbackAndHintsOLXWithMultipleAnswers,
|
||||
advancedProblemOlX,
|
||||
multipleProblemOlX,
|
||||
multipleProblemTwoOlX,
|
||||
multipleProblemThreeOlX,
|
||||
multipleTextInputProblemOlX,
|
||||
multipleNumericProblemOlX,
|
||||
NumericAndTextInputProblemOlX,
|
||||
blankProblemOLX,
|
||||
blankQuestionOLX,
|
||||
styledQuestionOLX,
|
||||
@@ -23,227 +23,275 @@ import {
|
||||
} from './mockData/olxTestData';
|
||||
import { ProblemTypeKeys } from '../../../data/constants/problem';
|
||||
|
||||
describe('Check OLXParser problem type', () => {
|
||||
test('Test checkbox with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.MULTISELECT);
|
||||
});
|
||||
test('Test numeric problem type', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.NUMERIC);
|
||||
});
|
||||
test('Test dropdown with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(dropdownOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.DROPDOWN);
|
||||
});
|
||||
test('Test multiple choice with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.SINGLESELECT);
|
||||
});
|
||||
test('Test textual problem type', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.TEXTINPUT);
|
||||
});
|
||||
test('Test Advanced Problem Type', () => {
|
||||
const olxparser = new OLXParser(advancedProblemOlX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
test('Test Advanced Problem Type by multiples', () => {
|
||||
const olxparser = new OLXParser(multipleProblemOlX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
test('Test Advanced Problem Type by multiples, second example', () => {
|
||||
const olxparser = new OLXParser(multipleProblemTwoOlX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
test('Test Advanced Problem Type by multiples, third example', () => {
|
||||
const olxparser = new OLXParser(multipleProblemThreeOlX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
test('Test Blank Problem Type', () => {
|
||||
const olxparser = new OLXParser(blankProblemOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(null);
|
||||
});
|
||||
});
|
||||
const blankOlxParser = new OLXParser(blankProblemOLX.rawOLX);
|
||||
const checkboxOlxParser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const numericOlxParser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const dropdownOlxParser = new OLXParser(dropdownOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const multipleChoiceOlxParser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const multipleChoiceWithoutAnswersOlxParser = new OLXParser(multipleChoiceWithoutAnswers.rawOLX);
|
||||
const textInputOlxParser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const textInputMultipleAnswersOlxParser = new OLXParser(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.rawOLX);
|
||||
const advancedOlxParser = new OLXParser(advancedProblemOlX.rawOLX);
|
||||
const multipleTextInputOlxParser = new OLXParser(multipleTextInputProblemOlX.rawOLX);
|
||||
const multipleNumericOlxParser = new OLXParser(multipleNumericProblemOlX.rawOLX);
|
||||
const numericAndTextInputOlxParser = new OLXParser(NumericAndTextInputProblemOlX.rawOLX);
|
||||
const labelDescriptionQuestionOlxParser = new OLXParser(labelDescriptionQuestionOLX.rawOLX);
|
||||
const shuffleOlxParser = new OLXParser(shuffleProblemOLX.rawOLX);
|
||||
|
||||
describe('OLX Parser settings attributes on problem tags', () => {
|
||||
test('OLX with attributes on the problem tags should error out', () => {
|
||||
const olxparser = new OLXParser(labelDescriptionQuestionOLX.rawOLX);
|
||||
try {
|
||||
olxparser.getParsedOLXData();
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
expect(e.message).toBe('Misc Attributes asscoiated with problem, opening in advanced editor');
|
||||
}
|
||||
describe('OLXParser', () => {
|
||||
describe('throws error and redirects to advanced editor', () => {
|
||||
describe('when settings attributes are on problem tags', () => {
|
||||
it('should throw error and contain message regarding opening advanced editor', () => {
|
||||
try {
|
||||
labelDescriptionQuestionOlxParser.getParsedOLXData();
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
expect(e.message).toBe('Misc Attributes asscoiated with problem, opening in advanced editor');
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('when settings attributes are on problem tags', () => {
|
||||
it('should throw error and contain message regarding opening advanced editor', () => {
|
||||
try {
|
||||
shuffleOlxParser.getParsedOLXData();
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
expect(e.message).toBe('Misc Tags, reverting to Advanced Editor');
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('when question parser finds script tags', () => {
|
||||
it('should throw error and contain message regarding opening advanced editor', () => {
|
||||
const olxparser = new OLXParser(scriptProblemOlX.rawOLX);
|
||||
expect(() => olxparser.parseQuestions('numericalresponse')).toThrow(new Error('Script Tag, reverting to Advanced Editor'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Check OLXParser hints', () => {
|
||||
test('Test checkbox hints', () => {
|
||||
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const hints = olxparser.getHints();
|
||||
expect(hints).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.hints);
|
||||
describe('getProblemType()', () => {
|
||||
describe('given a blank problem', () => {
|
||||
const problemType = blankOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.MULTISELECT', () => {
|
||||
expect(problemType).toEqual(null);
|
||||
});
|
||||
});
|
||||
describe('given checkbox olx with feedback and hints', () => {
|
||||
const problemType = checkboxOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.MULTISELECT', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.MULTISELECT);
|
||||
});
|
||||
});
|
||||
describe('given numeric olx with feedback and hints', () => {
|
||||
const problemType = numericOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.NUMERIC', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.NUMERIC);
|
||||
});
|
||||
});
|
||||
describe('given dropdown olx with feedback and hints', () => {
|
||||
const problemType = dropdownOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.DROPDOWN', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.DROPDOWN);
|
||||
});
|
||||
});
|
||||
describe('given multiple choice olx with feedback and hints', () => {
|
||||
const problemType = multipleChoiceOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.SINGLESELECT', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.SINGLESELECT);
|
||||
});
|
||||
});
|
||||
describe('given text input olx with feedback and hints', () => {
|
||||
const problemType = textInputOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.TEXTINPUT', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.TEXTINPUT);
|
||||
});
|
||||
});
|
||||
describe('given an advanced problem', () => {
|
||||
const problemType = advancedOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.ADVANCED', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
});
|
||||
describe('given a problem with multiple text inputs', () => {
|
||||
const problemType = multipleTextInputOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.ADVANCED', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
});
|
||||
describe('given a problem with multiple numeric inputs', () => {
|
||||
const problemType = multipleNumericOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.ADVANCED', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
});
|
||||
describe('given a problem with both a text and numeric input', () => {
|
||||
const problemType = numericAndTextInputOlxParser.getProblemType();
|
||||
it('should equal ProblemTypeKeys.ADVANCED', () => {
|
||||
expect(problemType).toEqual(ProblemTypeKeys.ADVANCED);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('Test numeric hints', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const hints = olxparser.getHints();
|
||||
expect(hints).toEqual(numericInputWithFeedbackAndHintsOLX.hints);
|
||||
describe('getHints()', () => {
|
||||
describe('given a problem with no hints', () => {
|
||||
const hints = labelDescriptionQuestionOlxParser.getHints();
|
||||
it('should return an empty array', () => {
|
||||
expect(hints).toEqual([]);
|
||||
});
|
||||
});
|
||||
describe('given checkbox olx with feedback and hints', () => {
|
||||
const hints = checkboxOlxParser.getHints();
|
||||
it('should equal an array of hints', () => {
|
||||
expect(hints).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
});
|
||||
describe('given numeric olx with feedback and hints', () => {
|
||||
const hints = numericOlxParser.getHints();
|
||||
it('should equal an array of hints', () => {
|
||||
expect(hints).toEqual(numericInputWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
});
|
||||
describe('given dropdown olx with feedback and hints', () => {
|
||||
const hints = dropdownOlxParser.getHints();
|
||||
it('should equal an array of hints', () => {
|
||||
expect(hints).toEqual(dropdownOLXWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
});
|
||||
describe('given multiple choice olx with feedback and hints', () => {
|
||||
const hints = multipleChoiceOlxParser.getHints();
|
||||
it('should equal an array of hints', () => {
|
||||
expect(hints).toEqual(multipleChoiceWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
});
|
||||
describe('given text input olx with feedback and hints', () => {
|
||||
const hints = textInputOlxParser.getHints();
|
||||
it('should equal an array of hints', () => {
|
||||
expect(hints).toEqual(textInputWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('Test dropdown with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(dropdownOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const hints = olxparser.getHints();
|
||||
expect(hints).toEqual(dropdownOLXWithFeedbackAndHintsOLX.hints);
|
||||
describe('parseMultipleChoiceAnswers()', () => {
|
||||
describe('given a problem with no answers', () => {
|
||||
const { answers } = multipleChoiceWithoutAnswersOlxParser.parseMultipleChoiceAnswers(
|
||||
'multiplechoiceresponse',
|
||||
'choicegroup',
|
||||
'choice',
|
||||
);
|
||||
it('should return a default answer', () => {
|
||||
expect(answers).toEqual(multipleChoiceWithoutAnswers.data.answers);
|
||||
expect(answers).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
describe('given multiple choice olx with hex numbers and leading zeros', () => {
|
||||
const olxparser = new OLXParser(numberParseTestOLX.rawOLX);
|
||||
const { answers } = olxparser.parseMultipleChoiceAnswers('multiplechoiceresponse', 'choicegroup', 'choice');
|
||||
it('should not parse hex numbers and leading zeros', () => {
|
||||
expect(answers).toEqual(numberParseTestOLX.data.answers);
|
||||
});
|
||||
it('should equal an array of objects with length four', () => {
|
||||
expect(answers).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
describe('given checkbox olx with feedback and hints', () => {
|
||||
const { answers } = checkboxOlxParser.parseMultipleChoiceAnswers('choiceresponse', 'checkboxgroup', 'choice');
|
||||
it('should equal an array of objects with length four', () => {
|
||||
expect(answers).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.data.answers);
|
||||
expect(answers).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
describe('given dropdown olx with feedback and hints', () => {
|
||||
const { answers } = dropdownOlxParser.parseMultipleChoiceAnswers('optionresponse', 'optioninput', 'option');
|
||||
it('should equal an array of objects with length three', () => {
|
||||
expect(answers).toEqual(dropdownOLXWithFeedbackAndHintsOLX.data.answers);
|
||||
expect(answers).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
describe('given multiple choice olx with feedback and hints', () => {
|
||||
const { answers } = multipleChoiceOlxParser.parseMultipleChoiceAnswers('multiplechoiceresponse', 'choicegroup', 'choice');
|
||||
it('should equal an array of objects with length three', () => {
|
||||
expect(answers).toEqual(multipleChoiceWithFeedbackAndHintsOLX.data.answers);
|
||||
expect(answers).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('Test multiple choice with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const hints = olxparser.getHints();
|
||||
expect(hints).toEqual(multipleChoiceWithFeedbackAndHintsOLX.hints);
|
||||
describe('parseStringResponse()', () => {
|
||||
// describe('given a problem with no answers', () => {
|
||||
// // TODO
|
||||
// });
|
||||
describe('given text input olx with feedback and hints', () => {
|
||||
const { answers } = textInputOlxParser.parseStringResponse();
|
||||
it('should equal an array of objects with length three', () => {
|
||||
expect(answers).toEqual(textInputWithFeedbackAndHintsOLX.data.answers);
|
||||
expect(answers).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
describe('given text input olx with feedback and hints with multiple answers', () => {
|
||||
const { answers } = textInputMultipleAnswersOlxParser.parseStringResponse();
|
||||
it('should equal an array of objects with length four', () => {
|
||||
expect(answers).toEqual(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.data.answers);
|
||||
expect(answers).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('Test textual problem type', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const hints = olxparser.getHints();
|
||||
expect(hints).toEqual(textInputWithFeedbackAndHintsOLX.hints);
|
||||
describe('parseNumericResponse()', () => {
|
||||
// describe('given a problem with no answers', () => {
|
||||
// // TODDO
|
||||
// });
|
||||
describe('given numeric olx with feedback and hints', () => {
|
||||
const { answers } = numericOlxParser.parseNumericResponse();
|
||||
it('should equal an array of objects with length two', () => {
|
||||
expect(answers).toEqual(numericInputWithFeedbackAndHintsOLX.data.answers);
|
||||
expect(answers).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Check OLXParser for answer parsing', () => {
|
||||
test('Test check single select with empty answers', () => {
|
||||
const olxparser = new OLXParser(multipleChoiceWithoutAnswers.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('multiplechoiceresponse', 'choicegroup', 'choice');
|
||||
expect(answer).toEqual(multipleChoiceWithoutAnswers.data);
|
||||
});
|
||||
test('Test checkbox answer', () => {
|
||||
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('choiceresponse', 'checkboxgroup', 'choice');
|
||||
expect(answer).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
|
||||
test('Test checkbox answer', () => {
|
||||
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('choiceresponse', 'checkboxgroup', 'choice');
|
||||
expect(answer).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
|
||||
test('Test checkboxs with extraneous tags error out', () => {
|
||||
const olxparser = new OLXParser(shuffleProblemOLX.rawOLX);
|
||||
try {
|
||||
olxparser.parseMultipleChoiceAnswers('choiceresponse', 'checkboxgroup', 'choice');
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
expect(e.message).toBe('Misc Tags, reverting to Advanced Editor');
|
||||
}
|
||||
});
|
||||
|
||||
test('Test dropdown answer', () => {
|
||||
const olxparser = new OLXParser(dropdownOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('optionresponse', 'optioninput', 'option');
|
||||
expect(answer).toEqual(dropdownOLXWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
test('Test multiple choice single select', () => {
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('multiplechoiceresponse', 'choicegroup', 'choice');
|
||||
expect(answer).toEqual(multipleChoiceWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
test('Test string response answers', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseStringResponse();
|
||||
expect(answer).toEqual(textInputWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
test('Test string response answers with multiple answers', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.rawOLX);
|
||||
const answer = olxparser.parseStringResponse();
|
||||
expect(answer).toEqual(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.data);
|
||||
});
|
||||
test('Test numerical response answers', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseNumericResponse();
|
||||
expect(answer).toEqual(numericInputWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Check OLXParser for question parsing', () => {
|
||||
test('Test checkbox question', () => {
|
||||
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const question = olxparser.parseQuestions('choiceresponse');
|
||||
expect(question).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test dropdown question', () => {
|
||||
const olxparser = new OLXParser(dropdownOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const question = olxparser.parseQuestions('optionresponse');
|
||||
expect(question).toEqual(dropdownOLXWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test multiple choice single select question', () => {
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const question = olxparser.parseQuestions('multiplechoiceresponse');
|
||||
expect(question).toEqual(multipleChoiceWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test string response question', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const question = olxparser.parseQuestions('stringresponse');
|
||||
expect(question).toEqual(textInputWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test numerical response question', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const question = olxparser.parseQuestions('numericalresponse');
|
||||
expect(question).toEqual(numericInputWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test Advanced Problem Type by script tag', () => {
|
||||
const olxparser = new OLXParser(scriptProblemOlX.rawOLX);
|
||||
expect(() => olxparser.parseQuestions('numericalresponse')).toThrow(new Error('Script Tag, reverting to Advanced Editor'));
|
||||
});
|
||||
test('Test OLX with no question content should have empty string for question', () => {
|
||||
const olxparser = new OLXParser(blankQuestionOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
expect(question).toBe(blankQuestionOLX.question);
|
||||
});
|
||||
test('Test OLX question content with styling should parse/build with correct styling', () => {
|
||||
const olxparser = new OLXParser(styledQuestionOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
expect(question).toBe(styledQuestionOLX.question);
|
||||
});
|
||||
test('Test OLX content with labels and descriptions inside reponse tag should parse correctly, appending the label/description to the question', () => {
|
||||
const olxparser = new OLXParser(labelDescriptionQuestionOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
expect(question).toBe(labelDescriptionQuestionOLX.question);
|
||||
});
|
||||
});
|
||||
|
||||
describe('OLXParser for problem with solution tag', () => {
|
||||
describe('for checkbox questions', () => {
|
||||
test('should parse text in p tags', () => {
|
||||
const { rawOLX } = getCheckboxesOLXWithFeedbackAndHintsOLX();
|
||||
const olxparser = new OLXParser(rawOLX);
|
||||
describe('parseQuestions()', () => {
|
||||
describe('given olx with no question content', () => {
|
||||
const olxparser = new OLXParser(blankQuestionOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const explanation = olxparser.getSolutionExplanation(problemType);
|
||||
const expected = getCheckboxesOLXWithFeedbackAndHintsOLX().solutionExplanation;
|
||||
expect(explanation.replace(/\s/g, '')).toBe(expected.replace(/\s/g, ''));
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
it('should return an empty string for question', () => {
|
||||
expect(question).toBe(blankQuestionOLX.question);
|
||||
});
|
||||
});
|
||||
describe('given a simple problem olx', () => {
|
||||
const question = textInputOlxParser.parseQuestions('stringresponse');
|
||||
it('should return a string of HTML', () => {
|
||||
expect(question).toEqual(textInputWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
});
|
||||
describe('given olx with html entities', () => {
|
||||
const olxparser = new OLXParser(htmlEntityTestOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
it('should not encode html entities', () => {
|
||||
expect(question).toEqual(htmlEntityTestOLX.question);
|
||||
});
|
||||
});
|
||||
describe('given olx with styled content', () => {
|
||||
const olxparser = new OLXParser(styledQuestionOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
it('should pase/build correct styling', () => {
|
||||
expect(question).toBe(styledQuestionOLX.question);
|
||||
});
|
||||
});
|
||||
describe('given olx with label and description tags inside response tag', () => {
|
||||
const olxparser = new OLXParser(labelDescriptionQuestionOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
it('should append the label/description to the question', () => {
|
||||
expect(question).toBe(labelDescriptionQuestionOLX.question);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('getSolutionExplanation()', () => {
|
||||
describe('for checkbox questions', () => {
|
||||
test('should parse text in p tags', () => {
|
||||
const { rawOLX } = getCheckboxesOLXWithFeedbackAndHintsOLX();
|
||||
const olxparser = new OLXParser(rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const explanation = olxparser.getSolutionExplanation(problemType);
|
||||
const expected = getCheckboxesOLXWithFeedbackAndHintsOLX().solutionExplanation;
|
||||
expect(explanation.replace(/\s/g, '')).toBe(expected.replace(/\s/g, ''));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Check OLXParser for proper encoding', () => {
|
||||
it('should not encode html entities', () => {
|
||||
const olxparser = new OLXParser(htmlEntityTestOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
const question = olxparser.parseQuestions(problemType);
|
||||
expect(question).toBe(htmlEntityTestOLX.question);
|
||||
});
|
||||
it('should not parse hex numbers and leading zeros', () => {
|
||||
const olxparser = new OLXParser(numberParseTestOLX.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('multiplechoiceresponse', 'choicegroup', 'choice');
|
||||
expect(answer).toEqual(numberParseTestOLX.data);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,6 +51,12 @@ class ReactStateOLXParser {
|
||||
this.problemState = problemState.problem;
|
||||
}
|
||||
|
||||
/** addHints()
|
||||
* The editorObject saved to the class constuctor is parsed for the attribute hints. No hints returns an empty object.
|
||||
* The hints are parsed and appended to the hintsArray as object representations of the hint. The hints array is saved
|
||||
* to the hint key in the demandHint object and returned.
|
||||
* @return {object} demandhint object with atrribut hint with array of objects
|
||||
*/
|
||||
addHints() {
|
||||
const hintsArray = [];
|
||||
const { hints } = this.editorObject;
|
||||
@@ -73,6 +79,13 @@ class ReactStateOLXParser {
|
||||
return demandhint;
|
||||
}
|
||||
|
||||
/** addSolution()
|
||||
* The editorObject saved to the class constuctor is parsed for the attribute solution. If the soltuion is empty, it
|
||||
* returns an empty object. The solution is parsed and checked if paragraph key's value is a string or array. Studio
|
||||
* requires a div wrapper with a heading (Explanation). The heading is prepended to the parsed solution object. The
|
||||
* solution object is returned with the updated div wrapper.
|
||||
* @return {object} object representation of solution
|
||||
*/
|
||||
addSolution() {
|
||||
const { solution } = this.editorObject;
|
||||
if (!solution || solution.length <= 0) { return {}; }
|
||||
@@ -91,6 +104,19 @@ class ReactStateOLXParser {
|
||||
return solutionObject;
|
||||
}
|
||||
|
||||
/** addMultiSelectAnswers(option)
|
||||
* addMultiSelectAnswers takes option. Option is used to assign an answers to the
|
||||
* correct OLX tag. This function is used for multiple choice, checkbox, and
|
||||
* dropdown problems. The editorObject saved to the class constuctor is parsed for
|
||||
* answers (titles only), selectFeedback, and unselectedFeedback. The problemState
|
||||
* saved to the class constructor is parsed for the problemType and answers (full
|
||||
* object). The answers are looped through to pair feedback with its respective
|
||||
* OLX tags. While matching feedback tags, answers are also mapped to their
|
||||
* respective OLX tags. he object representation of the answers is returned with
|
||||
* the correct wrapping tags. For checkbox problems, compound hints are also returned.
|
||||
* @param {string} option - string of answer tag name
|
||||
* @return {object} object representation of answers
|
||||
*/
|
||||
addMultiSelectAnswers(option) {
|
||||
const choice = [];
|
||||
let compoundhint = [];
|
||||
@@ -173,6 +199,12 @@ class ReactStateOLXParser {
|
||||
return widget;
|
||||
}
|
||||
|
||||
/** addGroupFeedbackList()
|
||||
* The problemState saved to the class constuctor is parsed for the attribute groupFeedbackList.
|
||||
* No group feedback returns an empty array. Each groupFeedback in the groupFeedback list is
|
||||
* mapped to a new object and appended to the compoundhint array.
|
||||
* @return {object} object representation of compoundhints
|
||||
*/
|
||||
addGroupFeedbackList() {
|
||||
const compoundhint = [];
|
||||
const { groupFeedbackList } = this.problemState;
|
||||
@@ -185,6 +217,11 @@ class ReactStateOLXParser {
|
||||
return compoundhint;
|
||||
}
|
||||
|
||||
/** addQuestion()
|
||||
* The editorObject saved to the class constuctor is parsed for the attribute question. The question is parsed and
|
||||
* checked for label tags. After the question is fully updated, the questionObject is returned.
|
||||
* @return {object} object representaion of question
|
||||
*/
|
||||
addQuestion() {
|
||||
const { question } = this.editorObject;
|
||||
const questionObject = this.questionParser.parse(question);
|
||||
@@ -207,6 +244,16 @@ class ReactStateOLXParser {
|
||||
return questionObject;
|
||||
}
|
||||
|
||||
/** buildMultiSelectProblem()
|
||||
* OLX builder for multiple choice, checkbox, and dropdown problems. The question
|
||||
* builder has a different format than the other parts (demand hint, answers, and
|
||||
* solution) of the problem so it has to be inserted into the OLX after the rest
|
||||
* of the problem is built.
|
||||
* @param {string} problemType - string of problem type tag
|
||||
* @param {string} widget - string of answer tag name
|
||||
* @param {string} option - string of feedback tag name
|
||||
* @return {string} string of OLX
|
||||
*/
|
||||
buildMultiSelectProblem(problemType, widget, option) {
|
||||
const question = this.addQuestion();
|
||||
const widgetObject = this.addMultiSelectAnswers(option);
|
||||
@@ -245,6 +292,12 @@ class ReactStateOLXParser {
|
||||
return problemString;
|
||||
}
|
||||
|
||||
/** buildTextInput()
|
||||
* String response OLX builder. The question builder has a different format than the
|
||||
* other parts (demand hint, answers, and solution) of the problem so it has to be
|
||||
* inserted into the OLX after the rest of the problem is built.
|
||||
* @return {string} string of string response OLX
|
||||
*/
|
||||
buildTextInput() {
|
||||
const question = this.addQuestion();
|
||||
const demandhint = this.addHints();
|
||||
@@ -270,6 +323,20 @@ class ReactStateOLXParser {
|
||||
return problemString;
|
||||
}
|
||||
|
||||
/** buildTextInputAnswersFeedback()
|
||||
* The editorObject saved to the class constuctor is parsed for the attribute
|
||||
* selectedFeedback. String response problems have two types of feedback tags,
|
||||
* correcthint and stringequalhint. Correcthint is for feedback associated with
|
||||
* correct answers and stringequalhint is for feedback associated with wrong
|
||||
* answers. The answers are fetched from the problemState and looped through to
|
||||
* pair feedback with its respective OLX tags. While matching feedback tags,
|
||||
* answers are also mapped to their respective OLX tags. The first correct
|
||||
* answer is wrapped in stringreponse tag. All other correct answers are wrapped
|
||||
* in additonal_answer tags. Incorrect answers are wrapped in stringequalhint
|
||||
* tags. The object representation of the answers is returned with the correct
|
||||
* wrapping tags.
|
||||
* @return {object} object representation of answers
|
||||
*/
|
||||
buildTextInputAnswersFeedback() {
|
||||
const { answers } = this.problemState;
|
||||
const { selectedFeedback } = this.editorObject;
|
||||
@@ -312,6 +379,12 @@ class ReactStateOLXParser {
|
||||
return answerObject;
|
||||
}
|
||||
|
||||
/** buildNumericInput()
|
||||
* Numeric response OLX builder. The question builder has a different format than the
|
||||
* other parts (demand hint, answers, and solution) of the problem so it has to be
|
||||
* inserted into the OLX after the rest of the problem is built.
|
||||
* @return {string} string of numeric response OLX
|
||||
*/
|
||||
buildNumericInput() {
|
||||
const question = this.addQuestion();
|
||||
const demandhint = this.addHints();
|
||||
@@ -337,6 +410,18 @@ class ReactStateOLXParser {
|
||||
return problemString;
|
||||
}
|
||||
|
||||
/** buildNumericalResponse()
|
||||
* The editorObject saved to the class constuctor is parsed for the attribute
|
||||
* selectedFeedback. The tolerance is fetched from the problemState settings.
|
||||
* The answers are fetched from the problemState and looped through to
|
||||
* pair feedback with its respective OLX tags. While matching feedback tags,
|
||||
* answers are also mapped to their respective OLX tags. For each answer, if
|
||||
* it is an answer range, it is santized to be less than to great than. The
|
||||
* first answer is wrapped in numericresponse tag. All other answers are
|
||||
* wrapped in additonal_answer tags. The object representation of the answers
|
||||
* is returned with the correct wrapping tags.
|
||||
* @return {object} object representation of answers
|
||||
*/
|
||||
buildNumericalResponse() {
|
||||
const { answers } = this.problemState;
|
||||
const { tolerance } = this.problemState.settings;
|
||||
@@ -419,6 +504,14 @@ class ReactStateOLXParser {
|
||||
return answerObject;
|
||||
}
|
||||
|
||||
/** getAnswerHints(feedback)
|
||||
* getAnswerHints takes feedback. The feedback is checked for definition. If feedback is
|
||||
* undefined or an empty string, it returns an empty object. The defined feedback is
|
||||
* parsed and saved to the key correcthint. Correcthint is the tag name for
|
||||
* numeric response and string response feedback.
|
||||
* @param {string} feedback - string of feedback
|
||||
* @return {object} object representaion of feedback
|
||||
*/
|
||||
getAnswerHints(feedback) {
|
||||
let correcthint = {};
|
||||
if (feedback !== undefined && feedback !== '') {
|
||||
@@ -432,6 +525,13 @@ class ReactStateOLXParser {
|
||||
return correcthint;
|
||||
}
|
||||
|
||||
/** hasAttributeWithValue(obj, attr)
|
||||
* hasAttributeWithValue takes obj and atrr. The obj is checked for the attribute defined by attr.
|
||||
* Returns true if atrribute is present, otherwise false.
|
||||
* @param {object} obj - defined object
|
||||
* @param {string} attr - string of desired attribute
|
||||
* @return {bool}
|
||||
*/
|
||||
hasAttributeWithValue(obj, attr) {
|
||||
return _.has(obj, attr) && _.get(obj, attr, '').toString().trim() !== '';
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ export const scriptProblemOlX = {
|
||||
</numericalresponse>
|
||||
</problem>`,
|
||||
};
|
||||
export const multipleProblemOlX = {
|
||||
export const multipleTextInputProblemOlX = {
|
||||
rawOLX: `<problem>
|
||||
<stringresponse answer="correct answer">
|
||||
<textline size="20"/>
|
||||
@@ -609,7 +609,7 @@ export const multipleProblemOlX = {
|
||||
</stringresponse>
|
||||
</problem>`,
|
||||
};
|
||||
export const multipleProblemTwoOlX = {
|
||||
export const multipleNumericProblemOlX = {
|
||||
rawOLX: `<problem>
|
||||
<numericalresponse answer="100">
|
||||
<formulaequationinput></formulaequationinput>
|
||||
@@ -619,7 +619,7 @@ export const multipleProblemTwoOlX = {
|
||||
</numericalresponse>
|
||||
</problem>`,
|
||||
};
|
||||
export const multipleProblemThreeOlX = {
|
||||
export const NumericAndTextInputProblemOlX = {
|
||||
rawOLX: `<problem>
|
||||
<stringresponse answer="correct answer">
|
||||
<textline size="20"/>
|
||||
|
||||
Reference in New Issue
Block a user