def test_no_answer_pool(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse> <choicegroup type="MultipleChoice"> <choice correct="false">wrong-1</choice> <choice correct="false">wrong-2</choice> <choice correct="true">correct-1</choice> <choice correct="false">wrong-3</choice> <choice correct="false">wrong-4</choice> </choicegroup> </multiplechoiceresponse> </problem> """) problem = new_loncapa_problem(xml_str, seed=723) the_html = problem.get_html() str1 = r"<div>.*\[.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*'wrong-4'.*\].*</div>" self.assertRegexpMatches(the_html, str1) # attributes *not* present response = problem.responders.values()[0] self.assertFalse(response.has_mask()) self.assertFalse(response.has_answerpool())
def test_shuffle_fixed_both_ends(self): xml_str = textwrap.dedent( """ <problem> <multiplechoiceresponse> <choicegroup type="MultipleChoice" shuffle="true"> <choice correct="false" fixed="true">Alpha</choice> <choice correct="false" fixed="true">Beta</choice> <choice correct="false">A</choice> <choice correct="false">B</choice> <choice correct="false">C</choice> <choice correct ="true">D</choice> <choice correct="false" fixed="true">Psi</choice> <choice correct="false" fixed="true">Omega</choice> </choicegroup> </multiplechoiceresponse> </problem> """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() self.assertRegexpMatches( the_html, r"<div>.*\[.*'Alpha'.*'Beta'.*'B'.*'A'.*'C'.*'D'.*'Psi'.*'Omega'.*\].*</div>" )
def test_label_is_empty_if_no_label_attribute(self): """ Verify that label in response_data is empty string when label attribute is missing and responsetype is not fully accessible. """ question = "Click the country which is home to the Pyramids." xml = """ <problem> <p>{}</p> <imageresponse> <imageinput src="/static/Africa.png" width="600" height="638" rectangle="(338,98)-(412,168)"/> </imageresponse> </problem> """.format(question) problem = new_loncapa_problem(xml) self.assertEqual( problem.problem_data, { '1_2_1': { 'label': '', 'descriptions': {} } } )
def test_find_correct_answer_text_choices(self, answer_id, answer_text): """ Verify that ``find_correct_answer_text`` can find the correct answer for ChoiceResponse, MultipleChoiceResponse and OptionResponse problems. """ problem = new_loncapa_problem( """ <problem> <choiceresponse> <checkboxgroup label="Select the correct synonym of paranoid?"> <choice correct="true">over-suspicious</choice> <choice correct="false">funny</choice> </checkboxgroup> </choiceresponse> <multiplechoiceresponse> <choicegroup type="MultipleChoice"> <choice correct="true">The iPad</choice> <choice correct="true">Napster</choice> <choice correct="false">The iPod</choice> </choicegroup> </multiplechoiceresponse> <optionresponse> <optioninput options="('yellow','blue','green')" correct="blue" label="Color_1"/> </optionresponse> </problem> """ ) self.assertEquals(problem.find_correct_answer_text(answer_id), answer_text)
def test_label_and_description_inside_responsetype(self, question): """ Verify that * label is extracted * <label> tag is removed to avoid duplication This is the case when we have a problem with single question or problem with multiple-questions separated as per the new format. """ xml = """ <problem> <choiceresponse> <label>{question}</label> <description>Only the paranoid survive.</description> <checkboxgroup> <choice correct="true">over-suspicious</choice> <choice correct="false">funny</choice> </checkboxgroup> </choiceresponse> </problem> """.format(question=question) problem = new_loncapa_problem(xml) self.assertEqual( problem.problem_data, { '1_2_1': { 'label': question, 'descriptions': {'description_1_1_1': 'Only the paranoid survive.'} } } ) self.assertEqual(len(problem.tree.xpath('//label')), 0)
def test_invalid_answer_pool_all_correct(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse> <choicegroup type="MultipleChoice" answer-pool="4"> <choice correct="true">!wrong-1</choice> <choice correct="true">!wrong-2</choice> <choice correct="true">!wrong-3</choice> <choice correct="true">!wrong-4</choice> </choicegroup> </multiplechoiceresponse> </problem> """) with self.assertRaisesRegexp(LoncapaProblemError, "1 correct.*1 incorrect"): new_loncapa_problem(xml_str)
def test_render_response_with_overall_msg(self): # CustomResponse script that sets an overall_message script = textwrap.dedent(""" def check_func(*args): msg = '<p>Test message 1<br /></p><p>Test message 2</p>' return {'overall_message': msg, 'input_list': [ {'ok': True, 'msg': '' } ] } """) # Generate some XML for a CustomResponse kwargs = {'script': script, 'cfn': 'check_func'} xml_str = CustomResponseXMLFactory().build_xml(**kwargs) # Create the problem and render the html problem = new_loncapa_problem(xml_str) # Grade the problem problem.grade_answers({'1_2_1': 'test'}) # Render the html rendered_html = etree.XML(problem.get_html()) # Expect that there is a <div> within the response <div> # with css class response_message msg_div_element = rendered_html.find(".//div[@class='response_message']") self.assertEqual(msg_div_element.tag, "div") self.assertEqual(msg_div_element.get('class'), "response_message") # Expect that the <div> contains our message (as part of the XML tree) msg_p_elements = msg_div_element.findall('p') self.assertEqual(msg_p_elements[0].tag, "p") self.assertEqual(msg_p_elements[0].text, "Test message 1") self.assertEqual(msg_p_elements[1].tag, "p") self.assertEqual(msg_p_elements[1].text, "Test message 2")
def test_multiple_descriptions(self): """ Verify that multiple descriptions are handled correctly. """ desc1 = "The problem with trying to be the <em>bad guy</em>, there's always someone <strong>worse</strong>." desc2 = "Anyone who looks the world as if it was a game of chess deserves to lose." xml = """ <problem> <p>Be sure to check your spelling.</p> <stringresponse answer="War" type="ci"> <label>___ requires sacrifices.</label> <description>{}</description> <description>{}</description> <textline size="40"/> </stringresponse> </problem> """.format(desc1, desc2) problem = new_loncapa_problem(xml) self.assertEqual( problem.problem_data, { '1_2_1': { 'label': '___ requires sacrifices.', 'descriptions': { 'description_1_1_1': desc1, 'description_1_1_2': desc2 } } } )
def test_non_accessible_inputtype(self): """ Verify that tag with question text is not removed when inputtype is not fully accessible. """ question = "Click the country which is home to the Pyramids." xml = """ <problem> <p>{}</p> <imageresponse> <imageinput label="{}" src="/static/Africa.png" width="600" height="638" rectangle="(338,98)-(412,168)"/> </imageresponse> </problem> """.format(question, question) problem = new_loncapa_problem(xml) self.assertEqual( problem.problem_data, { '1_2_1': { 'label': question, 'descriptions': {} } } ) # <p> tag with question text should not be deleted self.assertEqual(problem.tree.xpath("string(p[text()='{}'])".format(question)), question)
def test_shuffle_4_choices(self): xml_str = textwrap.dedent( """ <problem> <multiplechoiceresponse> <choicegroup type="MultipleChoice" shuffle="true"> <choice correct="false">Apple</choice> <choice correct="false">Banana</choice> <choice correct="false">Chocolate</choice> <choice correct ="true">Donut</choice> </choicegroup> </multiplechoiceresponse> </problem> """ ) problem = new_loncapa_problem(xml_str, seed=0) # shuffling 4 things with seed of 0 yields: B A C D # Check that the choices are shuffled the_html = problem.get_html() self.assertRegexpMatches(the_html, r"<div>.*\[.*'Banana'.*'Apple'.*'Chocolate'.*'Donut'.*\].*</div>") # Check that choice name masking is enabled and that unmasking works response = problem.responders.values()[0] self.assertFalse(response.has_mask()) self.assertEqual(response.unmask_order(), ["choice_1", "choice_0", "choice_2", "choice_3"]) self.assertEqual(the_html, problem.get_html(), "should be able to call get_html() twice")
def test_answer_pool_without_solutionset(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse> <choicegroup type="MultipleChoice" answer-pool="4"> <choice correct="false">wrong-1</choice> <choice correct="false">wrong-2</choice> <choice correct="true">correct-1</choice> <choice correct="false">wrong-3</choice> <choice correct="false">wrong-4</choice> <choice correct="true">correct-2</choice> </choicegroup> </multiplechoiceresponse> <solution> <div class="detailed-solution"> <p>Explanation</p> <p>This is the solution</p> <p>Not much to explain here, sorry!</p> </div> </solution> </problem> """) problem = new_loncapa_problem(xml_str, seed=723) the_html = problem.get_html() self.assertRegexpMatches(the_html, r"<div>.*\[.*'wrong-3'.*'correct-2'.*'wrong-2'.*'wrong-4'.*\].*</div>") self.assertRegexpMatches(the_html, r"<div>\{.*'1_solution_1'.*\}</div>")
def test_targeted_feedback_no_solution_element(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse targeted-feedback=""> <choicegroup type="MultipleChoice"> <choice correct="false">wrong-1</choice> <choice correct="false">wrong-2</choice> <choice correct="true" explanation-id="feedbackC">correct-1</choice> <choice correct="false">wrong-3</choice> </choicegroup> </multiplechoiceresponse> <targetedfeedbackset> <targetedfeedback explanation-id="feedbackC"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> </div> </targetedfeedback> </targetedfeedbackset> </problem> """) # Solution element not found problem = new_loncapa_problem(xml_str) problem.done = True problem.student_answers = {'1_2_1': 'choice_2'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # </div> right after </targetedfeedbackset> self.assertRegexpMatches( without_new_lines, r"<div>.*<targetedfeedbackset>.*</targetedfeedbackset>\s*</div>" )
def test_single_inputtypes(self): """ Verify that HTML is correctly rendered when there is single inputtype. """ question = 'Enter sum of 1+2' xml = textwrap.dedent(""" <problem> <customresponse cfn="test_sum" expect="3"> <script type="loncapa/python"> def test_sum(expect, ans): return int(expect) == int(ans) </script> <label>{}</label> <textline size="20" correct_answer="3" /> </customresponse> </problem> """.format(question)) problem = new_loncapa_problem(xml, use_capa_render_template=True) problem_html = etree.XML(problem.get_html()) # verify that only no multi input group div is present multi_inputs_group = problem_html.xpath('//div[@class="multi-inputs-group"]') self.assertEqual(len(multi_inputs_group), 0) # verify that question is rendered only once question = problem_html.xpath("//*[normalize-space(text())='{}']".format(question)) self.assertEqual(len(question), 1)
def test_multiple_inputtypes(self, group_label): """ Verify that group label and labels for individual inputtypes are extracted correctly. """ input1_label = 'What color is the sky?' input2_label = 'What color are pine needles?' xml = """ <problem> <optionresponse> <label>{}</label> <optioninput options="('yellow','blue','green')" correct="blue" label="{}"/> <optioninput options="('yellow','blue','green')" correct="green" label="{}"/> </optionresponse> </problem> """.format(group_label, input1_label, input2_label) problem = new_loncapa_problem(xml) self.assertEqual( problem.problem_data, { '1_2_1': { 'group_label': group_label, 'label': input1_label, 'descriptions': {} }, '1_2_2': { 'group_label': group_label, 'label': input2_label, 'descriptions': {} } } )
def test_legacy_problem(self, question, label_attr): """ Verify that legacy problem is handled correctly. """ xml = """ <problem> <p>Be sure to check your spelling.</p> <p>{}</p> <stringresponse answer="vulnerable" type="ci"> <textline label="{}" size="40"/> </stringresponse> </problem> """.format(question, label_attr) problem = new_loncapa_problem(xml) self.assertEqual( problem.problem_data, { '1_2_1': { 'label': question, 'descriptions': {} } } ) self.assertEqual( len(problem.tree.xpath("//*[normalize-space(text())='{}']".format(question))), 0 )
def test_targeted_feedback_not_finished(self): problem = new_loncapa_problem(load_fixture('targeted_feedback.xml')) the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertRegexpMatches(without_new_lines, r"<div>.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*</div>") self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedback3|feedbackC") self.assertEquals(the_html, problem.get_html(), "Should be able to call get_html() twice")
def test_shuffle_not_with_answerpool(self): """Raise error if shuffle and answer-pool are both used.""" xml_str = textwrap.dedent(""" <problem> <multiplechoiceresponse> <choicegroup type="MultipleChoice" shuffle="true" answer-pool="4"> <choice correct="false" fixed="true">A</choice> <choice correct="false">Mid</choice> <choice correct="true" fixed="true">C</choice> <choice correct="False">Mid</choice> <choice correct="false" fixed="true">D</choice> </choicegroup> </multiplechoiceresponse> </problem> """) with self.assertRaisesRegexp(LoncapaProblemError, "shuffle and answer-pool"): new_loncapa_problem(xml_str)
def test_targeted_feedback_multiple_not_answered(self): # Not answered -> empty targeted feedback problem = new_loncapa_problem(load_fixture('targeted_feedback_multiple.xml')) the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # Q1 and Q2 have no feedback self.assertRegexpMatches( without_new_lines, r'<targetedfeedbackset.*?/>.*<targetedfeedbackset.*?/>' )
def test_targeted_feedback_student_answer2(self): problem = new_loncapa_problem(load_fixture('targeted_feedback.xml')) problem.done = True problem.student_answers = {'1_2_1': 'choice_0'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # pylint: disable=line-too-long self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback1\" role=\"group\" aria-describedby=\"1_2_1-legend\">\s*<span class=\"sr\">Incorrect</span>.*1st WRONG solution") self.assertRegexpMatches(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>") self.assertNotRegexpMatches(without_new_lines, r"feedback2|feedback3|feedbackC")
def test_answer_pool_4_choices_1_multiplechoiceresponse_seed2(self): problem = new_loncapa_problem(self.common_question_xml, seed=9) the_html = problem.get_html() # [('choice_0', u'wrong-1'), ('choice_4', u'wrong-4'), ('choice_3', u'wrong-3'), ('choice_2', u'correct-1')] self.assertRegexpMatches(the_html, r"<div>.*\[.*'wrong-1'.*'wrong-4'.*'wrong-3'.*'correct-1'.*\].*</div>") self.assertRegexpMatches(the_html, r"<div>\{.*'1_solution_1'.*\}</div>") # Check about masking response = problem.responders.values()[0] self.assertFalse(response.has_mask()) self.assertTrue(hasattr(response, 'has_answerpool')) self.assertEqual(response.unmask_order(), ['choice_0', 'choice_4', 'choice_3', 'choice_2'])
def test_find_answer_text_textinput(self): problem = new_loncapa_problem( """ <problem> <stringresponse answer="hide" type="ci"> <textline size="40"/> </stringresponse> </problem> """ ) self.assertEquals(problem.find_answer_text('1_2_1', 'hide'), 'hide')
def test_targeted_feedback_student_answer2(self): problem = new_loncapa_problem(load_fixture('targeted_feedback.xml')) problem.done = True problem.student_answers = {'1_2_1': 'choice_0'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback1\">.*1st WRONG solution") self.assertRegexpMatches(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>") self.assertNotRegexpMatches(without_new_lines, r"feedback2|feedback3|feedbackC")
def test_find_question_label(self, answer_id, label, stripped_label): problem = new_loncapa_problem( '<problem><some-problem id="{}"/></problem>'.format(answer_id) ) mock_problem_data = { answer_id: { 'label': HTML(label) if label else '' } } with patch.object(problem, 'problem_data', mock_problem_data): self.assertEqual(problem.find_question_label(answer_id), stripped_label)
def test_targeted_feedback_multiple_answer_2(self): problem = new_loncapa_problem(load_fixture('targeted_feedback_multiple.xml')) problem.done = True problem.student_answers = {'1_2_1': 'choice_0', '1_3_1': 'choice_2'} # Q1 wrong, Q2 correct the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # Q1 has feedback1 and Q2 has feedbackC self.assertRegexpMatches( without_new_lines, r'<targetedfeedbackset.*?>.*?explanation-id="feedback1".*?</targetedfeedbackset>.*' + r'<targetedfeedbackset.*?>.*explanation-id="feedbackC".*?</targetedfeedbackset>' )
def test_targeted_feedback_correct_answer(self): """ Test the case of targeted feedback for a correct answer. """ problem = new_loncapa_problem(load_fixture('targeted_feedback.xml')) problem.done = True problem.student_answers = {'1_2_1': 'choice_2'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # pylint: disable=line-too-long self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedbackC\" role=\"group\" aria-describedby=\"1_2_1-legend\">\s*<span class=\"sr\">Correct</span>.*Feedback on your correct solution...") self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedback3")
def test_blank_problem(self): """ It's important that blank problems don't break, since that's what you start with in studio. """ xml_str = "<problem> </problem>" # Create the problem problem = new_loncapa_problem(xml_str) # Render the HTML etree.XML(problem.get_html())
def test_answer_pool_4_choices_1_multiplechoiceresponse_seed1(self): problem = new_loncapa_problem(self.common_question_xml, seed=723) the_html = problem.get_html() # [('choice_3', u'wrong-3'), ('choice_5', u'correct-2'), ('choice_1', u'wrong-2'), ('choice_4', u'wrong-4')] self.assertRegexpMatches(the_html, r"<div>.*\[.*'wrong-3'.*'correct-2'.*'wrong-2'.*'wrong-4'.*\].*</div>") self.assertRegexpMatches(the_html, r"<div>\{.*'1_solution_2'.*\}</div>") self.assertEqual(the_html, problem.get_html(), 'should be able to call get_html() twice') # Check about masking response = problem.responders.values()[0] self.assertFalse(response.has_mask()) self.assertTrue(response.has_answerpool()) self.assertEqual(response.unmask_order(), ['choice_3', 'choice_5', 'choice_1', 'choice_4'])
def test_targeted_feedback_student_answer1(self): problem = new_loncapa_problem(load_fixture('targeted_feedback.xml')) problem.done = True problem.student_answers = {'1_2_1': 'choice_3'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback3\">.*3rd WRONG solution") self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedbackC") # Check that calling it multiple times yields the same thing the_html2 = problem.get_html() self.assertEquals(the_html, the_html2)
def test_targeted_feedback_student_answer1(self): problem = new_loncapa_problem(load_fixture('targeted_feedback.xml')) problem.done = True problem.student_answers = {'1_2_1': 'choice_3'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # pylint: disable=line-too-long self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback3\" role=\"group\" aria-describedby=\"1_2_1-legend\">\s*<span class=\"sr\">Incorrect</span>.*3rd WRONG solution") self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedbackC") # Check that calling it multiple times yields the same thing the_html2 = problem.get_html() self.assertEquals(the_html, the_html2)
def test_invalid_answer_pool_value(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse> <choicegroup type="MultipleChoice" answer-pool="2.3"> <choice correct="false">wrong-1</choice> <choice correct="false">wrong-2</choice> <choice correct="true" explanation-id="solution1">correct-1</choice> <choice correct="false">wrong-3</choice> <choice correct="false">wrong-4</choice> <choice correct="true" explanation-id="solution2">correct-2</choice> </choicegroup> </multiplechoiceresponse> <solutionset> <solution explanation-id="solution1"> <div class="detailed-solution"> <p>Explanation</p> <p>This is the 1st solution</p> <p>Not much to explain here, sorry!</p> </div> </solution> <solution explanation-id="solution2"> <div class="detailed-solution"> <p>Explanation</p> <p>This is the 2nd solution</p> </div> </solution> </solutionset> </problem> """) with self.assertRaisesRegexp(LoncapaProblemError, "answer-pool"): new_loncapa_problem(xml_str)
def test_targeted_feedback_no_feedback_for_selected_choice2(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse targeted-feedback=""> <choicegroup type="MultipleChoice"> <choice correct="false" explanation-id="feedback1">wrong-1</choice> <choice correct="false" explanation-id="feedback2">wrong-2</choice> <choice correct="true" explanation-id="feedbackC">correct-1</choice> <choice correct="false" explanation-id="feedback3">wrong-3</choice> </choicegroup> </multiplechoiceresponse> <targetedfeedbackset> <targetedfeedback explanation-id="feedback1"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 1st WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback3"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 3rd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedbackC"> <div class="detailed-targeted-feedback-correct"> <p>Targeted Feedback</p> <p>Feedback on your correct solution...</p> </div> </targetedfeedback> </targetedfeedbackset> <solutionset> <solution explanation-id="feedbackC"> <div class="detailed-solution"> <p>Explanation</p> <p>This is the solution explanation</p> <p>Not much to explain here, sorry!</p> </div> </solution> </solutionset> </problem> """) # The student chooses one with no feedback set, so we check that there's no feedback. problem = new_loncapa_problem(xml_str) problem.done = True problem.student_answers = {'1_2_1': 'choice_1'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertNotRegex( without_new_lines, r"<targetedfeedback explanation-id=\"feedbackC\".*solution explanation" ) self.assertRegex(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>") self.assertNotRegex(without_new_lines, r"feedback1|feedback3|feedbackC")
def test_render_response_xml(self): # Generate some XML for a string response kwargs = { 'question_text': "Test question", 'explanation_text': "Test explanation", 'answer': 'Test answer', 'hints': [('test prompt', 'test_hint', 'test hint text')] } xml_str = StringResponseXMLFactory().build_xml(**kwargs) # Mock out the template renderer the_system = test_capa_system() the_system.render_template = mock.Mock() the_system.render_template.return_value = "<div class='input-template-render'>Input Template Render</div>" # Create the problem and render the HTML problem = new_loncapa_problem(xml_str, capa_system=the_system) rendered_html = etree.XML(problem.get_html()) # Expect problem has been turned into a <div> assert rendered_html.tag == 'div' # Expect that the response has been turned into a <div> with correct attributes response_element = rendered_html.find('div') assert response_element.tag == 'div' assert response_element.attrib['aria-label'] == 'Question 1' # Expect that the response div.wrapper-problem-response # that contains a <div> for the textline textline_element = response_element.find('div') assert textline_element.text == 'Input Template Render' # Expect a child <div> for the solution # with the rendered template solution_element = rendered_html.xpath('//div[@class="input-template-render"]')[0] assert solution_element.text == 'Input Template Render' # Expect that the template renderer was called with the correct # arguments, once for the textline input and once for # the solution expected_textline_context = { 'STATIC_URL': '/dummy-static/', 'status': the_system.STATUS_CLASS('unsubmitted'), 'value': '', 'preprocessor': None, 'msg': '', 'inline': False, 'hidden': False, 'do_math': False, 'id': '1_2_1', 'trailing_text': '', 'size': None, 'response_data': {'label': 'Test question', 'descriptions': {}}, 'describedby_html': HTML('aria-describedby="status_1_2_1"') } expected_solution_context = {'id': '1_solution_1'} expected_calls = [ mock.call('textline.html', expected_textline_context), mock.call('solutionspan.html', expected_solution_context), mock.call('textline.html', expected_textline_context), mock.call('solutionspan.html', expected_solution_context) ] assert the_system.render_template.call_args_list == expected_calls
def test_find_answer_test_not_implemented(self, current_answer): problem = new_loncapa_problem('<problem/>') self.assertRaises(NotImplementedError, problem.find_answer_text, '', current_answer)
def capa_problem(self, xml): """ Create capa problem. """ return new_loncapa_problem(xml, use_capa_render_template=True)
def test_targeted_feedback_id_typos(self): """Cases where the explanation-id's don't match anything.""" xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse targeted-feedback=""> <choicegroup type="MultipleChoice"> <choice correct="false" explanation-id="feedback1TYPO">wrong-1</choice> <choice correct="false" explanation-id="feedback2">wrong-2</choice> <choice correct="true" explanation-id="feedbackCTYPO">correct-1</choice> <choice correct="false" explanation-id="feedback3">wrong-3</choice> </choicegroup> </multiplechoiceresponse> <targetedfeedbackset> <targetedfeedback explanation-id="feedback1"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 1st WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback2"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 2nd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback3"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 3rd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedbackC"> <div class="detailed-targeted-feedback-correct"> <p>Targeted Feedback</p> <p>Feedback on your correct solution...</p> </div> </targetedfeedback> </targetedfeedbackset> <solution explanation-id="feedbackC"> <div class="detailed-solution"> <p>Explanation</p> <p>This is the solution explanation</p> <p>Not much to explain here, sorry!</p> </div> </solution> </problem> """) # explanation-id does not match anything: fall back to empty targetedfeedbackset problem = new_loncapa_problem(xml_str) problem.done = True problem.student_answers = {'1_2_1': 'choice_0'} the_html = problem.get_html() self.assertRegexpMatches(the_html, r"<targetedfeedbackset>\s*</targetedfeedbackset>") # New problem with same XML -- try the correct choice. problem = new_loncapa_problem(xml_str) problem.done = True problem.student_answers = {'1_2_1': 'choice_2'} # correct the_html = problem.get_html() self.assertRegexpMatches(the_html, r"<targetedfeedbackset>\s*</targetedfeedbackset>")
def test_no_targeted_feedback(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse> <choicegroup type="MultipleChoice"> <choice correct="false" explanation-id="feedback1">wrong-1</choice> <choice correct="false" explanation-id="feedback2">wrong-2</choice> <choice correct="true" explanation-id="feedbackC">correct-1</choice> <choice correct="false" explanation-id="feedback3">wrong-3</choice> </choicegroup> </multiplechoiceresponse> <targetedfeedbackset> <targetedfeedback explanation-id="feedback1"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 1st WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback2"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 2nd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback3"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 3rd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedbackC"> <div class="detailed-targeted-feedback-correct"> <p>Targeted Feedback</p> <p>Feedback on your correct solution...</p> </div> </targetedfeedback> </targetedfeedbackset> <solution explanation-id="feedbackC"> <div class="detailed-solution"> <p>Explanation</p> <p>This is the solution explanation</p> <p>Not much to explain here, sorry!</p> </div> </solution> </problem> """) problem = new_loncapa_problem(xml_str) the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertRegexpMatches(without_new_lines, r"<div>.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*</div>") self.assertRegexpMatches(without_new_lines, r"feedback1|feedback2|feedback3|feedbackC")
def test_targeted_feedback_with_solutionset_explanation(self): xml_str = textwrap.dedent(""" <problem> <p>What is the correct answer?</p> <multiplechoiceresponse targeted-feedback="alwaysShowCorrectChoiceExplanation"> <choicegroup type="MultipleChoice"> <choice correct="false" explanation-id="feedback1">wrong-1</choice> <choice correct="false" explanation-id="feedback2">wrong-2</choice> <choice correct="true" explanation-id="feedbackC">correct-1</choice> <choice correct="false" explanation-id="feedback3">wrong-3</choice> <choice correct="true" explanation-id="feedbackC2">correct-2</choice> </choicegroup> </multiplechoiceresponse> <targetedfeedbackset> <targetedfeedback explanation-id="feedback1"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 1st WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback2"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 2nd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedback3"> <div class="detailed-targeted-feedback"> <p>Targeted Feedback</p> <p>This is the 3rd WRONG solution</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedbackC"> <div class="detailed-targeted-feedback-correct"> <p>Targeted Feedback</p> <p>Feedback on your correct solution...</p> </div> </targetedfeedback> <targetedfeedback explanation-id="feedbackC2"> <div class="detailed-targeted-feedback-correct"> <p>Targeted Feedback</p> <p>Feedback on the other solution...</p> </div> </targetedfeedback> </targetedfeedbackset> <solutionset> <solution explanation-id="feedbackC2"> <div class="detailed-solution"> <p>Explanation</p> <p>This is the other solution explanation</p> <p>Not much to explain here, sorry!</p> </div> </solution> </solutionset> </problem> """) problem = new_loncapa_problem(xml_str) problem.done = True problem.student_answers = {'1_2_1': 'choice_0'} the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # pylint: disable=line-too-long self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback1\" role=\"group\" aria-describedby=\"1_2_1-legend\">.*1st WRONG solution") self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedbackC2\".*other solution explanation") self.assertNotRegexpMatches(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>") self.assertNotRegexpMatches(without_new_lines, r"feedback2|feedback3")