1217 lines
46 KiB
Python
1217 lines
46 KiB
Python
"""
|
|
Tests for the logic in input type mako templates.
|
|
"""
|
|
|
|
|
|
import json
|
|
import unittest
|
|
from collections import OrderedDict
|
|
|
|
from lxml import etree
|
|
from mako import exceptions
|
|
from six.moves import range
|
|
|
|
from xmodule.capa.inputtypes import Status
|
|
from xmodule.capa.tests.helpers import capa_render_template
|
|
from openedx.core.djangolib.markup import HTML
|
|
from xmodule.stringify import stringify_children
|
|
|
|
|
|
class TemplateError(Exception):
|
|
"""
|
|
Error occurred while rendering a Mako template.
|
|
"""
|
|
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
|
|
|
|
|
class TemplateTestCase(unittest.TestCase):
|
|
"""
|
|
Utilities for testing templates.
|
|
"""
|
|
|
|
# Subclasses override this to specify the file name of the template
|
|
# to be loaded from capa/templates.
|
|
# The template name should include the .html extension:
|
|
# for example: choicegroup.html
|
|
TEMPLATE_NAME = None
|
|
DESCRIBEDBY = 'aria-describedby="desc-1 desc-2"'
|
|
DESCRIPTIONS = OrderedDict(
|
|
[
|
|
('desc-1', 'description text 1'),
|
|
('desc-2', '<em>description</em> <mark>text</mark> 2')
|
|
]
|
|
)
|
|
DESCRIPTION_IDS = ' '.join(list(DESCRIPTIONS.keys()))
|
|
RESPONSE_DATA = {
|
|
'label': 'question text 101',
|
|
'descriptions': DESCRIPTIONS
|
|
}
|
|
|
|
def setUp(self):
|
|
"""
|
|
Initialize the context.
|
|
"""
|
|
super(TemplateTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {}
|
|
|
|
def render_to_xml(self, context_dict):
|
|
"""
|
|
Render the template using the `context_dict` dict.
|
|
Returns an `etree` XML element.
|
|
"""
|
|
# add dummy STATIC_URL to template context
|
|
context_dict.setdefault("STATIC_URL", "/dummy-static/")
|
|
try:
|
|
xml_str = capa_render_template(self.TEMPLATE_NAME, context_dict)
|
|
except:
|
|
raise TemplateError(exceptions.text_error_template().render()) # lint-amnesty, pylint: disable=raise-missing-from
|
|
|
|
# Attempt to construct an XML tree from the template
|
|
# This makes it easy to use XPath to make assertions, rather
|
|
# than dealing with a string.
|
|
# We modify the string slightly by wrapping it in <test>
|
|
# tags, to ensure it has one root element.
|
|
try:
|
|
xml = etree.fromstring("<test>" + xml_str + "</test>")
|
|
except Exception as exc:
|
|
raise TemplateError("Could not parse XML from '{0}': {1}".format( # lint-amnesty, pylint: disable=raise-missing-from
|
|
xml_str, str(exc)))
|
|
return xml
|
|
|
|
def assert_has_xpath(self, xml_root, xpath, context_dict, exact_num=1):
|
|
"""
|
|
Asserts that the xml tree has an element satisfying `xpath`.
|
|
|
|
`xml_root` is an etree XML element
|
|
`xpath` is an XPath string, such as `'/foo/bar'`
|
|
`context` is used to print a debugging message
|
|
`exact_num` is the exact number of matches to expect.
|
|
"""
|
|
message = ("XML does not have %d match(es) for xpath '%s'\nXML: %s\nContext: %s"
|
|
% (exact_num, str(xpath), etree.tostring(xml_root), str(context_dict)))
|
|
|
|
assert len(xml_root.xpath(xpath)) == exact_num, message
|
|
|
|
def assert_no_xpath(self, xml_root, xpath, context_dict):
|
|
"""
|
|
Asserts that the xml tree does NOT have an element
|
|
satisfying `xpath`.
|
|
|
|
`xml_root` is an etree XML element
|
|
`xpath` is an XPath string, such as `'/foo/bar'`
|
|
`context` is used to print a debugging message
|
|
"""
|
|
self.assert_has_xpath(xml_root, xpath, context_dict, exact_num=0)
|
|
|
|
def assert_has_text(self, xml_root, xpath, text, exact=True):
|
|
"""
|
|
Find the element at `xpath` in `xml_root` and assert
|
|
that its text is `text`.
|
|
|
|
`xml_root` is an etree XML element
|
|
`xpath` is an XPath string, such as `'/foo/bar'`
|
|
`text` is the expected text that the element should contain
|
|
|
|
If multiple elements are found, checks the first one.
|
|
If no elements are found, the assertion fails.
|
|
"""
|
|
element_list = xml_root.xpath(xpath)
|
|
assert len(element_list) > 0, ("Could not find element at '%s'\n%s" % (str(xpath), etree.tostring(xml_root)))
|
|
if exact:
|
|
assert text == element_list[0].text.strip()
|
|
else:
|
|
assert text in element_list[0].text.strip()
|
|
|
|
def assert_description(self, describedby_xpaths):
|
|
"""
|
|
Verify that descriptions information is correct.
|
|
|
|
Arguments:
|
|
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
|
|
"""
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Verify that each description <p> tag has correct id, text and order
|
|
descriptions = OrderedDict(
|
|
(tag.get('id'), stringify_children(tag)) for tag in xml.xpath('//p[@class="question-description"]')
|
|
)
|
|
assert self.DESCRIPTIONS == descriptions
|
|
|
|
# for each xpath verify that description_ids are set correctly
|
|
for describedby_xpath in describedby_xpaths:
|
|
describedbys = xml.xpath(describedby_xpath)
|
|
|
|
# aria-describedby attributes must have ids
|
|
assert describedbys
|
|
|
|
for describedby in describedbys:
|
|
assert describedby == self.DESCRIPTION_IDS
|
|
|
|
def assert_describedby_attribute(self, describedby_xpaths):
|
|
"""
|
|
Verify that an element has no aria-describedby attribute if there are no descriptions.
|
|
|
|
Arguments:
|
|
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
|
|
"""
|
|
self.context['describedby_html'] = ''
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# for each xpath verify that description_ids are set correctly
|
|
for describedby_xpath in describedby_xpaths:
|
|
describedbys = xml.xpath(describedby_xpath)
|
|
assert not describedbys
|
|
|
|
def assert_status(self, status_div=False, status_class=False):
|
|
"""
|
|
Verify status information.
|
|
|
|
Arguments:
|
|
status_div (bool): check presence of status div
|
|
status_class (bool): check presence of status class
|
|
"""
|
|
cases = [
|
|
('correct', 'correct'),
|
|
('unsubmitted', 'unanswered'),
|
|
('submitted', 'submitted'),
|
|
('incorrect', 'incorrect'),
|
|
('incomplete', 'incorrect')
|
|
]
|
|
|
|
for context_status, div_class in cases:
|
|
self.context['status'] = Status(context_status)
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Expect that we get a <div> with correct class
|
|
if status_div:
|
|
xpath = "//div[normalize-space(@class)='%s']" % div_class
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Expect that we get a <span> with class="status"
|
|
# (used to by CSS to draw the green check / red x)
|
|
self.assert_has_text(
|
|
xml,
|
|
"//span[@class='status {}']/span[@class='sr']".format(
|
|
div_class if status_class else ''
|
|
),
|
|
self.context['status'].display_name
|
|
)
|
|
|
|
def assert_label(self, xpath=None, aria_label=False):
|
|
"""
|
|
Verify label is rendered correctly.
|
|
|
|
Arguments:
|
|
xpath (str): xpath expression for label element
|
|
aria_label (bool): check aria-label attribute value
|
|
"""
|
|
labels = [
|
|
{
|
|
'actual': "You see, but you do not observe. The distinction is clear.",
|
|
'expected': "You see, but you do not observe. The distinction is clear.",
|
|
},
|
|
{
|
|
'actual': "I choose to have <mark>faith</mark> because without that, I have <em>nothing</em>.",
|
|
'expected': "I choose to have faith because without that, I have nothing.",
|
|
}
|
|
]
|
|
|
|
response_data = {
|
|
'response_data': {
|
|
'descriptions': {},
|
|
'label': ''
|
|
}
|
|
}
|
|
self.context.update(response_data)
|
|
|
|
for label in labels:
|
|
self.context['response_data']['label'] = label['actual']
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
if aria_label:
|
|
self.assert_has_xpath(xml, "//*[@aria-label='%s']" % label['expected'], self.context)
|
|
else:
|
|
element_list = xml.xpath(xpath)
|
|
assert len(element_list) == 1
|
|
assert stringify_children(element_list[0]) == label['actual']
|
|
|
|
|
|
class ChoiceGroupTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<choicegroup>` input.
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'choicegroup.html'
|
|
|
|
def setUp(self):
|
|
super(ChoiceGroupTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
choices = [('1', 'choice 1'), ('2', 'choice 2'), ('3', 'choice 3')]
|
|
self.context = {
|
|
'id': '1',
|
|
'choices': choices,
|
|
'status': Status('correct'),
|
|
'input_type': 'checkbox',
|
|
'name_array_suffix': '1',
|
|
'value': '3',
|
|
'response_data': self.RESPONSE_DATA,
|
|
'describedby_html': HTML(self.DESCRIBEDBY),
|
|
}
|
|
|
|
def test_problem_marked_correct(self):
|
|
"""
|
|
Test conditions under which the entire problem
|
|
(not a particular option) is marked correct.
|
|
"""
|
|
|
|
self.context['status'] = Status('correct')
|
|
self.context['input_type'] = 'checkbox'
|
|
self.context['value'] = ['1', '2']
|
|
|
|
# Should mark the entire problem correct
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//div[@class='indicator-container']/span[@class='status correct']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
self.assert_no_xpath(xml, "//label[@class='choicegroup_incorrect']",
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml, "//label[@class='choicegroup_correct']",
|
|
self.context)
|
|
|
|
def test_problem_marked_incorrect(self):
|
|
"""
|
|
Test all conditions under which the entire problem
|
|
(not a particular option) is marked incorrect.
|
|
"""
|
|
conditions = [
|
|
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': []},
|
|
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': ['2']},
|
|
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': ['2', '3']},
|
|
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': []},
|
|
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': ['2']},
|
|
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': ['2', '3']}]
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
self.assert_no_xpath(xml,
|
|
"//label[@class='choicegroup_incorrect']",
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml,
|
|
"//label[@class='choicegroup_correct']",
|
|
self.context)
|
|
|
|
def test_problem_marked_unsubmitted(self):
|
|
"""
|
|
Test all conditions under which the entire problem
|
|
(not a particular option) is marked unanswered.
|
|
"""
|
|
conditions = [
|
|
{'status': Status('unsubmitted'), 'input_type': 'radio', 'value': ''},
|
|
{'status': Status('unsubmitted'), 'input_type': 'radio', 'value': []},
|
|
{'status': Status('unsubmitted'), 'input_type': 'checkbox', 'value': []},
|
|
{'input_type': 'radio', 'value': ''},
|
|
{'input_type': 'radio', 'value': []},
|
|
{'input_type': 'checkbox', 'value': []},
|
|
{'input_type': 'checkbox', 'value': ['1']},
|
|
{'input_type': 'checkbox', 'value': ['1', '2']}]
|
|
|
|
self.context['status'] = Status('unanswered')
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//div[@class='indicator-container']/span[@class='status unanswered']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
self.assert_no_xpath(xml,
|
|
"//label[@class='choicegroup_incorrect']",
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml,
|
|
"//label[@class='choicegroup_correct']",
|
|
self.context)
|
|
|
|
def test_option_marked_correct(self):
|
|
"""
|
|
Test conditions under which a particular option
|
|
and the entire problem is marked correct.
|
|
"""
|
|
conditions = [
|
|
{'input_type': 'radio', 'value': '2'},
|
|
{'input_type': 'radio', 'value': ['2']}]
|
|
|
|
self.context['status'] = Status('correct')
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//label[contains(@class, 'choicegroup_correct')]"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should also mark the whole problem
|
|
xpath = "//div[@class='indicator-container']/span[@class='status correct']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_option_marked_incorrect(self):
|
|
"""
|
|
Test conditions under which a particular option
|
|
and the entire problem is marked incorrect.
|
|
"""
|
|
conditions = [
|
|
{'input_type': 'radio', 'value': '2'},
|
|
{'input_type': 'radio', 'value': ['2']}]
|
|
|
|
self.context['status'] = Status('incorrect')
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//label[contains(@class, 'choicegroup_incorrect')]"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should also mark the whole problem
|
|
xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_never_show_correctness(self):
|
|
"""
|
|
Test conditions under which we tell the template to
|
|
NOT show correct/incorrect, but instead show a message.
|
|
|
|
This is used, for example, by the Justice course to ask
|
|
questions without specifying a correct answer. When
|
|
the student responds, the problem displays "Thank you
|
|
for your response"
|
|
"""
|
|
|
|
conditions = [
|
|
{'input_type': 'radio', 'status': Status('correct'), 'value': ''},
|
|
{'input_type': 'radio', 'status': Status('correct'), 'value': '2'},
|
|
{'input_type': 'radio', 'status': Status('correct'), 'value': ['2']},
|
|
{'input_type': 'radio', 'status': Status('incorrect'), 'value': '2'},
|
|
{'input_type': 'radio', 'status': Status('incorrect'), 'value': []},
|
|
{'input_type': 'radio', 'status': Status('incorrect'), 'value': ['2']},
|
|
{'input_type': 'checkbox', 'status': Status('correct'), 'value': []},
|
|
{'input_type': 'checkbox', 'status': Status('correct'), 'value': ['2']},
|
|
{'input_type': 'checkbox', 'status': Status('incorrect'), 'value': []},
|
|
{'input_type': 'checkbox', 'status': Status('incorrect'), 'value': ['2']}]
|
|
|
|
self.context['show_correctness'] = 'never'
|
|
self.context['submitted_message'] = 'Test message'
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Should NOT mark the entire problem correct/incorrect
|
|
xpath = "//div[@class='indicator-container']/span[@class='status correct']"
|
|
self.assert_no_xpath(xml, xpath, self.context)
|
|
|
|
xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
|
|
self.assert_no_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
self.assert_no_xpath(xml,
|
|
"//label[@class='choicegroup_incorrect']",
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml,
|
|
"//label[@class='choicegroup_correct']",
|
|
self.context)
|
|
|
|
# Expect to see the message
|
|
self.assert_has_text(xml, "//div[@class='capa_alert']",
|
|
self.context['submitted_message'])
|
|
|
|
def test_no_message_before_submission(self):
|
|
"""
|
|
Ensure that we don't show the `submitted_message`
|
|
before submitting.
|
|
"""
|
|
|
|
conditions = [
|
|
{'input_type': 'radio', 'status': Status('unsubmitted'), 'value': ''},
|
|
{'input_type': 'radio', 'status': Status('unsubmitted'), 'value': []},
|
|
{'input_type': 'checkbox', 'status': Status('unsubmitted'), 'value': []},
|
|
|
|
# These tests expose bug #365
|
|
# When the bug is fixed, uncomment these cases.
|
|
#{'input_type': 'radio', 'status': 'unsubmitted', 'value': '2'},
|
|
#{'input_type': 'radio', 'status': 'unsubmitted', 'value': ['2']},
|
|
#{'input_type': 'radio', 'status': 'unsubmitted', 'value': '2'},
|
|
#{'input_type': 'radio', 'status': 'unsubmitted', 'value': ['2']},
|
|
#{'input_type': 'checkbox', 'status': 'unsubmitted', 'value': ['2']},
|
|
#{'input_type': 'checkbox', 'status': 'unsubmitted', 'value': ['2']}]
|
|
]
|
|
|
|
self.context['show_correctness'] = 'never'
|
|
self.context['submitted_message'] = 'Test message'
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Expect that we do NOT see the message yet
|
|
self.assert_no_xpath(xml, "//div[@class='capa_alert']", self.context)
|
|
|
|
def test_label(self):
|
|
"""
|
|
Verify label element value rendering.
|
|
"""
|
|
self.assert_label(xpath="//legend")
|
|
|
|
def test_description(self):
|
|
"""
|
|
Test that correct description information is set on desired elements.
|
|
"""
|
|
xpaths = ['//fieldset/@aria-describedby', '//label/@aria-describedby']
|
|
self.assert_description(xpaths)
|
|
self.assert_describedby_attribute(xpaths)
|
|
|
|
def test_status(self):
|
|
"""
|
|
Verify status information.
|
|
"""
|
|
self.assert_status(status_class=True)
|
|
|
|
|
|
class TextlineTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<textline>` input.
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'textline.html'
|
|
|
|
def setUp(self):
|
|
super(TextlineTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': '1',
|
|
'status': Status('correct'),
|
|
'value': '3',
|
|
'preprocessor': None,
|
|
'trailing_text': None,
|
|
'response_data': self.RESPONSE_DATA,
|
|
'describedby_html': HTML(self.DESCRIBEDBY),
|
|
}
|
|
|
|
def test_section_class(self):
|
|
cases = [({}, ' capa_inputtype textline'),
|
|
({'do_math': True}, 'text-input-dynamath capa_inputtype textline'),
|
|
({'inline': True}, ' capa_inputtype inline textline'),
|
|
({'do_math': True, 'inline': True}, 'text-input-dynamath capa_inputtype inline textline'), ]
|
|
|
|
for (context, css_class) in cases:
|
|
base_context = self.context.copy()
|
|
base_context.update(context)
|
|
xml = self.render_to_xml(base_context)
|
|
xpath = "//div[@class='%s']" % css_class
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_status(self):
|
|
"""
|
|
Verify status information.
|
|
"""
|
|
self.assert_status(status_class=True)
|
|
|
|
def test_label(self):
|
|
"""
|
|
Verify label element value rendering.
|
|
"""
|
|
self.assert_label(xpath="//label[@class='problem-group-label']")
|
|
|
|
def test_hidden(self):
|
|
self.context['hidden'] = True
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//div[@style='display:none;']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
xpath = "//input[@style='display:none;']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_do_math(self):
|
|
self.context['do_math'] = True
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//input[@class='mw-100 math']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
xpath = "//div[@class='equation']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
xpath = "//textarea[@id='input_1_dynamath']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_size(self):
|
|
self.context['size'] = '20'
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//input[@size='20']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_preprocessor(self):
|
|
self.context['preprocessor'] = {'class_name': 'test_class',
|
|
'script_src': 'test_script'}
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//div[contains(@class, 'text-input-dynamath_data') and @data-preprocessor='test_class']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
xpath = "//div[@class='script_placeholder' and @data-src='test_script']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_do_inline_and_preprocessor(self):
|
|
self.context['preprocessor'] = {'class_name': 'test_class',
|
|
'script_src': 'test_script'}
|
|
self.context['inline'] = True
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//div[contains(@class, 'text-input-dynamath_data inline') and @data-preprocessor='test_class']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_do_inline(self):
|
|
cases = [('correct', 'correct'),
|
|
('unsubmitted', 'unanswered'),
|
|
('incorrect', 'incorrect'),
|
|
('incomplete', 'incorrect')]
|
|
|
|
self.context['inline'] = True
|
|
|
|
for (context_status, div_class) in cases:
|
|
self.context['status'] = Status(context_status)
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Expect that we get a <div> with correct class
|
|
xpath = "//div[@class='%s inline']" % div_class
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_message(self):
|
|
self.context['msg'] = "Test message"
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//span[@class='message']"
|
|
self.assert_has_text(xml, xpath, self.context['msg'])
|
|
|
|
def test_description(self):
|
|
"""
|
|
Test that correct description information is set on desired elements.
|
|
"""
|
|
xpaths = ['//input/@aria-describedby']
|
|
self.assert_description(xpaths)
|
|
self.assert_describedby_attribute(xpaths)
|
|
|
|
|
|
class FormulaEquationInputTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test make template for `<formulaequationinput>`s.
|
|
"""
|
|
TEMPLATE_NAME = 'formulaequationinput.html'
|
|
|
|
def setUp(self):
|
|
super(FormulaEquationInputTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': 2,
|
|
'value': 'PREFILLED_VALUE',
|
|
'status': Status('unsubmitted'),
|
|
'previewer': 'file.js',
|
|
'reported_status': 'REPORTED_STATUS',
|
|
'trailing_text': None,
|
|
'response_data': self.RESPONSE_DATA,
|
|
'describedby_html': HTML(self.DESCRIBEDBY),
|
|
}
|
|
|
|
def test_no_size(self):
|
|
xml = self.render_to_xml(self.context)
|
|
self.assert_no_xpath(xml, "//input[@size]", self.context)
|
|
|
|
def test_size(self):
|
|
self.context['size'] = '40'
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
self.assert_has_xpath(xml, "//input[@size='40']", self.context)
|
|
|
|
def test_description(self):
|
|
"""
|
|
Test that correct description information is set on desired elements.
|
|
"""
|
|
xpaths = ['//input/@aria-describedby']
|
|
self.assert_description(xpaths)
|
|
self.assert_describedby_attribute(xpaths)
|
|
|
|
def test_status(self):
|
|
"""
|
|
Verify status information.
|
|
"""
|
|
self.assert_status(status_class=True)
|
|
|
|
def test_label(self):
|
|
"""
|
|
Verify label element value rendering.
|
|
"""
|
|
self.assert_label(xpath="//label[@class='problem-group-label']")
|
|
|
|
|
|
class AnnotationInputTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<annotationinput>` input.
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'annotationinput.html'
|
|
|
|
def setUp(self):
|
|
super(AnnotationInputTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': 2,
|
|
'value': '<p>Test value</p>',
|
|
'title': '<h1>This is a title</h1>',
|
|
'text': '<p><b>This</b> is a test.</p>',
|
|
'comment': '<p>This is a test comment</p>',
|
|
'comment_prompt': '<p>This is a test comment prompt</p>',
|
|
'comment_value': '<p>This is the value of a test comment</p>',
|
|
'tag_prompt': '<p>This is a tag prompt</p>',
|
|
'options': [],
|
|
'has_options_value': False,
|
|
'debug': False,
|
|
'status': Status('unsubmitted'),
|
|
'return_to_annotation': False,
|
|
'msg': '<p>This is a test message</p>',
|
|
}
|
|
|
|
def test_return_to_annotation(self):
|
|
"""
|
|
Test link for `Return to Annotation` appears if and only if
|
|
the flag is set.
|
|
"""
|
|
|
|
xpath = "//a[@class='annotation-return']"
|
|
|
|
# If return_to_annotation set, then show the link
|
|
self.context['return_to_annotation'] = True
|
|
xml = self.render_to_xml(self.context)
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Otherwise, do not show the links
|
|
self.context['return_to_annotation'] = False
|
|
xml = self.render_to_xml(self.context)
|
|
self.assert_no_xpath(xml, xpath, self.context)
|
|
|
|
def test_option_selection(self):
|
|
"""
|
|
Test that selected options are selected.
|
|
"""
|
|
|
|
# Create options 0-4 and select option 2
|
|
self.context['options_value'] = [2]
|
|
self.context['options'] = [
|
|
{'id': id_num,
|
|
'choice': 'correct',
|
|
'description': '<p>Unescaped <b>HTML {0}</b></p>'.format(id_num)}
|
|
for id_num in range(5)]
|
|
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Expect that each option description is visible
|
|
# with unescaped HTML.
|
|
# Since the HTML is unescaped, we can traverse the XML tree
|
|
for id_num in range(5):
|
|
xpath = "//span[@data-id='{0}']/p/b".format(id_num)
|
|
self.assert_has_text(xml, xpath, 'HTML {0}'.format(id_num), exact=False)
|
|
|
|
# Expect that the correct option is selected
|
|
xpath = "//span[contains(@class,'selected')]/p/b"
|
|
self.assert_has_text(xml, xpath, 'HTML 2', exact=False)
|
|
|
|
def test_submission_status(self):
|
|
"""
|
|
Test that the submission status displays correctly.
|
|
"""
|
|
|
|
# Test cases of `(input_status, expected_css_class)` tuples
|
|
test_cases = [('unsubmitted', 'unanswered'),
|
|
('incomplete', 'incorrect'),
|
|
('incorrect', 'incorrect')]
|
|
|
|
for (input_status, expected_css_class) in test_cases:
|
|
self.context['status'] = Status(input_status)
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
xpath = "//span[@class='status {0}']".format(expected_css_class)
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# If individual options are being marked, then expect
|
|
# just the option to be marked incorrect, not the whole problem
|
|
self.context['has_options_value'] = True
|
|
self.context['status'] = Status('incorrect')
|
|
xpath = "//span[@class='incorrect']"
|
|
xml = self.render_to_xml(self.context)
|
|
self.assert_no_xpath(xml, xpath, self.context)
|
|
|
|
def test_display_html_comment(self):
|
|
"""
|
|
Test that HTML comment and comment prompt render.
|
|
"""
|
|
self.context['comment'] = "<p>Unescaped <b>comment HTML</b></p>"
|
|
self.context['comment_prompt'] = "<p>Prompt <b>prompt HTML</b></p>"
|
|
self.context['text'] = "<p>Unescaped <b>text</b></p>"
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Because the HTML is unescaped, we should be able to
|
|
# descend to the <b> tag
|
|
xpath = "//div[@class='block']/p/b"
|
|
self.assert_has_text(xml, xpath, 'prompt HTML')
|
|
|
|
xpath = "//div[@class='block block-comment']/p/b"
|
|
self.assert_has_text(xml, xpath, 'comment HTML')
|
|
|
|
xpath = "//div[@class='block block-highlight']/p/b"
|
|
self.assert_has_text(xml, xpath, 'text')
|
|
|
|
def test_display_html_tag_prompt(self):
|
|
"""
|
|
Test that HTML tag prompts render.
|
|
"""
|
|
self.context['tag_prompt'] = "<p>Unescaped <b>HTML</b></p>"
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Because the HTML is unescaped, we should be able to
|
|
# descend to the <b> tag
|
|
xpath = "//div[@class='block']/p/b"
|
|
self.assert_has_text(xml, xpath, 'HTML')
|
|
|
|
|
|
class MathStringTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<mathstring>` input.
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'mathstring.html'
|
|
|
|
def setUp(self):
|
|
super(MathStringTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {'isinline': False, 'mathstr': '', 'tail': ''}
|
|
|
|
def test_math_string_inline(self):
|
|
self.context['isinline'] = True
|
|
self.context['mathstr'] = 'y = ax^2 + bx + c'
|
|
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//section[@class='math-string']/span[1]"
|
|
self.assert_has_text(xml, xpath,
|
|
'[mathjaxinline]y = ax^2 + bx + c[/mathjaxinline]')
|
|
|
|
def test_math_string_not_inline(self):
|
|
self.context['isinline'] = False
|
|
self.context['mathstr'] = 'y = ax^2 + bx + c'
|
|
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//section[@class='math-string']/span[1]"
|
|
self.assert_has_text(xml, xpath,
|
|
'[mathjax]y = ax^2 + bx + c[/mathjax]')
|
|
|
|
def test_tail_html(self):
|
|
self.context['tail'] = "<p>This is some <b>tail</b> <em>HTML</em></p>"
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# HTML from `tail` should NOT be escaped.
|
|
# We should be able to traverse it as part of the XML tree
|
|
xpath = "//section[@class='math-string']/span[2]/p/b"
|
|
self.assert_has_text(xml, xpath, 'tail')
|
|
|
|
xpath = "//section[@class='math-string']/span[2]/p/em"
|
|
self.assert_has_text(xml, xpath, 'HTML')
|
|
|
|
|
|
class OptionInputTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<optioninput>` input.
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'optioninput.html'
|
|
|
|
def setUp(self):
|
|
super(OptionInputTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': 2,
|
|
'options': [],
|
|
'status': Status('unsubmitted'),
|
|
'value': 0,
|
|
'default_option_text': 'Select an option',
|
|
'response_data': self.RESPONSE_DATA,
|
|
'describedby_html': HTML(self.DESCRIBEDBY),
|
|
}
|
|
|
|
def test_select_options(self):
|
|
|
|
# Create options 0-4, and select option 2
|
|
self.context['options'] = [(id_num, 'Option {0}'.format(id_num))
|
|
for id_num in range(5)]
|
|
self.context['value'] = 2
|
|
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Should have a dummy default
|
|
xpath = "//option[@value='option_2_dummy_default']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
for id_num in range(5):
|
|
xpath = "//option[@value='{0}']".format(id_num)
|
|
self.assert_has_text(xml, xpath, 'Option {0}'.format(id_num))
|
|
|
|
# Should have the correct option selected
|
|
xpath = "//option[@selected='true']"
|
|
self.assert_has_text(xml, xpath, 'Option 2')
|
|
|
|
def test_status(self):
|
|
"""
|
|
Verify status information.
|
|
"""
|
|
self.assert_status(status_class=True)
|
|
|
|
def test_label(self):
|
|
"""
|
|
Verify label element value rendering.
|
|
"""
|
|
self.assert_label(xpath="//label[@class='problem-group-label']")
|
|
|
|
def test_description(self):
|
|
"""
|
|
Test that correct description information is set on desired elements.
|
|
"""
|
|
xpaths = ['//select/@aria-describedby']
|
|
self.assert_description(xpaths)
|
|
self.assert_describedby_attribute(xpaths)
|
|
|
|
|
|
class DragAndDropTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<draganddropinput>` input.
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'drag_and_drop_input.html'
|
|
|
|
def setUp(self):
|
|
super(DragAndDropTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {'id': 2,
|
|
'drag_and_drop_json': '',
|
|
'value': 0,
|
|
'status': Status('unsubmitted'),
|
|
'msg': ''}
|
|
|
|
def test_status(self):
|
|
|
|
# Test cases, where each tuple represents
|
|
# `(input_status, expected_css_class, expected_text)`
|
|
test_cases = [('unsubmitted', 'unanswered', 'unanswered'),
|
|
('correct', 'correct', 'correct'),
|
|
('incorrect', 'incorrect', 'incorrect'),
|
|
('incomplete', 'incorrect', 'incomplete')]
|
|
|
|
for (input_status, expected_css_class, expected_text) in test_cases:
|
|
self.context['status'] = Status(input_status)
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Expect a <div> with the status
|
|
xpath = "//div[@class='{0}']".format(expected_css_class)
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Expect a <span> with the status
|
|
xpath = "//span[@class='status {0}']/span[@class='sr']".format(expected_css_class)
|
|
self.assert_has_text(xml, xpath, expected_text, exact=False)
|
|
|
|
def test_drag_and_drop_json_html(self):
|
|
|
|
json_with_html = json.dumps({'test': '<p>Unescaped <b>HTML</b></p>'})
|
|
self.context['drag_and_drop_json'] = json_with_html
|
|
xml = self.render_to_xml(self.context)
|
|
|
|
# Assert that the JSON-encoded string was inserted without
|
|
# escaping the HTML. We should be able to traverse the XML tree.
|
|
xpath = "//div[@class='drag_and_drop_problem_json']/p/b"
|
|
self.assert_has_text(xml, xpath, 'HTML')
|
|
|
|
|
|
class ChoiceTextGroupTemplateTest(TemplateTestCase):
|
|
"""Test mako template for `<choicetextgroup>` input"""
|
|
|
|
TEMPLATE_NAME = 'choicetext.html'
|
|
VALUE_DICT = {'1_choiceinput_0bc': '1_choiceinput_0bc', '1_choiceinput_0_textinput_0': '0',
|
|
'1_choiceinput_1_textinput_0': '0'}
|
|
EMPTY_DICT = {'1_choiceinput_0_textinput_0': '',
|
|
'1_choiceinput_1_textinput_0': ''}
|
|
BOTH_CHOICE_CHECKBOX = {'1_choiceinput_0bc': 'choiceinput_0',
|
|
'1_choiceinput_1bc': 'choiceinput_1',
|
|
'1_choiceinput_0_textinput_0': '0',
|
|
'1_choiceinput_1_textinput_0': '0'}
|
|
WRONG_CHOICE_CHECKBOX = {'1_choiceinput_1bc': 'choiceinput_1',
|
|
'1_choiceinput_0_textinput_0': '0',
|
|
'1_choiceinput_1_textinput_0': '0'}
|
|
|
|
def setUp(self):
|
|
super(ChoiceTextGroupTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
choices = [
|
|
(
|
|
'1_choiceinput_0bc',
|
|
[
|
|
{'tail_text': '', 'type': 'text', 'value': '', 'contents': ''},
|
|
{'tail_text': '', 'type': 'textinput', 'value': '', 'contents': 'choiceinput_0_textinput_0'},
|
|
]
|
|
),
|
|
(
|
|
'1_choiceinput_1bc',
|
|
[
|
|
{'tail_text': '', 'type': 'text', 'value': '', 'contents': ''},
|
|
{'tail_text': '', 'type': 'textinput', 'value': '', 'contents': 'choiceinput_1_textinput_0'},
|
|
]
|
|
)
|
|
]
|
|
self.context = {
|
|
'id': '1',
|
|
'choices': choices,
|
|
'status': Status('correct'),
|
|
'input_type': 'radio',
|
|
'value': self.VALUE_DICT,
|
|
'response_data': self.RESPONSE_DATA
|
|
}
|
|
|
|
def test_grouping_tag(self):
|
|
"""
|
|
Tests whether we are using a section or a label to wrap choice elements.
|
|
Section is used for checkbox, so inputting text does not deselect
|
|
"""
|
|
input_tags = ('radio', 'checkbox')
|
|
self.context['status'] = Status('correct')
|
|
xpath = "//section[@id='forinput1_choiceinput_0bc']"
|
|
|
|
self.context['value'] = {}
|
|
for input_type in input_tags:
|
|
self.context['input_type'] = input_type
|
|
xml = self.render_to_xml(self.context)
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
def test_problem_marked_correct(self):
|
|
"""Test conditions under which the entire problem
|
|
(not a particular option) is marked correct"""
|
|
|
|
self.context['status'] = Status('correct')
|
|
self.context['input_type'] = 'checkbox'
|
|
self.context['value'] = self.VALUE_DICT
|
|
|
|
# Should mark the entire problem correct
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//div[@class='indicator-container']/span[@class='status correct']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
self.assert_no_xpath(xml, "//label[@class='choicetextgroup_incorrect']",
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml, "//label[@class='choicetextgroup_correct']",
|
|
self.context)
|
|
|
|
def test_problem_marked_incorrect(self):
|
|
"""Test all conditions under which the entire problem
|
|
(not a particular option) is marked incorrect"""
|
|
grouping_tags = {'radio': 'label', 'checkbox': 'section'}
|
|
conditions = [
|
|
{'status': Status('incorrect'), 'input_type': 'radio', 'value': {}},
|
|
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': self.WRONG_CHOICE_CHECKBOX},
|
|
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': self.BOTH_CHOICE_CHECKBOX},
|
|
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': self.VALUE_DICT},
|
|
{'status': Status('incomplete'), 'input_type': 'radio', 'value': {}},
|
|
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': self.WRONG_CHOICE_CHECKBOX},
|
|
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': self.BOTH_CHOICE_CHECKBOX},
|
|
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': self.VALUE_DICT}]
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//div[@class='indicator-container']/span[@class='status incorrect']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
grouping_tag = grouping_tags[test_conditions['input_type']]
|
|
self.assert_no_xpath(xml,
|
|
"//{0}[@class='choicetextgroup_incorrect']".format(grouping_tag),
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml,
|
|
"//{0}[@class='choicetextgroup_correct']".format(grouping_tag),
|
|
self.context)
|
|
|
|
def test_problem_marked_unsubmitted(self):
|
|
"""Test all conditions under which the entire problem
|
|
(not a particular option) is marked unanswered"""
|
|
grouping_tags = {'radio': 'label', 'checkbox': 'section'}
|
|
|
|
conditions = [
|
|
{'status': Status('unsubmitted'), 'input_type': 'radio', 'value': {}},
|
|
{'status': Status('unsubmitted'), 'input_type': 'radio', 'value': self.EMPTY_DICT},
|
|
{'status': Status('unsubmitted'), 'input_type': 'checkbox', 'value': {}},
|
|
{'status': Status('unsubmitted'), 'input_type': 'checkbox', 'value': self.EMPTY_DICT},
|
|
{'status': Status('unsubmitted'), 'input_type': 'checkbox', 'value': self.VALUE_DICT},
|
|
{'status': Status('unsubmitted'), 'input_type': 'checkbox', 'value': self.BOTH_CHOICE_CHECKBOX},
|
|
]
|
|
|
|
self.context['status'] = Status('unanswered')
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//div[@class='indicator-container']/span[@class='status unanswered']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark individual options
|
|
grouping_tag = grouping_tags[test_conditions['input_type']]
|
|
self.assert_no_xpath(xml,
|
|
"//{0}[@class='choicetextgroup_incorrect']".format(grouping_tag),
|
|
self.context)
|
|
|
|
self.assert_no_xpath(xml,
|
|
"//{0}[@class='choicetextgroup_correct']".format(grouping_tag),
|
|
self.context)
|
|
|
|
def test_option_marked_correct(self):
|
|
"""Test conditions under which a particular option
|
|
(not the entire problem) is marked correct."""
|
|
|
|
conditions = [
|
|
{'input_type': 'radio', 'value': self.VALUE_DICT}]
|
|
|
|
self.context['status'] = Status('correct')
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//section[@id='forinput1_choiceinput_0bc' and\
|
|
@class='choicetextgroup_correct']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark the whole problem
|
|
xpath = "//div[@class='indicator-container']/span"
|
|
self.assert_no_xpath(xml, xpath, self.context)
|
|
|
|
def test_option_marked_incorrect(self):
|
|
"""Test conditions under which a particular option
|
|
(not the entire problem) is marked incorrect."""
|
|
|
|
conditions = [
|
|
{'input_type': 'radio', 'value': self.VALUE_DICT}]
|
|
|
|
self.context['status'] = Status('incorrect')
|
|
|
|
for test_conditions in conditions:
|
|
self.context.update(test_conditions)
|
|
xml = self.render_to_xml(self.context)
|
|
xpath = "//section[@id='forinput1_choiceinput_0bc' and\
|
|
@class='choicetextgroup_incorrect']"
|
|
self.assert_has_xpath(xml, xpath, self.context)
|
|
|
|
# Should NOT mark the whole problem
|
|
xpath = "//div[@class='indicator-container']/span"
|
|
self.assert_no_xpath(xml, xpath, self.context)
|
|
|
|
def test_aria_label(self):
|
|
"""
|
|
Verify aria-label attribute rendering.
|
|
"""
|
|
self.assert_label(aria_label=True)
|
|
|
|
|
|
class ChemicalEquationTemplateTest(TemplateTestCase):
|
|
"""Test mako template for `<chemicalequationinput>` input"""
|
|
|
|
TEMPLATE_NAME = 'chemicalequationinput.html'
|
|
|
|
def setUp(self):
|
|
super(ChemicalEquationTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': '1',
|
|
'status': Status('correct'),
|
|
'previewer': 'dummy.js',
|
|
'value': '101',
|
|
}
|
|
|
|
def test_aria_label(self):
|
|
"""
|
|
Verify aria-label attribute rendering.
|
|
"""
|
|
self.assert_label(aria_label=True)
|
|
|
|
|
|
class SchematicInputTemplateTest(TemplateTestCase):
|
|
"""Test mako template for `<schematic>` input"""
|
|
|
|
TEMPLATE_NAME = 'schematicinput.html'
|
|
|
|
def setUp(self):
|
|
super(SchematicInputTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': '1',
|
|
'status': Status('correct'),
|
|
'previewer': 'dummy.js',
|
|
'value': '101',
|
|
'STATIC_URL': '/dummy-static/',
|
|
'msg': '',
|
|
'initial_value': 'two large batteries',
|
|
'width': '100',
|
|
'height': '100',
|
|
'parts': 'resistors, capacitors, and flowers',
|
|
'setup_script': '/dummy-static/js/capa/schematicinput.js',
|
|
'analyses': 'fast, slow, and pink',
|
|
'submit_analyses': 'maybe',
|
|
}
|
|
|
|
def test_aria_label(self):
|
|
"""
|
|
Verify aria-label attribute rendering.
|
|
"""
|
|
self.assert_label(aria_label=True)
|
|
|
|
|
|
class CodeinputTemplateTest(TemplateTestCase):
|
|
"""
|
|
Test mako template for `<textbox>` input
|
|
"""
|
|
|
|
TEMPLATE_NAME = 'codeinput.html'
|
|
|
|
def setUp(self):
|
|
super(CodeinputTemplateTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.context = {
|
|
'id': '1',
|
|
'status': Status('correct'),
|
|
'mode': 'parrot',
|
|
'linenumbers': 'false',
|
|
'rows': '37',
|
|
'cols': '11',
|
|
'tabsize': '7',
|
|
'hidden': '',
|
|
'msg': '',
|
|
'value': 'print "good evening"',
|
|
'aria_label': 'python editor',
|
|
'code_mirror_exit_message': 'Press ESC then TAB or click outside of the code editor to exit',
|
|
'response_data': self.RESPONSE_DATA,
|
|
'describedby': HTML(self.DESCRIBEDBY),
|
|
}
|
|
|
|
def test_label(self):
|
|
"""
|
|
Verify question label is rendered correctly.
|
|
"""
|
|
self.assert_label(xpath="//label[@class='problem-group-label']")
|
|
|
|
def test_editor_exit_message(self):
|
|
"""
|
|
Verify that editor exit message is rendered.
|
|
"""
|
|
xml = self.render_to_xml(self.context)
|
|
self.assert_has_text(xml, '//span[@id="cm-editor-exit-message-1"]', self.context['code_mirror_exit_message'])
|