From f30c5f96b97a716dc9a2c0413901184d2dc54b0b Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Wed, 25 Sep 2024 11:14:08 -0700 Subject: [PATCH] fix: whitespace issues in some capa problem index_dictionary content --- xmodule/capa_block.py | 6 +++- xmodule/tests/test_capa_block.py | 57 +++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/xmodule/capa_block.py b/xmodule/capa_block.py index 54ca0cbc312f..c1c650144b05 100644 --- a/xmodule/capa_block.py +++ b/xmodule/capa_block.py @@ -616,11 +616,15 @@ def index_dictionary(self): "", capa_content ) + # Strip out all other tags, leaving their content. But we want spaces between adjacent tags, so that + #
Option A
Option B
+ # becomes "Option A Option B" not "Option AOption B" (these will appear in search results) + capa_content = re.sub(r"<([^>]+)>", r" <\2>", capa_content) capa_content = re.sub( r"(\s| |//)+", " ", nh3.clean(capa_content, tags=set()) - ) + ).strip() capa_body = { "capa_content": capa_content, diff --git a/xmodule/tests/test_capa_block.py b/xmodule/tests/test_capa_block.py index d1c01e109718..c81b137f1b23 100644 --- a/xmodule/tests/test_capa_block.py +++ b/xmodule/tests/test_capa_block.py @@ -3290,7 +3290,7 @@ def test_response_types_ignores_non_response_tags(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['multiplechoiceresponse'], - 'content': {'display_name': name, 'capa_content': ' Label Some comment Apple Banana Chocolate Donut '}} + 'content': {'display_name': name, 'capa_content': 'Label Some comment Apple Banana Chocolate Donut'}} def test_response_types_multiple_tags(self): xml = textwrap.dedent(""" @@ -3328,7 +3328,7 @@ def test_response_types_multiple_tags(self): 'problem_types': {"optionresponse", "multiplechoiceresponse"}, 'content': { 'display_name': name, - 'capa_content': " Label Some comment Donut Buggy '1','2' " + 'capa_content': "Label Some comment Donut Buggy '1','2'" }, } ) @@ -3369,7 +3369,7 @@ def test_solutions_not_indexed(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': [], - 'content': {'display_name': name, 'capa_content': ' '}} + 'content': {'display_name': name, 'capa_content': ''}} def test_indexing_checkboxes(self): name = "Checkboxes" @@ -3390,7 +3390,7 @@ def test_indexing_checkboxes(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['choiceresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_dropdown(self): name = "Dropdown" @@ -3405,7 +3405,7 @@ def test_indexing_dropdown(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['optionresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_multiple_choice(self): name = "Multiple Choice" @@ -3424,7 +3424,7 @@ def test_indexing_multiple_choice(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['multiplechoiceresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_numerical_input(self): name = "Numerical Input" @@ -3446,7 +3446,7 @@ def test_indexing_numerical_input(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['numericalresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_text_input(self): name = "Text Input" @@ -3465,7 +3465,7 @@ def test_indexing_text_input(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['stringresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_non_latin_problem(self): sample_text_input_problem_xml = textwrap.dedent(""" @@ -3476,7 +3476,7 @@ def test_indexing_non_latin_problem(self): """) name = "Non latin Input" block = self._create_block(sample_text_input_problem_xml, name=name) - capa_content = " Δοκιμή με μεταβλητές με Ελληνικούς χαρακτήρες μέσα σε python: $FX1_VAL " + capa_content = "Δοκιμή με μεταβλητές με Ελληνικούς χαρακτήρες μέσα σε python: $FX1_VAL" block_dict = block.index_dictionary() assert block_dict['content']['capa_content'] == smart_str(capa_content) @@ -3503,7 +3503,7 @@ def test_indexing_checkboxes_with_hints_and_feedback(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['choiceresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_dropdown_with_hints_and_feedback(self): name = "Dropdown with Hints and Feedback" @@ -3523,7 +3523,7 @@ def test_indexing_dropdown_with_hints_and_feedback(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['optionresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_multiple_choice_with_hints_and_feedback(self): name = "Multiple Choice with Hints and Feedback" @@ -3543,7 +3543,7 @@ def test_indexing_multiple_choice_with_hints_and_feedback(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['multiplechoiceresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_numerical_input_with_hints_and_feedback(self): name = "Numerical Input with Hints and Feedback" @@ -3561,7 +3561,7 @@ def test_indexing_numerical_input_with_hints_and_feedback(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['numericalresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_text_input_with_hints_and_feedback(self): name = "Text Input with Hints and Feedback" @@ -3579,7 +3579,7 @@ def test_indexing_text_input_with_hints_and_feedback(self): assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': ['stringresponse'], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ').strip()}} def test_indexing_problem_with_html_tags(self): sample_problem_xml = textwrap.dedent(""" @@ -3598,14 +3598,33 @@ def test_indexing_problem_with_html_tags(self): """) name = "Mixed business" block = self._create_block(sample_problem_xml, name=name) - capa_content = textwrap.dedent(""" - This has HTML comment in it. - HTML end. - """) + capa_content = "This has HTML comment in it. HTML end." assert block.index_dictionary() ==\ {'content_type': ProblemBlock.INDEX_CONTENT_TYPE, 'problem_types': [], - 'content': {'display_name': name, 'capa_content': capa_content.replace('\n', ' ')}} + 'content': {'display_name': name, 'capa_content': capa_content}} + + def test_indexing_problem_with_no_whitespace_between_tags(self): + """ + The new (MFE) visual editor for capa problems renders the OLX without spaces between the tags. + We want to make sure the index description is still readable and has whitespace. + """ + sample_problem_xml = ( + "" + "
Question text here.
" + "
Option A
" + "
Option B
" + "
" + "
" + ) + name = "No spaces" + block = self._create_block(sample_problem_xml, name=name) + capa_content = "Question text here. Option A Option B" + assert block.index_dictionary() == { + 'content_type': ProblemBlock.INDEX_CONTENT_TYPE, + 'problem_types': ['choiceresponse'], + 'content': {'display_name': name, 'capa_content': capa_content}, + } def test_invalid_xml_handling(self): """