""" Tests the logic of the "targeted-feedback" attribute for MultipleChoice questions, i.e. those with the element """ import textwrap import unittest from xmodule.capa.tests.helpers import load_fixture, mock_capa_system, new_loncapa_problem class CapaTargetedFeedbackTest(unittest.TestCase): """ Testing class """ def setUp(self): super().setUp() self.system = mock_capa_system() def test_no_targeted_feedback(self): """Verify that no targeted feedback is shown when not finished.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 2nd WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Explanation

This is the solution explanation

Not much to explain here, sorry!

""" ) problem = new_loncapa_problem(xml_str) the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertRegex(without_new_lines, r"
.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*
") self.assertRegex(without_new_lines, r"feedback1|feedback2|feedback3|feedbackC") def test_targeted_feedback_not_finished(self): """Check HTML output when targeted feedback is incomplete.""" problem = new_loncapa_problem(load_fixture("targeted_feedback.xml")) the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") self.assertRegex(without_new_lines, r"
.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*
") self.assertNotRegex(without_new_lines, r"feedback1|feedback2|feedback3|feedbackC") assert the_html == problem.get_html(), "Should be able to call get_html() twice" def test_targeted_feedback_student_answer1(self): """Test targeted feedback rendering for a specific wrong student answer.""" 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", "").replace("\n", "") self.assertRegex( without_new_lines, ( r"\s*" r"Incorrect.*3rd WRONG solution" ), ) self.assertNotRegex(without_new_lines, r"feedback1|feedback2|feedbackC") # Check that calling it multiple times yields the same thing the_html2 = problem.get_html() assert the_html == the_html2 def test_targeted_feedback_student_answer2(self): """Test targeted feedback rendering for another wrong student answer.""" 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", "").replace("\n", "") self.assertRegex( without_new_lines, ( r"\s*" r"Incorrect.*1st WRONG solution" ), ) self.assertRegex(without_new_lines, r"
\{.*'1_solution_1'.*\}
") self.assertNotRegex(without_new_lines, r"feedback2|feedback3|feedbackC") 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", "").replace("\n", "") self.assertRegex( without_new_lines, ( r"\s*" r"Correct.*Feedback on your correct solution..." ), ) self.assertNotRegex(without_new_lines, r"feedback1|feedback2|feedback3") def test_targeted_feedback_id_typos(self): """Cases where the explanation-id's don't match anything.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 2nd WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Explanation

This is the solution explanation

Not much to explain here, sorry!

""" ) # 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.assertRegex(the_html, r"\s*") # 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.assertRegex(the_html, r"\s*") def test_targeted_feedback_no_solution_element(self): """Check behavior when the solution element is missing.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

""" ) # 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", "") # right after self.assertRegex(without_new_lines, r"
.*.*\s*
") def test_targeted_feedback_show_solution_explanation(self): """Verify that solution explanation is shown when configured to always show.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 2nd WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Explanation

This is the solution explanation

Not much to explain here, sorry!

""" ) 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", "") self.assertRegex( without_new_lines, ( r".*1st WRONG solution" ), ) self.assertRegex(without_new_lines, r"\{.*'1_solution_1'.*\}") self.assertNotRegex(without_new_lines, r"feedback2|feedback3") # Check that calling it multiple times yields the same thing the_html2 = problem.get_html() assert the_html == the_html2 def test_targeted_feedback_no_show_solution_explanation(self): """Verify that solution explanation is hidden when not configured to show.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 2nd WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Explanation

This is the solution explanation

Not much to explain here, sorry!

""" ) 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", "") self.assertRegex( without_new_lines, ( r".*1st WRONG solution" ), ) self.assertNotRegex(without_new_lines, r"\{.*'1_solution_1'.*\}") self.assertNotRegex(without_new_lines, r"feedback2|feedback3|feedbackC") def test_targeted_feedback_with_solutionset_explanation(self): """Test targeted feedback when multiple correct solutions exist.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3 correct-2

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 2nd WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Targeted Feedback

Feedback on the other solution...

Explanation

This is the other solution explanation

Not much to explain here, sorry!

""" ) 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", "") self.assertRegex( without_new_lines, ( r".*1st WRONG solution" ), ) self.assertRegex( without_new_lines, r"\{.*'1_solution_1'.*\}") self.assertNotRegex(without_new_lines, r"feedback2|feedback3") def test_targeted_feedback_no_feedback_for_selected_choice1(self): """Check behavior when selected choice has no feedback but correct solution should show.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Explanation

This is the solution explanation

Not much to explain here, sorry!

""" ) # The student choses one with no feedback, but alwaysShowCorrectChoiceExplanation # is in force, so we should see the correct solution 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.assertRegex(without_new_lines, r"\{.*'1_solution_1'.*\}") self.assertNotRegex(without_new_lines, r"feedback1|feedback3") def test_targeted_feedback_no_feedback_for_selected_choice2(self): """Check behavior when selected choice has no feedback and no solution explanation is shown.""" xml_str = textwrap.dedent( """

What is the correct answer?

wrong-1 wrong-2 correct-1 wrong-3

Targeted Feedback

This is the 1st WRONG solution

Targeted Feedback

This is the 3rd WRONG solution

Targeted Feedback

Feedback on your correct solution...

Explanation

This is the solution explanation

Not much to explain here, sorry!

""" ) # 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"\{.*'1_solution_1'.*\}") self.assertNotRegex(without_new_lines, r"feedback1|feedback3|feedbackC") def test_targeted_feedback_multiple_not_answered(self): """Ensure empty targeted feedback is rendered for unanswered multiple questions.""" # 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.assertRegex( without_new_lines, r"\s*.*\s*", ) def test_targeted_feedback_multiple_answer_1(self): """Test feedback rendering for the first question answered in a multi-question problem.""" problem = new_loncapa_problem(load_fixture("targeted_feedback_multiple.xml")) problem.done = True problem.student_answers = {"1_2_1": "choice_0"} # feedback1 the_html = problem.get_html() without_new_lines = the_html.replace("\n", "") # Q1 has feedback1 and Q2 has nothing self.assertRegex( without_new_lines, r'.*?explanation-id="feedback1".*?.*' + r"\s*", ) def test_targeted_feedback_multiple_answer_2(self): """Test feedback rendering for multiple answered questions in a multi-question problem.""" 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.assertRegex( without_new_lines, r'.*?explanation-id="feedback1".*?.*' + r'.*explanation-id="feedbackC".*?', )