308 lines
11 KiB
Python
308 lines
11 KiB
Python
"""
|
|
CAPA HTML rendering tests.
|
|
"""
|
|
|
|
|
|
import os
|
|
import textwrap
|
|
import unittest
|
|
|
|
import ddt
|
|
import mock
|
|
from lxml import etree
|
|
from xmodule.capa.tests.helpers import new_loncapa_problem, test_capa_system
|
|
from openedx.core.djangolib.markup import HTML
|
|
|
|
from .response_xml_factory import CustomResponseXMLFactory, StringResponseXMLFactory
|
|
|
|
|
|
@ddt.ddt
|
|
class CapaHtmlRenderTest(unittest.TestCase):
|
|
"""
|
|
CAPA HTML rendering tests class.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(CapaHtmlRenderTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
|
self.capa_system = test_capa_system()
|
|
|
|
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())
|
|
# TODO: This test should inspect the rendered html and assert one or more things about it
|
|
|
|
def test_include_html(self):
|
|
# Create a test file to include
|
|
self._create_test_file(
|
|
'test_include.xml',
|
|
'<test>Test include</test>'
|
|
)
|
|
|
|
# Generate some XML with an <include>
|
|
xml_str = textwrap.dedent("""
|
|
<problem>
|
|
<include file="test_include.xml"/>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem
|
|
problem = new_loncapa_problem(xml_str, capa_system=self.capa_system)
|
|
|
|
# Render the HTML
|
|
rendered_html = etree.XML(problem.get_html())
|
|
|
|
# Expect that the include file was embedded in the problem
|
|
test_element = rendered_html.find("test")
|
|
assert test_element.tag == 'test'
|
|
assert test_element.text == 'Test include'
|
|
|
|
def test_process_outtext(self):
|
|
# Generate some XML with <startouttext /> and <endouttext />
|
|
xml_str = textwrap.dedent("""
|
|
<problem>
|
|
<startouttext/>Test text<endouttext/>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem
|
|
problem = new_loncapa_problem(xml_str)
|
|
|
|
# Render the HTML
|
|
rendered_html = etree.XML(problem.get_html())
|
|
|
|
# Expect that the <startouttext /> and <endouttext />
|
|
# were converted to <span></span> tags
|
|
span_element = rendered_html.find('span')
|
|
assert span_element.text == 'Test text'
|
|
|
|
def test_anonymous_student_id(self):
|
|
# make sure anonymous_student_id is rendered properly as a context variable
|
|
xml_str = textwrap.dedent("""
|
|
<problem>
|
|
<span>Welcome $anonymous_student_id</span>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem
|
|
problem = new_loncapa_problem(xml_str)
|
|
|
|
# Render the HTML
|
|
rendered_html = etree.XML(problem.get_html())
|
|
|
|
# Expect that the anonymous_student_id was converted to "student"
|
|
span_element = rendered_html.find('span')
|
|
assert span_element.text == 'Welcome student'
|
|
|
|
def test_render_script(self):
|
|
# Generate some XML with a <script> tag
|
|
xml_str = textwrap.dedent("""
|
|
<problem>
|
|
<script>test=True</script>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem
|
|
problem = new_loncapa_problem(xml_str)
|
|
|
|
# Render the HTML
|
|
rendered_html = etree.XML(problem.get_html())
|
|
|
|
# Expect that the script element has been removed from the rendered HTML
|
|
script_element = rendered_html.find('script')
|
|
assert script_element is None
|
|
|
|
def test_render_javascript(self):
|
|
# Generate some XML with a <script> tag
|
|
xml_str = textwrap.dedent("""
|
|
<problem>
|
|
<script type="text/javascript">function(){}</script>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem
|
|
problem = new_loncapa_problem(xml_str)
|
|
|
|
# Render the HTML
|
|
rendered_html = etree.XML(problem.get_html())
|
|
|
|
# expect the javascript is still present in the rendered html
|
|
assert '<script type="text/javascript">function(){}</script>' in etree.tostring(rendered_html).decode('utf-8')
|
|
|
|
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_correct_aria_label(self):
|
|
xml = """
|
|
<problem>
|
|
<choiceresponse>
|
|
<checkboxgroup>
|
|
<choice correct="true">over-suspicious</choice>
|
|
<choice correct="false">funny</choice>
|
|
</checkboxgroup>
|
|
</choiceresponse>
|
|
<choiceresponse>
|
|
<checkboxgroup>
|
|
<choice correct="true">Urdu</choice>
|
|
<choice correct="false">Finnish</choice>
|
|
</checkboxgroup>
|
|
</choiceresponse>
|
|
</problem>
|
|
"""
|
|
problem = new_loncapa_problem(xml)
|
|
rendered_html = etree.XML(problem.get_html())
|
|
response_elements = rendered_html.findall('div')
|
|
assert response_elements[0].attrib['aria-label'] == 'Question 1'
|
|
assert response_elements[1].attrib['aria-label'] == 'Question 2'
|
|
|
|
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']")
|
|
assert msg_div_element.tag == 'div'
|
|
assert 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')
|
|
assert msg_p_elements[0].tag == 'p'
|
|
assert msg_p_elements[0].text == 'Test message 1'
|
|
|
|
assert msg_p_elements[1].tag == 'p'
|
|
assert msg_p_elements[1].text == 'Test message 2'
|
|
|
|
def test_substitute_python_vars(self):
|
|
# Generate some XML with Python variables defined in a script
|
|
# and used later as attributes
|
|
xml_str = textwrap.dedent("""
|
|
<problem>
|
|
<script>test="TEST"</script>
|
|
<span attr="$test"></span>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem and render the HTML
|
|
problem = new_loncapa_problem(xml_str)
|
|
rendered_html = etree.XML(problem.get_html())
|
|
|
|
# Expect that the variable $test has been replaced with its value
|
|
span_element = rendered_html.find('span')
|
|
assert span_element.get('attr') == 'TEST'
|
|
|
|
def test_xml_comments_and_other_odd_things(self):
|
|
# Comments and processing instructions should be skipped.
|
|
xml_str = textwrap.dedent("""\
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<!DOCTYPE html []>
|
|
<problem>
|
|
<!-- A commment. -->
|
|
<?ignore this processing instruction. ?>
|
|
</problem>
|
|
""")
|
|
|
|
# Create the problem
|
|
problem = new_loncapa_problem(xml_str)
|
|
|
|
# Render the HTML
|
|
the_html = problem.get_html()
|
|
self.assertRegex(the_html, r"<div>\s*</div>")
|
|
|
|
def _create_test_file(self, path, content_str): # lint-amnesty, pylint: disable=missing-function-docstring
|
|
test_fp = self.capa_system.resources_fs.open(path, "w")
|
|
test_fp.write(content_str)
|
|
test_fp.close()
|
|
|
|
self.addCleanup(lambda: os.remove(test_fp.name))
|