diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index c200cf4fd6..8ee50b1c6a 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -937,12 +937,26 @@ class LoncapaProblem(object): if len(inputfields) > 1: response.set('multiple_inputtypes', 'true') group_label_tag = response.find('label') + group_description_tags = response.findall('description') + group_label_tag_id = u'multiinput-group-label-{}'.format(responsetype_id) group_label_tag_text = '' if group_label_tag is not None: group_label_tag.tag = 'p' - group_label_tag.set('id', responsetype_id) + group_label_tag.set('id', group_label_tag_id) group_label_tag.set('class', 'multi-inputs-group-label') group_label_tag_text = stringify_children(group_label_tag) + response.set('multiinput-group-label-id', group_label_tag_id) + + group_description_ids = [] + for index, group_description_tag in enumerate(group_description_tags): + group_description_tag_id = u'multiinput-group-description-{}-{}'.format(responsetype_id, index) + group_description_tag.tag = 'p' + group_description_tag.set('id', group_description_tag_id) + group_description_tag.set('class', 'multi-inputs-group-description question-description') + group_description_ids.append(group_description_tag_id) + + if group_description_ids: + response.set('multiinput-group_description_ids', ' '.join(group_description_ids)) for inputfield in inputfields: problem_data[inputfield.get('id')] = { diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 447a34e7c6..2587966ac3 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -272,7 +272,12 @@ class LoncapaResponse(object): content = etree.SubElement(tree, 'div') content.set('class', 'multi-inputs-group') content.set('role', 'group') - content.set('aria-labelledby', response_id) + + if self.xml.get('multiinput-group-label-id'): + content.set('aria-labelledby', self.xml.get('multiinput-group-label-id')) + + if self.xml.get('multiinput-group_description_ids'): + content.set('aria-describedby', self.xml.get('multiinput-group_description_ids')) else: content = tree diff --git a/common/lib/capa/capa/tests/test_capa_problem.py b/common/lib/capa/capa/tests/test_capa_problem.py index 9f2888180e..c914ce579f 100644 --- a/common/lib/capa/capa/tests/test_capa_problem.py +++ b/common/lib/capa/capa/tests/test_capa_problem.py @@ -468,6 +468,11 @@ class CAPAMultiInputProblemTest(unittest.TestCase): def assert_problem_html(self, problme_html, group_label, *input_labels): """ Verify that correct html is rendered for multiple inputtypes. + + Arguments: + problme_html (str): problem HTML + group_label (str or None): multi input group label or None if label is not present + input_labels (tuple): individual input labels """ html = etree.XML(problme_html) @@ -477,12 +482,16 @@ class CAPAMultiInputProblemTest(unittest.TestCase): ) self.assertEqual(len(multi_inputs_group), 1) - # verify that multi input group label

tag exists and its - # id matches with correct multi input group aria-labelledby - multi_inputs_group_label_id = multi_inputs_group[0].attrib.get('aria-labelledby') - multi_inputs_group_label = html.xpath('//p[@id="{}"]'.format(multi_inputs_group_label_id)) - self.assertEqual(len(multi_inputs_group_label), 1) - self.assertEqual(multi_inputs_group_label[0].text, group_label) + if group_label is None: + # if multi inputs group label is not present then there shouldn't be `aria-labelledby` attribute + self.assertEqual(multi_inputs_group[0].attrib.get('aria-labelledby'), None) + else: + # verify that multi input group label

tag exists and its + # id matches with correct multi input group aria-labelledby + multi_inputs_group_label_id = multi_inputs_group[0].attrib.get('aria-labelledby') + multi_inputs_group_label = html.xpath('//p[@id="{}"]'.format(multi_inputs_group_label_id)) + self.assertEqual(len(multi_inputs_group_label), 1) + self.assertEqual(multi_inputs_group_label[0].text, group_label) # verify that label for each input comes only once for input_label in input_labels: @@ -490,22 +499,26 @@ class CAPAMultiInputProblemTest(unittest.TestCase): input_label_element = multi_inputs_group[0].xpath('//*[normalize-space(text())="{}"]'.format(input_label)) self.assertEqual(len(input_label_element), 1) - def test_optionresponse(self): + @ddt.unpack + @ddt.data( + {'label_html': '', 'group_label': 'Choose the correct color'}, + {'label_html': '', 'group_label': None} + ) + def test_optionresponse(self, label_html, group_label): """ Verify that optionresponse problem with multiple inputtypes is rendered correctly. """ - group_label = 'Choose the correct color' input1_label = 'What color is the sky?' input2_label = 'What color are pine needles?' xml = """ - - - + {label_html} + + - """.format(group_label, input1_label, input2_label) + """.format(label_html=label_html, input1_label=input1_label, input2_label=input2_label) problem = self.capa_problem(xml) self.assert_problem_html(problem.get_html(), group_label, input1_label, input2_label) @@ -537,3 +550,43 @@ class CAPAMultiInputProblemTest(unittest.TestCase): """.format(group_label, input1_label, input2_label, inputtype=inputtype)) problem = self.capa_problem(xml) self.assert_problem_html(problem.get_html(), group_label, input1_label, input2_label) + + @ddt.unpack + @ddt.data( + { + 'descriptions': ('desc1', 'desc2'), + 'descriptions_html': 'desc1desc2' + }, + { + 'descriptions': (), + 'descriptions_html': '' + } + ) + def test_descriptions(self, descriptions, descriptions_html): + """ + Verify that groups descriptions are rendered correctly. + """ + xml = """ + + + + {descriptions_html} + + + + + """.format(descriptions_html=descriptions_html) + problem = self.capa_problem(xml) + problem_html = etree.XML(problem.get_html()) + + multi_inputs_group = problem_html.xpath('//div[@class="multi-inputs-group"]')[0] + description_ids = multi_inputs_group.attrib.get('aria-describedby', '').split() + + # Verify that number of descriptions matches description_ids + self.assertEqual(len(description_ids), len(descriptions)) + + # For each description, check its order and text is correct + for index, description_id in enumerate(description_ids): + description_element = multi_inputs_group.xpath('//p[@id="{}"]'.format(description_id)) + self.assertEqual(len(description_element), 1) + self.assertEqual(description_element[0].text, descriptions[index])