Commit 7240b625 by muhammad-ammar Committed by muzaffaryousaf

Associate descriptions with question text

TNL-5014
parent 3556f2a3
...@@ -13,6 +13,7 @@ Main module which shows problems (of "capa" type). ...@@ -13,6 +13,7 @@ Main module which shows problems (of "capa" type).
This is used by capa_module. This is used by capa_module.
""" """
from collections import OrderedDict
from copy import deepcopy from copy import deepcopy
from datetime import datetime from datetime import datetime
import logging import logging
...@@ -35,6 +36,16 @@ from capa.safe_exec import safe_exec ...@@ -35,6 +36,16 @@ from capa.safe_exec import safe_exec
# extra things displayed after "show answers" is pressed # extra things displayed after "show answers" is pressed
solution_tags = ['solution'] solution_tags = ['solution']
# fully accessible capa response types
ACCESSIBLE_CAPA_RESPONSE_TYPES = [
'choiceresponse',
'multiplechoiceresponse',
'optionresponse',
'numericalresponse',
'stringresponse',
'formularesponse',
]
# these get captured as student responses # these get captured as student responses
response_properties = ["codeparam", "responseparam", "answer", "openendedparam"] response_properties = ["codeparam", "responseparam", "answer", "openendedparam"]
...@@ -61,6 +72,8 @@ log = logging.getLogger(__name__) ...@@ -61,6 +72,8 @@ log = logging.getLogger(__name__)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# main class for this module # main class for this module
DEFAULT_QUESTION_TEXT = "Formatting error: You must explicitly specify the question text."
class LoncapaSystem(object): class LoncapaSystem(object):
""" """
...@@ -855,17 +868,76 @@ class LoncapaProblem(object): ...@@ -855,17 +868,76 @@ class LoncapaProblem(object):
id=response_id_str id=response_id_str
) )
# assign one answer_id for each input type or solution type # assign one answer_id for each input type
for entry in inputfields: for entry in inputfields:
entry.attrib['response_id'] = str(response_id) entry.attrib['response_id'] = str(response_id)
entry.attrib['answer_id'] = str(answer_id) entry.attrib['answer_id'] = str(answer_id)
entry.attrib['id'] = "%s_%i_%i" % (self.problem_id, response_id, answer_id) entry.attrib['id'] = "%s_%i_%i" % (self.problem_id, response_id, answer_id)
answer_id = answer_id + 1 answer_id = answer_id + 1
# Find the label and save it for html transformation step question_id = u'{}_{}'.format(self.problem_id, response_id)
responsetype_label = response.find('label') label = ''
problem_data[self.problem_id + '_' + str(response_id)] = { element_to_be_deleted = None
'label': responsetype_label.text if responsetype_label is not None else ''
# Extract label value from <label> tag or label attribute from inside the responsetype
responsetype_label_tag = response.find('label')
if responsetype_label_tag is not None:
label = responsetype_label_tag.text
# store <label> tag containing question text to delete
# it later otherwise question will be rendered twice
element_to_be_deleted = responsetype_label_tag
elif 'label' in inputfields[0].attrib:
# Extract label value from label attribute
# This is the case when we have a problem
# * with multiple questions without separation
# * single question with old XML format only
label = inputfields[0].attrib['label']
# Get first <p> tag before responsetype, this <p> contains the question text.
p_tag = response.xpath('preceding-sibling::p[1]')
if p_tag:
# It may be possible that label attribute value doesn't match with <p> tag
# This happens when author updated the question <p> tag directly in XML but
# didn't changed the label attribute value. In this case we will consider the
# first <p> tag before responsetype as question.
if label != p_tag[0].text:
label = p_tag[0].text
element_to_be_deleted = p_tag[0]
else:
# In this case the problems don't have tag or label attribute inside the responsetype
# so we will get the first preceding label tag w.r.t to this responsetype.
# This will take care of those multi-question problems that are not using --- in their markdown.
label_tag = response.xpath("preceding-sibling::label[1]")
if label_tag:
label = label_tag[0].text
element_to_be_deleted = label_tag[0]
label = label.strip() or DEFAULT_QUESTION_TEXT
# delete label or p element only if responsetype is fully accessible
if response.tag in ACCESSIBLE_CAPA_RESPONSE_TYPES and element_to_be_deleted is not None:
element_to_be_deleted.getparent().remove(element_to_be_deleted)
# for non-accessible responsetypes it may be possible that label attribute is not present
# in this case pass an empty label. remember label attribute is only used as value for aria-label
if response.tag not in ACCESSIBLE_CAPA_RESPONSE_TYPES and label == DEFAULT_QUESTION_TEXT:
label = ''
# Extract descriptions and set unique id on each description tag
description_tags = response.findall('description')
description_id = 1
descriptions = OrderedDict()
for description in description_tags:
descriptions[
"description_%s_%i_%i" % (self.problem_id, response_id, description_id)
] = description.text
response.remove(description)
description_id += 1
problem_data[question_id] = {
'label': label,
'descriptions': descriptions
} }
# instantiate capa Response # instantiate capa Response
......
...@@ -225,7 +225,7 @@ class InputTypeBase(object): ...@@ -225,7 +225,7 @@ class InputTypeBase(object):
self.hintmode = feedback.get('hintmode', None) self.hintmode = feedback.get('hintmode', None)
self.input_state = state.get('input_state', {}) self.input_state = state.get('input_state', {})
self.answervariable = state.get('answervariable', None) self.answervariable = state.get('answervariable', None)
self.response_data = state.get('response_data', None) self.response_data = state.get('response_data')
# put hint above msg if it should be displayed # put hint above msg if it should be displayed
if self.hintmode == 'always': if self.hintmode == 'always':
...@@ -319,8 +319,16 @@ class InputTypeBase(object): ...@@ -319,8 +319,16 @@ class InputTypeBase(object):
'msg': self.msg, 'msg': self.msg,
'response_data': self.response_data, 'response_data': self.response_data,
'STATIC_URL': self.capa_system.STATIC_URL, 'STATIC_URL': self.capa_system.STATIC_URL,
'describedby': '',
} }
# Don't add aria-describedby attribute if there are no descriptions
if self.response_data.get('descriptions'):
description_ids = ' '.join(self.response_data.get('descriptions').keys())
context.update(
{'describedby': 'aria-describedby="{}"'.format(description_ids)}
)
context.update( context.update(
(a, v) for (a, v) in self.loaded_attributes.iteritems() if a in self.to_render (a, v) for (a, v) in self.loaded_attributes.iteritems() if a in self.to_render
) )
...@@ -380,7 +388,7 @@ class OptionInput(InputTypeBase): ...@@ -380,7 +388,7 @@ class OptionInput(InputTypeBase):
Example: Example:
<optioninput options="('Up','Down')" label="Where is the sky?" correct="Up"/><text>The location of the sky</text> <optioninput options="('Up','Down')" correct="Up"/><text>The location of the sky</text>
# TODO: allow ordering to be randomized # TODO: allow ordering to be randomized
""" """
...@@ -416,7 +424,6 @@ class OptionInput(InputTypeBase): ...@@ -416,7 +424,6 @@ class OptionInput(InputTypeBase):
Convert options to a convenient format. Convert options to a convenient format.
""" """
return [Attribute('options', transform=cls.parse_options), return [Attribute('options', transform=cls.parse_options),
Attribute('label', ''),
Attribute('inline', False)] Attribute('inline', False)]
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
...@@ -435,7 +442,7 @@ class ChoiceGroup(InputTypeBase): ...@@ -435,7 +442,7 @@ class ChoiceGroup(InputTypeBase):
Example: Example:
<choicegroup label="Which foil?"> <choicegroup>
<choice correct="false" name="foil1"> <choice correct="false" name="foil1">
<text>This is foil One.</text> <text>This is foil One.</text>
</choice> </choice>
...@@ -478,7 +485,6 @@ class ChoiceGroup(InputTypeBase): ...@@ -478,7 +485,6 @@ class ChoiceGroup(InputTypeBase):
# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file # `django.utils.translation.ugettext_noop` because Django cannot be imported in this file
_ = lambda text: text _ = lambda text: text
return [Attribute("show_correctness", "always"), return [Attribute("show_correctness", "always"),
Attribute('label', ''),
Attribute("submitted_message", _("Answer received."))] Attribute("submitted_message", _("Answer received."))]
def _extra_context(self): def _extra_context(self):
...@@ -640,7 +646,7 @@ class TextLine(InputTypeBase): ...@@ -640,7 +646,7 @@ class TextLine(InputTypeBase):
is used e.g. for embedding simulations turned into questions. is used e.g. for embedding simulations turned into questions.
Example: Example:
<textline math="1" trailing_text="m/s" label="How fast is a cheetah?" /> <textline math="1" trailing_text="m/s"/>
This example will render out a text line with a math preview and the text 'm/s' This example will render out a text line with a math preview and the text 'm/s'
after the end of the text line. after the end of the text line.
...@@ -656,7 +662,6 @@ class TextLine(InputTypeBase): ...@@ -656,7 +662,6 @@ class TextLine(InputTypeBase):
""" """
return [ return [
Attribute('size', None), Attribute('size', None),
Attribute('label', ''),
Attribute('hidden', False), Attribute('hidden', False),
Attribute('inline', False), Attribute('inline', False),
...@@ -716,7 +721,6 @@ class FileSubmission(InputTypeBase): ...@@ -716,7 +721,6 @@ class FileSubmission(InputTypeBase):
Convert the list of allowed files to a convenient format. Convert the list of allowed files to a convenient format.
""" """
return [Attribute('allowed_files', '[]', transform=cls.parse_files), return [Attribute('allowed_files', '[]', transform=cls.parse_files),
Attribute('label', ''),
Attribute('required_files', '[]', transform=cls.parse_files), ] Attribute('required_files', '[]', transform=cls.parse_files), ]
def setup(self): def setup(self):
...@@ -1030,7 +1034,6 @@ class Schematic(InputTypeBase): ...@@ -1030,7 +1034,6 @@ class Schematic(InputTypeBase):
Attribute('analyses', None), Attribute('analyses', None),
Attribute('initial_value', None), Attribute('initial_value', None),
Attribute('submit_analyses', None), Attribute('submit_analyses', None),
Attribute('label', ''),
] ]
def _extra_context(self): def _extra_context(self):
...@@ -1066,7 +1069,6 @@ class ImageInput(InputTypeBase): ...@@ -1066,7 +1069,6 @@ class ImageInput(InputTypeBase):
""" """
return [Attribute('src'), return [Attribute('src'),
Attribute('height'), Attribute('height'),
Attribute('label', ''),
Attribute('width'), ] Attribute('width'), ]
def setup(self): def setup(self):
...@@ -1157,8 +1159,7 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -1157,8 +1159,7 @@ class ChemicalEquationInput(InputTypeBase):
""" """
Can set size of text field. Can set size of text field.
""" """
return [Attribute('size', '20'), return [Attribute('size', '20'), ]
Attribute('label', ''), ]
def _extra_context(self): def _extra_context(self):
""" """
...@@ -1221,7 +1222,7 @@ class FormulaEquationInput(InputTypeBase): ...@@ -1221,7 +1222,7 @@ class FormulaEquationInput(InputTypeBase):
Example: Example:
<formulaequationinput size="50" label="Enter the equation for motion" /> <formulaequationinput size="50"/>
options: size -- width of the textbox. options: size -- width of the textbox.
trailing_text -- text to show after the input textbox when trailing_text -- text to show after the input textbox when
...@@ -1239,7 +1240,6 @@ class FormulaEquationInput(InputTypeBase): ...@@ -1239,7 +1240,6 @@ class FormulaEquationInput(InputTypeBase):
return [ return [
Attribute('size', '20'), Attribute('size', '20'),
Attribute('inline', False), Attribute('inline', False),
Attribute('label', ''),
Attribute('trailing_text', ''), Attribute('trailing_text', ''),
] ]
...@@ -1629,7 +1629,7 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1629,7 +1629,7 @@ class ChoiceTextGroup(InputTypeBase):
select the correct choice and fill in numbers to make it accurate. select the correct choice and fill in numbers to make it accurate.
<endouttext/> <endouttext/>
<choicetextresponse> <choicetextresponse>
<radiotextgroup label="What is the correct choice?"> <radiotextgroup>
<choice correct="false">The lowest number rolled was: <choice correct="false">The lowest number rolled was:
<decoy_input/> and the highest number rolled was: <decoy_input/> and the highest number rolled was:
<decoy_input/> .</choice> <decoy_input/> .</choice>
...@@ -1652,7 +1652,7 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1652,7 +1652,7 @@ class ChoiceTextGroup(InputTypeBase):
select the correct choices and fill in numbers to make them accurate. select the correct choices and fill in numbers to make them accurate.
<endouttext/> <endouttext/>
<choicetextresponse> <choicetextresponse>
<checkboxtextgroup label="What is the answer?"> <checkboxtextgroup>
<choice correct="true"> <choice correct="true">
The lowest number selected was <numtolerance_input answer="1.4142" tolerance="0.01"/> The lowest number selected was <numtolerance_input answer="1.4142" tolerance="0.01"/>
</choice> </choice>
...@@ -1718,7 +1718,6 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1718,7 +1718,6 @@ class ChoiceTextGroup(InputTypeBase):
return [ return [
Attribute("show_correctness", "always"), Attribute("show_correctness", "always"),
Attribute("submitted_message", _("Answer received.")), Attribute("submitted_message", _("Answer received.")),
Attribute("label", ""),
] ]
def _extra_context(self): def _extra_context(self):
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="${status.classname}" id="status_${id}"> <div class="${status.classname}" id="status_${id}">
<input type="text" name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}" <input type="text" name="input_${id}" id="input_${id}" aria-label="${response_data['label']}" aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}"
% if size: % if size:
size="${size}" size="${size}"
% endif % endif
......
...@@ -5,12 +5,11 @@ ...@@ -5,12 +5,11 @@
)) ))
%> %>
<form class="choicegroup capa_inputtype" id="inputtype_${id}"> <form class="choicegroup capa_inputtype" id="inputtype_${id}">
<fieldset> <fieldset ${describedby}>
% if response_data and response_data['label']: <legend id="${id}-legend" class="response-fieldset-legend field-group-hd">${response_data['label']}</legend>
<legend id="${id}-legend" class="response-fieldset-legend question-text">${response_data['label']}</legend> % for description_id, description_text in response_data['descriptions'].items():
% else: <p class="question-description" id="${description_id}">${description_text}</p>
<legend>Question</legend> % endfor
% endif
% for choice_id, choice_label in choices: % for choice_id, choice_label in choices:
<div class="field" aria-live="polite" aria-atomic="true"> <div class="field" aria-live="polite" aria-atomic="true">
<% <%
...@@ -33,8 +32,9 @@ ...@@ -33,8 +32,9 @@
<% label_class += ' choicegroup_' + correctness %> <% label_class += ' choicegroup_' + correctness %>
% endif % endif
% endif % endif
class="${label_class}" > class="${label_class}"
${describedby}
>
<input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" class="field-input input-${input_type}" value="${choice_id}" <input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" class="field-input input-${input_type}" value="${choice_id}"
## If the student selected this choice... ## If the student selected this choice...
% if is_radio_input(choice_id): % if is_radio_input(choice_id):
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<form class="choicetextgroup capa_inputtype" id="inputtype_${id}"> <form class="choicetextgroup capa_inputtype" id="inputtype_${id}">
<div class="script_placeholder" data-src="${STATIC_URL}js/capa/choicetextinput.js"/> <div class="script_placeholder" data-src="${STATIC_URL}js/capa/choicetextinput.js"/>
<fieldset aria-label="${label}"> <fieldset aria-label="${response_data['label']}">
% for choice_id, choice_description in choices: % for choice_id, choice_description in choices:
<%choice_id= choice_id %> <%choice_id= choice_id %>
<section id="forinput${choice_id}" <section id="forinput${choice_id}"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
% endif % endif
<p class="debug">${status}</p> <p class="debug">${status}</p>
<input type="file" name="input_${id}" id="input_${id}" value="${value}" multiple="multiple" data-required_files="${required_files|h}" data-allowed_files="${allowed_files|h}" aria-label="${label}" /> <input type="file" name="input_${id}" id="input_${id}" value="${value}" multiple="multiple" data-required_files="${required_files|h}" data-allowed_files="${allowed_files|h}" aria-label="${response_data['label']}"/>
</div> </div>
<div class="message">${msg|n}</div> <div class="message">${msg|n}</div>
</section> </section>
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
<div class="${status.classname}" id="status_${id}"> <div class="${status.classname}" id="status_${id}">
<input type="text" name="input_${id}" id="input_${id}" <input type="text" name="input_${id}" id="input_${id}"
data-input-id="${id}" value="${value}" data-input-id="${id}" value="${value}"
aria-label="${label}" ${describedby}
aria-describedby="${id}_status"
% if size: % if size:
size="${size}" size="${size}"
% endif % endif
......
<% doinline = "inline" if inline else "" %> <% doinline = "inline" if inline else "" %>
<form class="inputtype option-input ${doinline}"> <form class="inputtype option-input ${doinline}">
<select name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}"> <select name="input_${id}" id="input_${id}" aria-label="${response_data['label']}" ${describedby}>
<option value="option_${id}_dummy_default"> </option> <option value="option_${id}_dummy_default"> </option>
% for option_id, option_description in options: % for option_id, option_description in options:
<option value="${option_id}" <option value="${option_id}"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
analyses="${analyses}" analyses="${analyses}"
name="input_${id}" name="input_${id}"
id="input_${id}" id="input_${id}"
aria-label="${label}" aria-label="${response_data['label']}"
aria-describedby="answer_${id}" aria-describedby="answer_${id}"
value="${value|h}" value="${value|h}"
initial_value="${initial_value|h}" initial_value="${initial_value|h}"
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<div style="display:none;" name="${hidden}" inputid="input_${id}" /> <div style="display:none;" name="${hidden}" inputid="input_${id}" />
% endif % endif
<input type="text" name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}" value="${value}" <input type="text" name="input_${id}" id="input_${id}" aria-label="${response_data['label']}" ${describedby} value="${value}"
% if do_math: % if do_math:
class="math" class="math"
% endif % endif
...@@ -36,8 +36,8 @@ ...@@ -36,8 +36,8 @@
<span class="sr"> <span class="sr">
%if value: %if value:
${value} ${value}
% else: %else:
${label} ${response_data['label']}
%endif %endif
- -
${status.display_name} ${status.display_name}
......
"""
Test capa problem.
"""
import unittest
from . import new_loncapa_problem
from capa.capa_problem import DEFAULT_QUESTION_TEXT
class CAPAProblemTest(unittest.TestCase):
""" CAPA problem related tests"""
def test_label_and_description_inside_responsetype(self):
"""
Verify that
* label is extracted
* <label> tag is removed to avoid duplication
This is the case when we have a problem with single question or
problem with multiple-questions separated as per the new format.
"""
xml = """
<problem>
<choiceresponse>
<label>Select the correct synonym of paranoid?</label>
<description>Only the paranoid survive.</description>
<checkboxgroup>
<choice correct="true">over-suspicious</choice>
<choice correct="false">funny</choice>
</checkboxgroup>
</choiceresponse>
</problem>
"""
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': 'Select the correct synonym of paranoid?',
'descriptions': {'description_1_2_1': 'Only the paranoid survive.'}
}
}
)
self.assertEqual(len(problem.tree.xpath('//label')), 0)
def test_label_attribute_only(self):
"""
Verify that label is extracted and <p> tag with question
text is removed when label attribute is set on inputtype.
"""
question = "Once we become predictable, we become ______?"
xml = """
<problem>
<p>Be sure to check your spelling.</p>
<p>{}</p>
<stringresponse answer="vulnerable" type="ci">
<textline label="{}" size="40"/>
</stringresponse>
</problem>
""".format(question, question)
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{'1_2': {'label': question, 'descriptions': {}}}
)
self.assertEqual(
len(problem.tree.xpath('//p[text()="{}"]'.format(question))),
0
)
def test_neither_label_tag_nor_attribute(self):
"""
Verify that label is extracted correctly.
This is the case when we have a markdown problem with multiple-questions.
In this case when markdown is converted to xml, there will be no label
tag and label attribute inside responsetype. But we have a label tag
before the responsetype.
"""
question1 = 'People who say they have nothing to ____ almost always do?'
question2 = 'Select the correct synonym of paranoid?'
xml = """
<problem>
<p>Be sure to check your spelling.</p>
<label>{}</label>
<stringresponse answer="hide" type="ci">
<textline size="40"/>
</stringresponse>
<choiceresponse>
<label>{}</label>
<checkboxgroup>
<choice correct="true">over-suspicious</choice>
<choice correct="false">funny</choice>
</checkboxgroup>
</choiceresponse>
</problem>
""".format(question1, question2)
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': question1,
'descriptions': {}
},
'1_3':
{
'label': question2,
'descriptions': {}
}
}
)
for question in (question1, question2):
self.assertEqual(
len(problem.tree.xpath('//label[text()="{}"]'.format(question))),
0
)
def test_multiple_descriptions(self):
"""
Verify that multiple descriptions are handled correctly.
"""
xml = """
<problem>
<p>Be sure to check your spelling.</p>
<stringresponse answer="War" type="ci">
<label>___ requires sacrifices.</label>
<description>The problem with trying to be the bad guy, there's always someone worse.</description>
<description>Anyone who looks the world as if it was a game of chess deserves to lose.</description>
<textline size="40"/>
</stringresponse>
</problem>
"""
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': '___ requires sacrifices.',
'descriptions': {
'description_1_2_1': "The problem with trying to be the bad guy, there's always someone worse.",
'description_1_2_2': "Anyone who looks the world as if it was a game of chess deserves to lose."
}
}
}
)
def test_default_question_text(self):
"""
Verify that default question text is shown when question is missing.
"""
xml = """
<problem>
<p>Be sure to check your spelling.</p>
<stringresponse answer="War" type="ci">
<description>Everybody needs somebody to talk to.</description>
<textline size="40"/>
</stringresponse>
</problem>
"""
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': DEFAULT_QUESTION_TEXT,
'descriptions': {
'description_1_2_1': "Everybody needs somebody to talk to."
}
}
}
)
def test_question_is_not_removed(self):
"""
Verify that tag with question text is not removed when responsetype is not fully accessible.
"""
question = "Click the country which is home to the Pyramids."
xml = """
<problem>
<p>{}</p>
<imageresponse>
<imageinput label="{}"
src="/static/Africa.png" width="600" height="638" rectangle="(338,98)-(412,168)"/>
</imageresponse>
</problem>
""".format(question, question)
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': 'Click the country which is home to the Pyramids.',
'descriptions': {}
}
}
)
# <p> tag with question text should not be deleted
self.assertEqual(problem.tree.xpath("string(p[text()='{}'])".format(question)), question)
def test_label_is_empty_if_no_label_attribute(self):
"""
Verify that label in response_data is empty string when label
attribute is missing and responsetype is not fully accessible.
"""
question = "Click the country which is home to the Pyramids."
xml = """
<problem>
<p>{}</p>
<imageresponse>
<imageinput
src="/static/Africa.png" width="600" height="638" rectangle="(338,98)-(412,168)"/>
</imageresponse>
</problem>
""".format(question)
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': '',
'descriptions': {}
}
}
)
def test_multiple_questions_problem(self):
"""
For a problem with multiple questions verify that for each question
* label is extracted
* descriptions info is constructed
* <label> tag is removed to avoid duplication
"""
xml = """
<problem>
<choiceresponse>
<label>Select the correct synonym of paranoid?</label>
<description>Only the paranoid survive.</description>
<checkboxgroup>
<choice correct="true">over-suspicious</choice>
<choice correct="false">funny</choice>
</checkboxgroup>
</choiceresponse>
<multiplechoiceresponse>
<p>one more question</p>
<label>What Apple device competed with the portable CD player?</label>
<description>Device looks like an egg plant.</description>
<choicegroup type="MultipleChoice">
<choice correct="false">The iPad</choice>
<choice correct="false">Napster</choice>
<choice correct="true">The iPod</choice>
<choice correct="false">The vegetable peeler</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>
"""
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': 'Select the correct synonym of paranoid?',
'descriptions': {'description_1_2_1': 'Only the paranoid survive.'}
},
'1_3':
{
'label': 'What Apple device competed with the portable CD player?',
'descriptions': {'description_1_3_1': 'Device looks like an egg plant.'}
}
}
)
self.assertEqual(len(problem.tree.xpath('//label')), 0)
def test_label_attribute_mismatches_question_tag(self):
"""
Verify that question text is extracted correctly when label attribtue value
mismatched with question tag value.
This is the case when author updated the question <p> tag directly in XML but
didn't change the label attribute value. In this case we will consider the
first <p> tag before responsetype as question.
"""
question = 'Select the correct synonym of paranoid?'
xml = """
<problem>
<p>Choose wisely.</p>
<p>{}</p>
<choiceresponse>
<checkboxgroup label="Is egg plant a fruit?">
<choice correct="true">over-suspicious</choice>
<choice correct="false">funny</choice>
</checkboxgroup>
</choiceresponse>
</problem>
""".format(question)
problem = new_loncapa_problem(xml)
self.assertEqual(
problem.problem_data,
{
'1_2':
{
'label': question,
'descriptions': {}
}
}
)
self.assertEqual(
len(problem.tree.xpath('//p[text()="{}"]'.format(question))),
0
)
<problem> <problem>
<p>Select all the fruits from the list. In retrospect, the wordiness of these tests increases the dizziness!</p> <p>In retrospect, the wordiness of these tests increases the dizziness!</p>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the fruits from the list"> <label>Select all the fruits from the list</label>
<checkboxgroup>
<choice correct="true" id="alpha">Apple <choice correct="true" id="alpha">Apple
<choicehint selected="TrUe">You are right that apple is a fruit. <choicehint selected="TrUe">You are right that apple is a fruit.
</choicehint> </choicehint>
...@@ -33,9 +34,10 @@ ...@@ -33,9 +34,10 @@
</compoundhint> </compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<p>Select all the vegetables from the list</p>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the vegetables from the list"> <label>Select all the vegetables from the list</label>
<checkboxgroup>
<choice correct="false">Banana <choice correct="false">Banana
<choicehint selected="true">No, sorry, a banana is a fruit. <choicehint selected="true">No, sorry, a banana is a fruit.
</choicehint> </choicehint>
...@@ -66,6 +68,7 @@ ...@@ -66,6 +68,7 @@
</compoundhint> </compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<p>Compoundhint vs. correctness</p> <p>Compoundhint vs. correctness</p>
<choiceresponse> <choiceresponse>
<checkboxgroup> <checkboxgroup>
...@@ -114,4 +117,3 @@ ...@@ -114,4 +117,3 @@
</problem> </problem>
<problem> <problem>
<p>(note the blank line before mushroom -- be sure to include this test case)</p> <p>(note the blank line before mushroom -- be sure to include this test case)</p>
<p>Select the fruit from the list</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the fruit from the list" type="MultipleChoice"> <label>Select the fruit from the list</label>
<choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint label="">Mushroom is a fungus, not a fruit. <choicehint label="">Mushroom is a fungus, not a fruit.
</choicehint> </choicehint>
...@@ -14,9 +14,10 @@ ...@@ -14,9 +14,10 @@
</choice> </choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<p>Select the vegetables from the list</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the vegetables from the list" type="MultipleChoice"> <label>Select the vegetables from the list</label>
<choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint>Mushroom is a fungus, not a vegetable. <choicehint>Mushroom is a fungus, not a vegetable.
</choicehint> </choicehint>
......
<problem> <problem>
<p>Select the fruit from the list</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the fruit from the list" type="MultipleChoice"> <label>Select the fruit from the list</label>
<choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint>Mushroom <img src="#" ale="#"/>is a fungus, not a fruit.</choicehint> <choicehint>Mushroom <img src="#" ale="#"/>is a fungus, not a fruit.</choicehint>
</choice> </choice>
......
<problem> <problem>
<numericalresponse answer="1.141"> <numericalresponse answer="1.141">
<label>What value when squared is approximately equal to 2 (give your answer to 2 decimal places)?</label>
<responseparam default=".01" type="tolerance"/> <responseparam default=".01" type="tolerance"/>
<formulaequationinput label="What value when squared is approximately equal to 2 (give your answer to 2 decimal places)?"/> <formulaequationinput/>
<correcthint label="Nice"> <correcthint label="Nice">
The square root of two turns up in the strangest places. The square root of two turns up in the strangest places.
...@@ -11,8 +12,9 @@ ...@@ -11,8 +12,9 @@
</numericalresponse> </numericalresponse>
<numericalresponse answer="4"> <numericalresponse answer="4">
<label>What is 2 + 2?</label>
<responseparam default=".01" type="tolerance"/> <responseparam default=".01" type="tolerance"/>
<formulaequationinput label="What is 2 + 2?"/> <formulaequationinput/>
<correcthint> <correcthint>
Pretty easy, uh?. Pretty easy, uh?.
</correcthint> </correcthint>
...@@ -34,4 +36,3 @@ also not multiple correcthint ...@@ -34,4 +36,3 @@ also not multiple correcthint
</lehint> </lehint>
--> -->
</problem> </problem>
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<p>In which country would you find the city of Paris?</p> <p>In which country would you find the city of Paris?</p>
<stringresponse answer="FranceΩ" type="ci" > <stringresponse answer="FranceΩ" type="ci" >
<textline label="In which country would you find the city of Paris?" size="20"/> <label>In which country would you find the city of Paris?</label>
<textline size="20"/>
<correcthint> <correcthint>
Viva la France!Ω Viva la France!Ω
</correcthint> </correcthint>
...@@ -22,16 +23,18 @@ ...@@ -22,16 +23,18 @@
<p>What color is the sky? A minimal example, case sensitive, not regex.</p> <p>What color is the sky? A minimal example, case sensitive, not regex.</p>
<stringresponse answer="Blue"> <stringresponse answer="Blue">
<label>What color is the sky?</label>
<correcthint >The red light is scattered by water molecules leaving only blue light. <correcthint >The red light is scattered by water molecules leaving only blue light.
</correcthint> </correcthint>
<textline label="What color is the sky?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<p>(This question will cause an illegal regular expression exception)</p> <p>(This question will cause an illegal regular expression exception)</p>
<stringresponse answer="Bonk"> <stringresponse answer="Bonk">
<label>Why not?</label>
<correcthint >This hint should never appear. <correcthint >This hint should never appear.
</correcthint> </correcthint>
<textline label="Why not?" size="20"/> <textline size="20"/>
<regexphint answer="["> <regexphint answer="[">
This hint should never appear either because the regex is illegal. This hint should never appear either because the regex is illegal.
</regexphint> </regexphint>
......
<problem> <problem>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the vegetables from the list"> <label>Select all the vegetables from the list</label>
<checkboxgroup>
<choice correct="false">Banana <choice correct="false">Banana
<choicehint selected="true">No, sorry, a banana is a fruit. <choicehint selected="true">No, sorry, a banana is a fruit.
</choicehint> </choicehint>
......
...@@ -7,6 +7,7 @@ import mock ...@@ -7,6 +7,7 @@ import mock
from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFactory from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFactory
from . import test_capa_system, new_loncapa_problem from . import test_capa_system, new_loncapa_problem
from capa.capa_problem import DEFAULT_QUESTION_TEXT
class CapaHtmlRenderTest(unittest.TestCase): class CapaHtmlRenderTest(unittest.TestCase):
...@@ -176,7 +177,6 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -176,7 +177,6 @@ class CapaHtmlRenderTest(unittest.TestCase):
expected_textline_context = { expected_textline_context = {
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'status': the_system.STATUS_CLASS('unsubmitted'), 'status': the_system.STATUS_CLASS('unsubmitted'),
'label': '',
'value': '', 'value': '',
'preprocessor': None, 'preprocessor': None,
'msg': '', 'msg': '',
...@@ -186,6 +186,8 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -186,6 +186,8 @@ class CapaHtmlRenderTest(unittest.TestCase):
'id': '1_2_1', 'id': '1_2_1',
'trailing_text': '', 'trailing_text': '',
'size': None, 'size': None,
'response_data': {'label': DEFAULT_QUESTION_TEXT, 'descriptions': {}},
'describedby': ''
} }
expected_solution_context = {'id': '1_solution_1'} expected_solution_context = {'id': '1_solution_1'}
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Tests for the logic in input type mako templates. Tests for the logic in input type mako templates.
""" """
from collections import OrderedDict
import unittest import unittest
import capa import capa
import os.path import os.path
...@@ -29,6 +30,13 @@ class TemplateTestCase(unittest.TestCase): ...@@ -29,6 +30,13 @@ class TemplateTestCase(unittest.TestCase):
# The template name should include the .html extension: # The template name should include the .html extension:
# for example: choicegroup.html # for example: choicegroup.html
TEMPLATE_NAME = None TEMPLATE_NAME = None
DESCRIBEDBY = 'aria-describedby="desc-1 desc-2"'
DESCRIPTIONS = OrderedDict([('desc-1', 'description text 1'), ('desc-2', 'description text 2')])
DESCRIPTION_IDS = ' '.join(DESCRIPTIONS.keys())
RESPONSE_DATA = {
'label': 'question text 101',
'descriptions': DESCRIPTIONS
}
def setUp(self): def setUp(self):
""" """
...@@ -42,6 +50,8 @@ class TemplateTestCase(unittest.TestCase): ...@@ -42,6 +50,8 @@ class TemplateTestCase(unittest.TestCase):
with open(self.template_path) as f: with open(self.template_path) as f:
self.template = MakoTemplate(f.read()) self.template = MakoTemplate(f.read())
self.context = {}
def render_to_xml(self, context_dict): def render_to_xml(self, context_dict):
""" """
Render the template using the `context_dict` dict. Render the template using the `context_dict` dict.
...@@ -112,6 +122,49 @@ class TemplateTestCase(unittest.TestCase): ...@@ -112,6 +122,49 @@ class TemplateTestCase(unittest.TestCase):
else: else:
self.assertIn(text, element_list[0].text) self.assertIn(text, element_list[0].text)
def assert_description(self, describedby_xpaths, descriptions=True):
"""
Verify that descriptions information is correct.
Arguments:
describedby_xpaths (list): list of xpaths to check aria-describedby attribute
descriptions (bool): tells whether we need to check description <p> tags
"""
xml = self.render_to_xml(self.context)
# TODO! This check should be removed once description <p> tags are added into all templates.
if descriptions:
# Verify that each description <p> tag has correct id, text and order
descriptions = OrderedDict(
(tag.get('id'), tag.text) for tag in xml.xpath('//p[@class="question-description"]')
)
self.assertEqual(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
self.assertTrue(describedbys)
for describedby in describedbys:
self.assertEqual(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'] = ''
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)
self.assertFalse(describedbys)
class ChoiceGroupTemplateTest(TemplateTestCase): class ChoiceGroupTemplateTest(TemplateTestCase):
""" """
...@@ -121,18 +174,18 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -121,18 +174,18 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'choicegroup.html' TEMPLATE_NAME = 'choicegroup.html'
def setUp(self): def setUp(self):
super(ChoiceGroupTemplateTest, self).setUp()
choices = [('1', 'choice 1'), ('2', 'choice 2'), ('3', 'choice 3')] choices = [('1', 'choice 1'), ('2', 'choice 2'), ('3', 'choice 3')]
self.context = { self.context = {
'id': '1', 'id': '1',
'choices': choices, 'choices': choices,
'status': Status('correct'), 'status': Status('correct'),
'label': 'test',
'input_type': 'checkbox', 'input_type': 'checkbox',
'name_array_suffix': '1', 'name_array_suffix': '1',
'value': '3', 'value': '3',
'response_data': {'label': 'test'} 'response_data': self.RESPONSE_DATA,
'describedby': self.DESCRIBEDBY,
} }
super(ChoiceGroupTemplateTest, self).setUp()
def test_problem_marked_correct(self): def test_problem_marked_correct(self):
""" """
...@@ -344,7 +397,15 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -344,7 +397,15 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
def test_label(self): def test_label(self):
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//legend" xpath = "//legend"
self.assert_has_text(xml, xpath, self.context['label']) self.assert_has_text(xml, xpath, self.context['response_data']['label'])
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)
class TextlineTemplateTest(TemplateTestCase): class TextlineTemplateTest(TemplateTestCase):
...@@ -355,13 +416,16 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -355,13 +416,16 @@ class TextlineTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'textline.html' TEMPLATE_NAME = 'textline.html'
def setUp(self): def setUp(self):
self.context = {'id': '1', super(TextlineTemplateTest, self).setUp()
self.context = {
'id': '1',
'status': Status('correct'), 'status': Status('correct'),
'label': 'test',
'value': '3', 'value': '3',
'preprocessor': None, 'preprocessor': None,
'trailing_text': None} 'trailing_text': None,
super(TextlineTemplateTest, self).setUp() 'response_data': self.RESPONSE_DATA,
'describedby': self.DESCRIBEDBY,
}
def test_section_class(self): def test_section_class(self):
cases = [({}, ' capa_inputtype textline'), cases = [({}, ' capa_inputtype textline'),
...@@ -397,7 +461,7 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -397,7 +461,7 @@ class TextlineTemplateTest(TemplateTestCase):
def test_label(self): def test_label(self):
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//input[@aria-label='%s']" % self.context['label'] xpath = "//input[@aria-label='%s']" % self.context['response_data']['label']
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
def test_hidden(self): def test_hidden(self):
...@@ -473,6 +537,14 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -473,6 +537,14 @@ class TextlineTemplateTest(TemplateTestCase):
xpath = "//span[@class='message']" xpath = "//span[@class='message']"
self.assert_has_text(xml, xpath, self.context['msg']) 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, descriptions=False)
self.assert_describedby_attribute(xpaths)
class FormulaEquationInputTemplateTest(TemplateTestCase): class FormulaEquationInputTemplateTest(TemplateTestCase):
""" """
...@@ -481,16 +553,17 @@ class FormulaEquationInputTemplateTest(TemplateTestCase): ...@@ -481,16 +553,17 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'formulaequationinput.html' TEMPLATE_NAME = 'formulaequationinput.html'
def setUp(self): def setUp(self):
super(FormulaEquationInputTemplateTest, self).setUp()
self.context = { self.context = {
'id': 2, 'id': 2,
'value': 'PREFILLED_VALUE', 'value': 'PREFILLED_VALUE',
'status': Status('unsubmitted'), 'status': Status('unsubmitted'),
'label': 'test',
'previewer': 'file.js', 'previewer': 'file.js',
'reported_status': 'REPORTED_STATUS', 'reported_status': 'REPORTED_STATUS',
'trailing_text': None, 'trailing_text': None,
'response_data': self.RESPONSE_DATA,
'describedby': self.DESCRIBEDBY,
} }
super(FormulaEquationInputTemplateTest, self).setUp()
def test_no_size(self): def test_no_size(self):
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
...@@ -502,6 +575,14 @@ class FormulaEquationInputTemplateTest(TemplateTestCase): ...@@ -502,6 +575,14 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
self.assert_has_xpath(xml, "//input[@size='40']", 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, descriptions=False)
self.assert_describedby_attribute(xpaths)
class AnnotationInputTemplateTest(TemplateTestCase): class AnnotationInputTemplateTest(TemplateTestCase):
""" """
...@@ -511,7 +592,9 @@ class AnnotationInputTemplateTest(TemplateTestCase): ...@@ -511,7 +592,9 @@ class AnnotationInputTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'annotationinput.html' TEMPLATE_NAME = 'annotationinput.html'
def setUp(self): def setUp(self):
self.context = {'id': 2, super(AnnotationInputTemplateTest, self).setUp()
self.context = {
'id': 2,
'value': '<p>Test value</p>', 'value': '<p>Test value</p>',
'title': '<h1>This is a title</h1>', 'title': '<h1>This is a title</h1>',
'text': '<p><b>This</b> is a test.</p>', 'text': '<p><b>This</b> is a test.</p>',
...@@ -524,8 +607,8 @@ class AnnotationInputTemplateTest(TemplateTestCase): ...@@ -524,8 +607,8 @@ class AnnotationInputTemplateTest(TemplateTestCase):
'debug': False, 'debug': False,
'status': Status('unsubmitted'), 'status': Status('unsubmitted'),
'return_to_annotation': False, 'return_to_annotation': False,
'msg': '<p>This is a test message</p>', } 'msg': '<p>This is a test message</p>',
super(AnnotationInputTemplateTest, self).setUp() }
def test_return_to_annotation(self): def test_return_to_annotation(self):
""" """
...@@ -637,8 +720,8 @@ class MathStringTemplateTest(TemplateTestCase): ...@@ -637,8 +720,8 @@ class MathStringTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'mathstring.html' TEMPLATE_NAME = 'mathstring.html'
def setUp(self): def setUp(self):
self.context = {'isinline': False, 'mathstr': '', 'tail': ''}
super(MathStringTemplateTest, self).setUp() super(MathStringTemplateTest, self).setUp()
self.context = {'isinline': False, 'mathstr': '', 'tail': ''}
def test_math_string_inline(self): def test_math_string_inline(self):
self.context['isinline'] = True self.context['isinline'] = True
...@@ -679,14 +762,15 @@ class OptionInputTemplateTest(TemplateTestCase): ...@@ -679,14 +762,15 @@ class OptionInputTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'optioninput.html' TEMPLATE_NAME = 'optioninput.html'
def setUp(self): def setUp(self):
super(OptionInputTemplateTest, self).setUp()
self.context = { self.context = {
'id': 2, 'id': 2,
'options': [], 'options': [],
'status': Status('unsubmitted'), 'status': Status('unsubmitted'),
'label': 'test', 'value': 0,
'value': 0 'response_data': self.RESPONSE_DATA,
'describedby': self.DESCRIBEDBY,
} }
super(OptionInputTemplateTest, self).setUp()
def test_select_options(self): def test_select_options(self):
...@@ -730,9 +814,17 @@ class OptionInputTemplateTest(TemplateTestCase): ...@@ -730,9 +814,17 @@ class OptionInputTemplateTest(TemplateTestCase):
def test_label(self): def test_label(self):
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//select[@aria-label='%s']" % self.context['label'] xpath = "//select[@aria-label='%s']" % self.context['response_data']['label']
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
def test_description(self):
"""
Test that correct description information is set on desired elements.
"""
xpaths = ['//select/@aria-describedby']
self.assert_description(xpaths, descriptions=False)
self.assert_describedby_attribute(xpaths)
class DragAndDropTemplateTest(TemplateTestCase): class DragAndDropTemplateTest(TemplateTestCase):
""" """
...@@ -742,12 +834,12 @@ class DragAndDropTemplateTest(TemplateTestCase): ...@@ -742,12 +834,12 @@ class DragAndDropTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'drag_and_drop_input.html' TEMPLATE_NAME = 'drag_and_drop_input.html'
def setUp(self): def setUp(self):
super(DragAndDropTemplateTest, self).setUp()
self.context = {'id': 2, self.context = {'id': 2,
'drag_and_drop_json': '', 'drag_and_drop_json': '',
'value': 0, 'value': 0,
'status': Status('unsubmitted'), 'status': Status('unsubmitted'),
'msg': ''} 'msg': ''}
super(DragAndDropTemplateTest, self).setUp()
def test_status(self): def test_status(self):
...@@ -799,6 +891,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -799,6 +891,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
'1_choiceinput_1_textinput_0': '0'} '1_choiceinput_1_textinput_0': '0'}
def setUp(self): def setUp(self):
super(ChoiceTextGroupTemplateTest, self).setUp()
choices = [ choices = [
( (
'1_choiceinput_0bc', '1_choiceinput_0bc',
...@@ -820,12 +913,10 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -820,12 +913,10 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
'choices': choices, 'choices': choices,
'status': Status('correct'), 'status': Status('correct'),
'input_type': 'radio', 'input_type': 'radio',
'label': 'choicetext label',
'value': self.VALUE_DICT, 'value': self.VALUE_DICT,
'response_data': self.RESPONSE_DATA
} }
super(ChoiceTextGroupTemplateTest, self).setUp()
def test_grouping_tag(self): def test_grouping_tag(self):
""" """
Tests whether we are using a section or a label to wrap choice elements. Tests whether we are using a section or a label to wrap choice elements.
...@@ -965,5 +1056,5 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -965,5 +1056,5 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
def test_label(self): def test_label(self):
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
xpath = "//fieldset[@aria-label='%s']" % self.context['label'] xpath = "//fieldset[@aria-label='%s']" % self.context['response_data']['label']
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
...@@ -16,7 +16,7 @@ TODO: ...@@ -16,7 +16,7 @@ TODO:
- test funny xml chars -- should never get xml parse error if things are escaped properly. - test funny xml chars -- should never get xml parse error if things are escaped properly.
""" """
from collections import OrderedDict
import json import json
from lxml import etree from lxml import etree
from lxml.html import fromstring from lxml.html import fromstring
...@@ -36,6 +36,14 @@ from capa.xqueue_interface import XQUEUE_TIMEOUT ...@@ -36,6 +36,14 @@ from capa.xqueue_interface import XQUEUE_TIMEOUT
lookup_tag = inputtypes.registry.get_class_for_tag lookup_tag = inputtypes.registry.get_class_for_tag
DESCRIBEDBY = 'aria-describedby="desc-1 desc-2"'
DESCRIPTIONS = OrderedDict([('desc-1', 'description text 1'), ('desc-2', 'description text 2')])
RESPONSE_DATA = {
'label': 'question text 101',
'descriptions': DESCRIPTIONS
}
def quote_attr(s): def quote_attr(s):
return saxutils.quoteattr(s)[1:-1] # don't want the outer quotes return saxutils.quoteattr(s)[1:-1] # don't want the outer quotes
...@@ -49,9 +57,12 @@ class OptionInputTest(unittest.TestCase): ...@@ -49,9 +57,12 @@ class OptionInputTest(unittest.TestCase):
xml_str = """<optioninput options="('Up','Down','Don't know')" id="sky_input" correct="Up"/>""" xml_str = """<optioninput options="('Up','Down','Don't know')" id="sky_input" correct="Up"/>"""
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'Down', state = {
'value': 'Down',
'id': 'sky_input', 'id': 'sky_input',
'status': 'answered'} 'status': 'answered',
'response_data': RESPONSE_DATA
}
option_input = lookup_tag('optioninput')(test_capa_system(), element, state) option_input = lookup_tag('optioninput')(test_capa_system(), element, state)
context = option_input._get_render_context() # pylint: disable=protected-access context = option_input._get_render_context() # pylint: disable=protected-access
...@@ -61,10 +72,11 @@ class OptionInputTest(unittest.TestCase): ...@@ -61,10 +72,11 @@ class OptionInputTest(unittest.TestCase):
'value': 'Down', 'value': 'Down',
'options': [('Up', 'Up'), ('Down', 'Down'), ('Don\'t know', 'Don\'t know')], 'options': [('Up', 'Up'), ('Down', 'Down'), ('Don\'t know', 'Don\'t know')],
'status': inputtypes.Status('answered'), 'status': inputtypes.Status('answered'),
'label': '',
'msg': '', 'msg': '',
'inline': False, 'inline': False,
'id': 'sky_input', 'id': 'sky_input',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -105,12 +117,14 @@ class ChoiceGroupTest(unittest.TestCase): ...@@ -105,12 +117,14 @@ class ChoiceGroupTest(unittest.TestCase):
<choice correct="false" name="foil4">This is <b>foil</b> Four.</choice> <choice correct="false" name="foil4">This is <b>foil</b> Four.</choice>
</{tag}> </{tag}>
""".format(tag=tag) """.format(tag=tag)
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'foil3', state = {
'value': 'foil3',
'id': 'sky_input', 'id': 'sky_input',
'status': 'answered'} 'status': 'answered',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag(tag)(test_capa_system(), element, state) the_input = lookup_tag(tag)(test_capa_system(), element, state)
...@@ -121,7 +135,6 @@ class ChoiceGroupTest(unittest.TestCase): ...@@ -121,7 +135,6 @@ class ChoiceGroupTest(unittest.TestCase):
'id': 'sky_input', 'id': 'sky_input',
'value': 'foil3', 'value': 'foil3',
'status': inputtypes.Status('answered'), 'status': inputtypes.Status('answered'),
'label': '',
'msg': '', 'msg': '',
'input_type': expected_input_type, 'input_type': expected_input_type,
'choices': [('foil1', '<text>This is foil One.</text>'), 'choices': [('foil1', '<text>This is foil One.</text>'),
...@@ -131,6 +144,8 @@ class ChoiceGroupTest(unittest.TestCase): ...@@ -131,6 +144,8 @@ class ChoiceGroupTest(unittest.TestCase):
'show_correctness': 'always', 'show_correctness': 'always',
'submitted_message': 'Answer received.', 'submitted_message': 'Answer received.',
'name_array_suffix': expected_suffix, # what is this for?? 'name_array_suffix': expected_suffix, # what is this for??
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -165,7 +180,10 @@ class JavascriptInputTest(unittest.TestCase): ...@@ -165,7 +180,10 @@ class JavascriptInputTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': '3', } state = {
'value': '3',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('javascriptinput')(test_capa_system(), element, state) the_input = lookup_tag('javascriptinput')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=protected-access context = the_input._get_render_context() # pylint: disable=protected-access
...@@ -174,13 +192,14 @@ class JavascriptInputTest(unittest.TestCase): ...@@ -174,13 +192,14 @@ class JavascriptInputTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2', 'id': 'prob_1_2',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
# 'label': '',
'msg': '', 'msg': '',
'value': '3', 'value': '3',
'params': params, 'params': params,
'display_file': display_file, 'display_file': display_file,
'display_class': display_class, 'display_class': display_class,
'problem_state': problem_state, 'problem_state': problem_state,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -193,11 +212,14 @@ class TextLineTest(unittest.TestCase): ...@@ -193,11 +212,14 @@ class TextLineTest(unittest.TestCase):
def test_rendering(self): def test_rendering(self):
size = "42" size = "42"
xml_str = """<textline id="prob_1_2" label="testing 123" size="{size}"/>""".format(size=size) xml_str = """<textline id="prob_1_2" size="{size}"/>""".format(size=size)
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee', } state = {
'value': 'BumbleBee',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('textline')(test_capa_system(), element, state) the_input = lookup_tag('textline')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=protected-access context = the_input._get_render_context() # pylint: disable=protected-access
...@@ -207,7 +229,6 @@ class TextLineTest(unittest.TestCase): ...@@ -207,7 +229,6 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'BumbleBee', 'value': 'BumbleBee',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
'label': 'testing 123',
'size': size, 'size': size,
'msg': '', 'msg': '',
'hidden': False, 'hidden': False,
...@@ -215,6 +236,8 @@ class TextLineTest(unittest.TestCase): ...@@ -215,6 +236,8 @@ class TextLineTest(unittest.TestCase):
'do_math': False, 'do_math': False,
'trailing_text': '', 'trailing_text': '',
'preprocessor': None, 'preprocessor': None,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -229,7 +252,10 @@ class TextLineTest(unittest.TestCase): ...@@ -229,7 +252,10 @@ class TextLineTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee', } state = {
'value': 'BumbleBee',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('textline')(test_capa_system(), element, state) the_input = lookup_tag('textline')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=protected-access context = the_input._get_render_context() # pylint: disable=protected-access
...@@ -239,7 +265,6 @@ class TextLineTest(unittest.TestCase): ...@@ -239,7 +265,6 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'BumbleBee', 'value': 'BumbleBee',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
'label': '',
'size': size, 'size': size,
'msg': '', 'msg': '',
'hidden': False, 'hidden': False,
...@@ -250,6 +275,8 @@ class TextLineTest(unittest.TestCase): ...@@ -250,6 +275,8 @@ class TextLineTest(unittest.TestCase):
'class_name': preprocessorClass, 'class_name': preprocessorClass,
'script_src': script, 'script_src': script,
}, },
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -273,7 +300,10 @@ class TextLineTest(unittest.TestCase): ...@@ -273,7 +300,10 @@ class TextLineTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee', } state = {
'value': 'BumbleBee',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('textline')(test_capa_system(), element, state) the_input = lookup_tag('textline')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=protected-access context = the_input._get_render_context() # pylint: disable=protected-access
...@@ -283,7 +313,6 @@ class TextLineTest(unittest.TestCase): ...@@ -283,7 +313,6 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'BumbleBee', 'value': 'BumbleBee',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
'label': '',
'size': size, 'size': size,
'msg': '', 'msg': '',
'hidden': False, 'hidden': False,
...@@ -291,6 +320,8 @@ class TextLineTest(unittest.TestCase): ...@@ -291,6 +320,8 @@ class TextLineTest(unittest.TestCase):
'do_math': False, 'do_math': False,
'trailing_text': expected_text, 'trailing_text': expected_text,
'preprocessor': None, 'preprocessor': None,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -312,9 +343,12 @@ class FileSubmissionTest(unittest.TestCase): ...@@ -312,9 +343,12 @@ class FileSubmissionTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee.py', state = {
'value': 'BumbleBee.py',
'status': 'incomplete', 'status': 'incomplete',
'feedback': {'message': '3'}, } 'feedback': {'message': '3'},
'response_data': RESPONSE_DATA
}
input_class = lookup_tag('filesubmission') input_class = lookup_tag('filesubmission')
the_input = input_class(test_capa_system(), element, state) the_input = input_class(test_capa_system(), element, state)
...@@ -324,12 +358,13 @@ class FileSubmissionTest(unittest.TestCase): ...@@ -324,12 +358,13 @@ class FileSubmissionTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2', 'id': 'prob_1_2',
'status': inputtypes.Status('queued'), 'status': inputtypes.Status('queued'),
'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'value': 'BumbleBee.py', 'value': 'BumbleBee.py',
'queue_len': '3', 'queue_len': '3',
'allowed_files': '["runme.py", "nooooo.rb", "ohai.java"]', 'allowed_files': '["runme.py", "nooooo.rb", "ohai.java"]',
'required_files': '["cookies.py"]', 'required_files': '["cookies.py"]',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -359,9 +394,12 @@ class CodeInputTest(unittest.TestCase): ...@@ -359,9 +394,12 @@ class CodeInputTest(unittest.TestCase):
escapedict = {'"': '&quot;'} escapedict = {'"': '&quot;'}
state = {'value': 'print "good evening"', state = {
'value': 'print "good evening"',
'status': 'incomplete', 'status': 'incomplete',
'feedback': {'message': '3'}, } 'feedback': {'message': '3'},
'response_data': RESPONSE_DATA
}
input_class = lookup_tag('codeinput') input_class = lookup_tag('codeinput')
the_input = input_class(test_capa_system(), element, state) the_input = input_class(test_capa_system(), element, state)
...@@ -373,7 +411,6 @@ class CodeInputTest(unittest.TestCase): ...@@ -373,7 +411,6 @@ class CodeInputTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': inputtypes.Status('queued'), 'status': inputtypes.Status('queued'),
# 'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'mode': mode, 'mode': mode,
'linenumbers': linenumbers, 'linenumbers': linenumbers,
...@@ -382,6 +419,8 @@ class CodeInputTest(unittest.TestCase): ...@@ -382,6 +419,8 @@ class CodeInputTest(unittest.TestCase):
'hidden': '', 'hidden': '',
'tabsize': int(tabsize), 'tabsize': int(tabsize),
'queue_len': '3', 'queue_len': '3',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -413,9 +452,12 @@ class MatlabTest(unittest.TestCase): ...@@ -413,9 +452,12 @@ class MatlabTest(unittest.TestCase):
payload=self.payload, payload=self.payload,
ln=self.linenumbers) ln=self.linenumbers)
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
state = {'value': 'print "good evening"', state = {
'value': 'print "good evening"',
'status': 'incomplete', 'status': 'incomplete',
'feedback': {'message': '3'}, } 'feedback': {'message': '3'},
'response_data': {}
}
self.input_class = lookup_tag('matlabinput') self.input_class = lookup_tag('matlabinput')
self.the_input = self.input_class(test_capa_system(), elt, state) self.the_input = self.input_class(test_capa_system(), elt, state)
...@@ -428,7 +470,6 @@ class MatlabTest(unittest.TestCase): ...@@ -428,7 +470,6 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': inputtypes.Status('queued'), 'status': inputtypes.Status('queued'),
# 'label': '',
'msg': self.the_input.submitted_msg, 'msg': self.the_input.submitted_msg,
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -440,15 +481,20 @@ class MatlabTest(unittest.TestCase): ...@@ -440,15 +481,20 @@ class MatlabTest(unittest.TestCase):
'button_enabled': True, 'button_enabled': True,
'queue_len': '3', 'queue_len': '3',
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js', 'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
'response_data': {},
'describedby': ''
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
def test_rendering_with_state(self): def test_rendering_with_state(self):
state = {'value': 'print "good evening"', state = {
'value': 'print "good evening"',
'status': 'incomplete', 'status': 'incomplete',
'input_state': {'queue_msg': 'message'}, 'input_state': {'queue_msg': 'message'},
'feedback': {'message': '3'}, } 'feedback': {'message': '3'},
'response_data': RESPONSE_DATA
}
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_capa_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
...@@ -459,7 +505,6 @@ class MatlabTest(unittest.TestCase): ...@@ -459,7 +505,6 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': inputtypes.Status('queued'), 'status': inputtypes.Status('queued'),
# 'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -471,15 +516,19 @@ class MatlabTest(unittest.TestCase): ...@@ -471,15 +516,19 @@ class MatlabTest(unittest.TestCase):
'button_enabled': True, 'button_enabled': True,
'queue_len': '3', 'queue_len': '3',
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js', 'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
def test_rendering_when_completed(self): def test_rendering_when_completed(self):
for status in ['correct', 'incorrect']: for status in ['correct', 'incorrect']:
state = {'value': 'print "good evening"', state = {
'value': 'print "good evening"',
'status': status, 'status': status,
'input_state': {}, 'input_state': {},
'response_data': RESPONSE_DATA
} }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
...@@ -490,7 +539,6 @@ class MatlabTest(unittest.TestCase): ...@@ -490,7 +539,6 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': inputtypes.Status(status), 'status': inputtypes.Status(status),
# 'label': '',
'msg': '', 'msg': '',
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -502,15 +550,19 @@ class MatlabTest(unittest.TestCase): ...@@ -502,15 +550,19 @@ class MatlabTest(unittest.TestCase):
'button_enabled': False, 'button_enabled': False,
'queue_len': '0', 'queue_len': '0',
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js', 'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
@patch('capa.inputtypes.time.time', return_value=10) @patch('capa.inputtypes.time.time', return_value=10)
def test_rendering_while_queued(self, time): def test_rendering_while_queued(self, time):
state = {'value': 'print "good evening"', state = {
'value': 'print "good evening"',
'status': 'incomplete', 'status': 'incomplete',
'input_state': {'queuestate': 'queued', 'queuetime': 5}, 'input_state': {'queuestate': 'queued', 'queuetime': 5},
'response_data': RESPONSE_DATA
} }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
...@@ -521,7 +573,6 @@ class MatlabTest(unittest.TestCase): ...@@ -521,7 +573,6 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': inputtypes.Status('queued'), 'status': inputtypes.Status('queued'),
# 'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -533,6 +584,8 @@ class MatlabTest(unittest.TestCase): ...@@ -533,6 +584,8 @@ class MatlabTest(unittest.TestCase):
'button_enabled': True, 'button_enabled': True,
'queue_len': '1', 'queue_len': '1',
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js', 'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -650,13 +703,13 @@ class MatlabTest(unittest.TestCase): ...@@ -650,13 +703,13 @@ class MatlabTest(unittest.TestCase):
textwrap.dedent(""" textwrap.dedent("""
<div>{\'status\': Status(\'queued\'), \'button_enabled\': True, <div>{\'status\': Status(\'queued\'), \'button_enabled\': True,
\'rows\': \'10\', \'queue_len\': \'3\', \'mode\': \'\', \'rows\': \'10\', \'queue_len\': \'3\', \'mode\': \'\',
\'cols\': \'80\', \'STATIC_URL\': \'/dummy-static/\', \'tabsize\': 4, \'cols\': \'80\', \'STATIC_URL\': \'/dummy-static/\',
\'linenumbers\': \'true\', \'queue_msg\': \'\', \'describedby\': \'\', \'queue_msg\': \'\',
\'value\': \'print "good evening"\', \'value\': \'print "good evening"\',
\'msg\': u\'Submitted. As soon as a response is returned, \'msg\': u\'Submitted. As soon as a response is returned,
this message will be replaced by that feedback.\', this message will be replaced by that feedback.\',
\'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\', \'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\',
\'hidden\': \'\', \'id\': \'prob_1_2\', \'tabsize\': 4}</div> \'hidden\': \'\', \'linenumbers\': \'true\', \'id\': \'prob_1_2\', \'response_data\': {}}</div>
""").replace('\n', ' ').strip() """).replace('\n', ' ').strip()
) )
...@@ -724,10 +777,13 @@ class MatlabTest(unittest.TestCase): ...@@ -724,10 +777,13 @@ class MatlabTest(unittest.TestCase):
</div><ul></ul></div> </div><ul></ul></div>
""") """)
state = {'value': 'print "good evening"', state = {
'value': 'print "good evening"',
'status': 'incomplete', 'status': 'incomplete',
'input_state': {'queue_msg': queue_msg}, 'input_state': {'queue_msg': queue_msg},
'feedback': {'message': '3'}, } 'feedback': {'message': '3'},
'response_data': RESPONSE_DATA
}
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_capa_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
...@@ -759,6 +815,8 @@ class MatlabTest(unittest.TestCase): ...@@ -759,6 +815,8 @@ class MatlabTest(unittest.TestCase):
'button_enabled': True, 'button_enabled': True,
'queue_len': '3', 'queue_len': '3',
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js', 'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
'response_data': {},
'describedby': ''
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -845,8 +903,11 @@ class SchematicTest(unittest.TestCase): ...@@ -845,8 +903,11 @@ class SchematicTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
value = 'three resistors and an oscilating pendulum' value = 'three resistors and an oscilating pendulum'
state = {'value': value, state = {
'status': 'unsubmitted'} 'value': value,
'status': 'unsubmitted',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('schematic')(test_capa_system(), element, state) the_input = lookup_tag('schematic')(test_capa_system(), element, state)
...@@ -857,7 +918,6 @@ class SchematicTest(unittest.TestCase): ...@@ -857,7 +918,6 @@ class SchematicTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': inputtypes.Status('unsubmitted'), 'status': inputtypes.Status('unsubmitted'),
'label': '',
'msg': '', 'msg': '',
'initial_value': initial_value, 'initial_value': initial_value,
'width': width, 'width': width,
...@@ -866,6 +926,8 @@ class SchematicTest(unittest.TestCase): ...@@ -866,6 +926,8 @@ class SchematicTest(unittest.TestCase):
'setup_script': '/dummy-static/js/capa/schematicinput.js', 'setup_script': '/dummy-static/js/capa/schematicinput.js',
'analyses': analyses, 'analyses': analyses,
'submit_analyses': submit_analyses, 'submit_analyses': submit_analyses,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -889,8 +951,11 @@ class ImageInputTest(unittest.TestCase): ...@@ -889,8 +951,11 @@ class ImageInputTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': value, state = {
'status': 'unsubmitted'} 'value': value,
'status': 'unsubmitted',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('imageinput')(test_capa_system(), element, state) the_input = lookup_tag('imageinput')(test_capa_system(), element, state)
...@@ -901,13 +966,14 @@ class ImageInputTest(unittest.TestCase): ...@@ -901,13 +966,14 @@ class ImageInputTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': inputtypes.Status('unsubmitted'), 'status': inputtypes.Status('unsubmitted'),
'label': '',
'width': width, 'width': width,
'height': height, 'height': height,
'src': src, 'src': src,
'gx': egx, 'gx': egx,
'gy': egy, 'gy': egy,
'msg': '', 'msg': '',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -944,8 +1010,11 @@ class CrystallographyTest(unittest.TestCase): ...@@ -944,8 +1010,11 @@ class CrystallographyTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
value = 'abc' value = 'abc'
state = {'value': value, state = {
'status': 'unsubmitted'} 'value': value,
'status': 'unsubmitted',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('crystallography')(test_capa_system(), element, state) the_input = lookup_tag('crystallography')(test_capa_system(), element, state)
...@@ -956,10 +1025,11 @@ class CrystallographyTest(unittest.TestCase): ...@@ -956,10 +1025,11 @@ class CrystallographyTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': inputtypes.Status('unsubmitted'), 'status': inputtypes.Status('unsubmitted'),
# 'label': '',
'msg': '', 'msg': '',
'width': width, 'width': width,
'height': height, 'height': height,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -986,8 +1056,11 @@ class VseprTest(unittest.TestCase): ...@@ -986,8 +1056,11 @@ class VseprTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
value = 'abc' value = 'abc'
state = {'value': value, state = {
'status': 'unsubmitted'} 'value': value,
'status': 'unsubmitted',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('vsepr_input')(test_capa_system(), element, state) the_input = lookup_tag('vsepr_input')(test_capa_system(), element, state)
...@@ -1003,6 +1076,8 @@ class VseprTest(unittest.TestCase): ...@@ -1003,6 +1076,8 @@ class VseprTest(unittest.TestCase):
'height': height, 'height': height,
'molecules': molecules, 'molecules': molecules,
'geometries': geometries, 'geometries': geometries,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -1019,7 +1094,10 @@ class ChemicalEquationTest(unittest.TestCase): ...@@ -1019,7 +1094,10 @@ class ChemicalEquationTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'H2OYeah', } state = {
'value': 'H2OYeah',
'response_data': RESPONSE_DATA
}
self.the_input = lookup_tag('chemicalequationinput')(test_capa_system(), element, state) self.the_input = lookup_tag('chemicalequationinput')(test_capa_system(), element, state)
def test_rendering(self): def test_rendering(self):
...@@ -1031,10 +1109,11 @@ class ChemicalEquationTest(unittest.TestCase): ...@@ -1031,10 +1109,11 @@ class ChemicalEquationTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'H2OYeah', 'value': 'H2OYeah',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
'label': '',
'msg': '', 'msg': '',
'size': self.size, 'size': self.size,
'previewer': '/dummy-static/js/capa/chemical_equation_preview.js', 'previewer': '/dummy-static/js/capa/chemical_equation_preview.js',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -1106,7 +1185,10 @@ class FormulaEquationTest(unittest.TestCase): ...@@ -1106,7 +1185,10 @@ class FormulaEquationTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'x^2+1/2'} state = {
'value': 'x^2+1/2',
'response_data': RESPONSE_DATA
}
self.the_input = lookup_tag('formulaequationinput')(test_capa_system(), element, state) self.the_input = lookup_tag('formulaequationinput')(test_capa_system(), element, state)
def test_rendering(self): def test_rendering(self):
...@@ -1120,12 +1202,13 @@ class FormulaEquationTest(unittest.TestCase): ...@@ -1120,12 +1202,13 @@ class FormulaEquationTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'x^2+1/2', 'value': 'x^2+1/2',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
'label': '',
'msg': '', 'msg': '',
'size': self.size, 'size': self.size,
'previewer': '/dummy-static/js/capa/src/formula_equation_preview.js', 'previewer': '/dummy-static/js/capa/src/formula_equation_preview.js',
'inline': False, 'inline': False,
'trailing_text': '', 'trailing_text': '',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -1152,7 +1235,10 @@ class FormulaEquationTest(unittest.TestCase): ...@@ -1152,7 +1235,10 @@ class FormulaEquationTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'x^2+1/2', } state = {
'value': 'x^2+1/2',
'response_data': RESPONSE_DATA
}
the_input = lookup_tag('formulaequationinput')(test_capa_system(), element, state) the_input = lookup_tag('formulaequationinput')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=protected-access context = the_input._get_render_context() # pylint: disable=protected-access
...@@ -1162,12 +1248,13 @@ class FormulaEquationTest(unittest.TestCase): ...@@ -1162,12 +1248,13 @@ class FormulaEquationTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'x^2+1/2', 'value': 'x^2+1/2',
'status': inputtypes.Status('unanswered'), 'status': inputtypes.Status('unanswered'),
'label': '',
'msg': '', 'msg': '',
'size': size, 'size': size,
'previewer': '/dummy-static/js/capa/src/formula_equation_preview.js', 'previewer': '/dummy-static/js/capa/src/formula_equation_preview.js',
'inline': False, 'inline': False,
'trailing_text': expected_text, 'trailing_text': expected_text,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.assertEqual(context, expected) self.assertEqual(context, expected)
...@@ -1263,8 +1350,11 @@ class DragAndDropTest(unittest.TestCase): ...@@ -1263,8 +1350,11 @@ class DragAndDropTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
value = 'abc' value = 'abc'
state = {'value': value, state = {
'status': 'unsubmitted'} 'value': value,
'status': 'unsubmitted',
'response_data': RESPONSE_DATA
}
user_input = { # order matters, for string comparison user_input = { # order matters, for string comparison
"target_outline": "false", "target_outline": "false",
...@@ -1293,9 +1383,10 @@ class DragAndDropTest(unittest.TestCase): ...@@ -1293,9 +1383,10 @@ class DragAndDropTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': inputtypes.Status('unsubmitted'), 'status': inputtypes.Status('unsubmitted'),
# 'label': '',
'msg': '', 'msg': '',
'drag_and_drop_json': json.dumps(user_input) 'drag_and_drop_json': json.dumps(user_input),
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
# as we are dumping 'draggables' dicts while dumping user_input, string # as we are dumping 'draggables' dicts while dumping user_input, string
...@@ -1332,7 +1423,8 @@ class AnnotationInputTest(unittest.TestCase): ...@@ -1332,7 +1423,8 @@ class AnnotationInputTest(unittest.TestCase):
state = { state = {
'value': json_value, 'value': json_value,
'id': 'annotation_input', 'id': 'annotation_input',
'status': 'answered' 'status': 'answered',
'response_data': RESPONSE_DATA
} }
tag = 'annotationinput' tag = 'annotationinput'
...@@ -1345,7 +1437,6 @@ class AnnotationInputTest(unittest.TestCase): ...@@ -1345,7 +1437,6 @@ class AnnotationInputTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'id': 'annotation_input', 'id': 'annotation_input',
'status': inputtypes.Status('answered'), 'status': inputtypes.Status('answered'),
# 'label': '',
'msg': '', 'msg': '',
'title': 'foo', 'title': 'foo',
'text': 'bar', 'text': 'bar',
...@@ -1362,7 +1453,9 @@ class AnnotationInputTest(unittest.TestCase): ...@@ -1362,7 +1453,9 @@ class AnnotationInputTest(unittest.TestCase):
'has_options_value': len(value['options']) > 0, 'has_options_value': len(value['options']) > 0,
'comment_value': value['comment'], 'comment_value': value['comment'],
'debug': False, 'debug': False,
'return_to_annotation': True 'return_to_annotation': True,
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
self.maxDiff = None self.maxDiff = None
...@@ -1405,6 +1498,7 @@ class TestChoiceText(unittest.TestCase): ...@@ -1405,6 +1498,7 @@ class TestChoiceText(unittest.TestCase):
'value': '{}', 'value': '{}',
'id': 'choicetext_input', 'id': 'choicetext_input',
'status': inputtypes.Status('answered'), 'status': inputtypes.Status('answered'),
'response_data': RESPONSE_DATA
} }
first_input = self.build_choice_element('numtolerance_input', 'choiceinput_0_textinput_0', 'false', '') first_input = self.build_choice_element('numtolerance_input', 'choiceinput_0_textinput_0', 'false', '')
...@@ -1421,11 +1515,12 @@ class TestChoiceText(unittest.TestCase): ...@@ -1421,11 +1515,12 @@ class TestChoiceText(unittest.TestCase):
expected = { expected = {
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'msg': '', 'msg': '',
'label': '',
'input_type': expected_input_type, 'input_type': expected_input_type,
'choices': choices, 'choices': choices,
'show_correctness': 'always', 'show_correctness': 'always',
'submitted_message': 'Answer received.' 'submitted_message': 'Answer received.',
'response_data': RESPONSE_DATA,
'describedby': DESCRIBEDBY
} }
expected.update(state) expected.update(state)
the_input = lookup_tag(tag)(test_capa_system(), element, state) the_input = lookup_tag(tag)(test_capa_system(), element, state)
......
...@@ -1256,7 +1256,6 @@ class CapaMixin(CapaFields): ...@@ -1256,7 +1256,6 @@ class CapaMixin(CapaFields):
of the problem. If problem related metadata cannot be located it should be replaced with empty of the problem. If problem related metadata cannot be located it should be replaced with empty
strings ''. strings ''.
""" """
input_metadata = {} input_metadata = {}
for input_id, internal_answer in answers.iteritems(): for input_id, internal_answer in answers.iteritems():
answer_input = self.lcp.inputs.get(input_id) answer_input = self.lcp.inputs.get(input_id)
...@@ -1290,7 +1289,7 @@ class CapaMixin(CapaFields): ...@@ -1290,7 +1289,7 @@ class CapaMixin(CapaFields):
is_correct = '' is_correct = ''
input_metadata[input_id] = { input_metadata[input_id] = {
'question': getattr(answer_input, 'loaded_attributes', {}).get('label', ''), 'question': answer_input.response_data.get('label', ''),
'answer': user_visible_answer, 'answer': user_visible_answer,
'response_type': getattr(getattr(answer_response, 'xml', None), 'tag', ''), 'response_type': getattr(getattr(answer_response, 'xml', None), 'tag', ''),
'input_type': getattr(answer_input, 'tag', ''), 'input_type': getattr(answer_input, 'tag', ''),
......
...@@ -153,11 +153,8 @@ div.problem { ...@@ -153,11 +153,8 @@ div.problem {
} }
} }
span > label { .question-description {
display: block; @include margin(($baseline*0.75), 0);
margin-bottom: $baseline;
font: inherit;
color: inherit;
} }
} }
......
...@@ -235,7 +235,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -235,7 +235,7 @@ describe 'MarkdownEditingDescriptor', ->
<p>A multiple choice problem presents radio buttons for student input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.</p> <p>A multiple choice problem presents radio buttons for student input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.</p>
<p>One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make.</p> <p>One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make.</p>
<label>What Apple device competed with the portable CD player?</label> <label>What Apple device competed with the portable CD player?</label>
<choicegroup label="What Apple device competed with the portable CD player?" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">The iPad</choice> <choice correct="false">The iPad</choice>
<choice correct="false">Napster</choice> <choice correct="false">Napster</choice>
<choice correct="true">The iPod</choice> <choice correct="true">The iPod</choice>
...@@ -488,7 +488,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -488,7 +488,7 @@ describe 'MarkdownEditingDescriptor', ->
<problem> <problem>
<stringresponse answer="w*.?s*Luther Kings*.*" type="ci regexp"> <stringresponse answer="w*.?s*Luther Kings*.*" type="ci regexp">
<label>Who lead the civil right movement in the United States of America?</label> <label>Who lead the civil right movement in the United States of America?</label>
<textline label="Who lead the civil right movement in the United States of America?" size="20"/> <textline size="20"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
...@@ -512,27 +512,26 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -512,27 +512,26 @@ describe 'MarkdownEditingDescriptor', ->
(x) Berlin (x) Berlin
( ) Donut ( ) Donut
""") """)
expect(data).toXMLEqual("""<problem> expect(data).toXMLEqual("""
<problem>
<p>France is a country in Europe.</p> <p>France is a country in Europe.</p>
<p>What is the capital of France?</p> <label>What is the capital of France?</label>
<stringresponse answer="Paris" type="ci" > <stringresponse answer="Paris" type="ci" >
<textline label="What is the capital of France?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<p>Germany is a country in Europe, too.</p> <p>Germany is a country in Europe, too.</p>
<p>What is the capital of Germany?</p> <label>What is the capital of Germany?</label>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="What is the capital of Germany?" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Bonn</choice> <choice correct="false">Bonn</choice>
<choice correct="false">Hamburg</choice> <choice correct="false">Hamburg</choice>
<choice correct="true">Berlin</choice> <choice correct="true">Berlin</choice>
<choice correct="false">Donut</choice> <choice correct="false">Donut</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
</problem>""") </problem>""")
it 'tests multiple questions with only one label', -> it 'tests multiple questions with only one label', ->
data = MarkdownEditingDescriptor.markdownToXml(""" data = MarkdownEditingDescriptor.markdownToXml("""
...@@ -549,12 +548,13 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -549,12 +548,13 @@ describe 'MarkdownEditingDescriptor', ->
(x) Berlin (x) Berlin
( ) Donut ( ) Donut
""") """)
expect(data).toXMLEqual("""<problem> expect(data).toXMLEqual("""
<problem>
<p>France is a country in Europe.</p> <p>France is a country in Europe.</p>
<p>What is the capital of France?</p> <label>What is the capital of France?</label>
<stringresponse answer="Paris" type="ci" > <stringresponse answer="Paris" type="ci" >
<textline label="What is the capital of France?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<p>Germany is a country in Europe, too.</p> <p>Germany is a country in Europe, too.</p>
...@@ -568,8 +568,6 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -568,8 +568,6 @@ describe 'MarkdownEditingDescriptor', ->
<choice correct="false">Donut</choice> <choice correct="false">Donut</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
</problem>""") </problem>""")
it 'adds labels to formulae', -> it 'adds labels to formulae', ->
...@@ -581,7 +579,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -581,7 +579,7 @@ describe 'MarkdownEditingDescriptor', ->
<numericalresponse answer="3.14159"> <numericalresponse answer="3.14159">
<label>Enter the numerical value of Pi:</label> <label>Enter the numerical value of Pi:</label>
<responseparam type="tolerance" default=".02"/> <responseparam type="tolerance" default=".02"/>
<formulaequationinput label="Enter the numerical value of Pi:"/> <formulaequationinput/>
</numericalresponse> </numericalresponse>
...@@ -752,7 +750,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -752,7 +750,7 @@ describe 'MarkdownEditingDescriptor', ->
<multiplechoiceresponse> <multiplechoiceresponse>
<p>Multiple choice problems allow learners to select only one option. Learners can see all the options along with the problem text.</p> <p>Multiple choice problems allow learners to select only one option. Learners can see all the options along with the problem text.</p>
<label>Which of the following countries has the largest population?</label> <label>Which of the following countries has the largest population?</label>
<choicegroup label="Which of the following countries has the largest population?" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint> <choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint>
</choice> </choice>
<choice correct="false">Germany</choice> <choice correct="false">Germany</choice>
...@@ -774,7 +772,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -774,7 +772,7 @@ describe 'MarkdownEditingDescriptor', ->
<choiceresponse> <choiceresponse>
<p>Checkbox problems allow learners to select multiple options. Learners can see all the options along with the problem text.</p> <p>Checkbox problems allow learners to select multiple options. Learners can see all the options along with the problem text.</p>
<label>The following languages are in the Indo-European family:</label> <label>The following languages are in the Indo-European family:</label>
<checkboxgroup label="The following languages are in the Indo-European family:"> <checkboxgroup>
<choice correct="true">Urdu</choice> <choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice> <choice correct="false">Finnish</choice>
<choice correct="true">Marathi</choice> <choice correct="true">Marathi</choice>
...@@ -818,7 +816,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -818,7 +816,7 @@ describe 'MarkdownEditingDescriptor', ->
<multiplechoiceresponse> <multiplechoiceresponse>
<label>Which of the following countries has the largest population?</label> <label>Which of the following countries has the largest population?</label>
<choicegroup label="Which of the following countries has the largest population?" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint> <choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint>
</choice> </choice>
<choice correct="false">Germany</choice> <choice correct="false">Germany</choice>
...@@ -839,9 +837,9 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -839,9 +837,9 @@ describe 'MarkdownEditingDescriptor', ->
</problem> </problem>
""") """)
it 'can do separation if spaces are prsent around ---', -> it 'can do separation if spaces are present around ---', ->
data = MarkdownEditingDescriptor.markdownToXml(""" data = MarkdownEditingDescriptor.markdownToXml("""
>>The following languages are in the Indo-European family:<< >>The following languages are in the Indo-European family:||There are three correct choices.<<
[x] Urdu [x] Urdu
[ ] Finnish [ ] Finnish
[x] Marathi [x] Marathi
...@@ -850,7 +848,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -850,7 +848,7 @@ describe 'MarkdownEditingDescriptor', ->
--- ---
>>Which of the following countries has the largest population?<< >>Which of the following countries has the largest population?||You have only choice.<<
( ) Brazil {{ timely feedback -- explain why an almost correct answer is wrong }} ( ) Brazil {{ timely feedback -- explain why an almost correct answer is wrong }}
( ) Germany ( ) Germany
(x) Indonesia (x) Indonesia
...@@ -860,7 +858,8 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -860,7 +858,8 @@ describe 'MarkdownEditingDescriptor', ->
<problem> <problem>
<choiceresponse> <choiceresponse>
<label>The following languages are in the Indo-European family:</label> <label>The following languages are in the Indo-European family:</label>
<checkboxgroup label="The following languages are in the Indo-European family:"> <description>There are three correct choices.</description>
<checkboxgroup>
<choice correct="true">Urdu</choice> <choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice> <choice correct="false">Finnish</choice>
<choice correct="true">Marathi</choice> <choice correct="true">Marathi</choice>
...@@ -869,19 +868,87 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -869,19 +868,87 @@ describe 'MarkdownEditingDescriptor', ->
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<multiplechoiceresponse> <multiplechoiceresponse>
<label>Which of the following countries has the largest population?</label> <label>Which of the following countries has the largest population?</label>
<choicegroup label="Which of the following countries has the largest population?" type="MultipleChoice"> <description>You have only choice.</description>
<choice correct="false">Brazil <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint> <choicegroup type="MultipleChoice">
<choice correct="false">Brazil
<choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint>
</choice> </choice>
<choice correct="false">Germany</choice> <choice correct="false">Germany</choice>
<choice correct="true">Indonesia</choice> <choice correct="true">Indonesia</choice>
<choice correct="false">Russia</choice> <choice correct="false">Russia</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
</problem>
""")
it 'can extract question description', ->
data = MarkdownEditingDescriptor.markdownToXml("""
>>The following languages are in the Indo-European family:||Choose wisely.<<
[x] Urdu
[ ] Finnish
[x] Marathi
[x] French
[ ] Hungarian
""")
expect(data).toXMLEqual("""
<problem>
<choiceresponse>
<label>The following languages are in the Indo-European family:</label>
<description>Choose wisely.</description>
<checkboxgroup>
<choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice>
<choice correct="true">Marathi</choice>
<choice correct="true">French</choice>
<choice correct="false">Hungarian</choice>
</checkboxgroup>
</choiceresponse>
</problem>
""")
it 'can handle question and description spanned across multiple lines', ->
data = MarkdownEditingDescriptor.markdownToXml("""
>>The following languages
are in the
Indo-European family:
||
first second
third
<<
[x] Urdu
[ ] Finnish
[x] Marathi
""")
expect(data).toXMLEqual("""
<problem>
<choiceresponse>
<label>The following languages are in the Indo-European family:</label>
<description>first second third</description>
<checkboxgroup>
<choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice>
<choice correct="true">Marathi</choice>
</checkboxgroup>
</choiceresponse>
</problem>
""")
it 'will not add empty description', ->
data = MarkdownEditingDescriptor.markdownToXml("""
>>The following languages are in the Indo-European family:||<<
[x] Urdu
[ ] Finnish
""")
expect(data).toXMLEqual("""
<problem>
<choiceresponse>
<label>The following languages are in the Indo-European family:</label>
<checkboxgroup>
<choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice>
</checkboxgroup>
</choiceresponse>
</problem> </problem>
""") """)
...@@ -119,7 +119,7 @@ describe 'Markdown to xml extended hint dropdown', -> ...@@ -119,7 +119,7 @@ describe 'Markdown to xml extended hint dropdown', ->
<problem> <problem>
<optionresponse> <optionresponse>
<label>q1</label> <label>q1</label>
<optioninput label="q1"> <optioninput>
<option correct="True">aa <optionhint>hint1</optionhint> <option correct="True">aa <optionhint>hint1</optionhint>
</option> </option>
<option correct="False">bb</option> <option correct="False">bb</option>
...@@ -149,7 +149,7 @@ describe 'Markdown to xml extended hint dropdown', -> ...@@ -149,7 +149,7 @@ describe 'Markdown to xml extended hint dropdown', ->
<problem> <problem>
<optionresponse> <optionresponse>
<label>q1</label> <label>q1</label>
<optioninput label="q1"> <optioninput>
<option correct="False">aa <optionhint>hint1</optionhint> <option correct="False">aa <optionhint>hint1</optionhint>
</option> </option>
<option correct="False">bb <optionhint>hint2</optionhint> <option correct="False">bb <optionhint>hint2</optionhint>
...@@ -191,50 +191,54 @@ describe 'Markdown to xml extended hint checkbox', -> ...@@ -191,50 +191,54 @@ describe 'Markdown to xml extended hint checkbox', ->
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>Select all the fruits from the list</p> <label>Select all the fruits from the list</label>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the fruits from the list"> <checkboxgroup>
<choice correct="true">Apple <choice correct="true">Apple
<choicehint selected="true">You're right that apple is a fruit.</choicehint> <choicehint selected="true">You're right that apple is a fruit.</choicehint>
<choicehint selected="false">Remember that apple is also a fruit.</choicehint></choice> <choicehint selected="false">Remember that apple is also a fruit.</choicehint>
</choice>
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint selected="true">Mushroom is a fungus, not a fruit.</choicehint> <choicehint selected="true">Mushroom is a fungus, not a fruit.</choicehint>
<choicehint selected="false">You're right that mushrooms aren't fruit</choicehint></choice> <choicehint selected="false">You're right that mushrooms aren't fruit</choicehint>
</choice>
<choice correct="true">Grape <choice correct="true">Grape
<choicehint selected="true">You're right that grape is a fruit</choicehint> <choicehint selected="true">You're right that grape is a fruit</choicehint>
<choicehint selected="false">Remember that grape is also a fruit.</choicehint></choice> <choicehint selected="false">Remember that grape is also a fruit.</choicehint>
</choice>
<choice correct="false">Mustang</choice> <choice correct="false">Mustang</choice>
<choice correct="false">Camero <choice correct="false">Camero
<choicehint selected="true">I don't know what a Camero is but it isn't a fruit.</choicehint> <choicehint selected="true">I don't know what a Camero is but it isn't a fruit.</choicehint>
<choicehint selected="false">What is a camero anyway?</choicehint></choice> <choicehint selected="false">What is a camero anyway?</choicehint>
</choice>
<compoundhint value="A*B">You're right that apple is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint> <compoundhint value="A*B">You're right that apple is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint>
<compoundhint value="B*C">You're right that grape is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint> <compoundhint value="B*C">You're right that grape is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<p>Select all the vegetables from the list</p> <label>Select all the vegetables from the list</label>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the vegetables from the list"> <checkboxgroup>
<choice correct="false">Banana <choice correct="false">Banana
<choicehint selected="true">No, sorry, a banana is a fruit.</choicehint> <choicehint selected="true">No, sorry, a banana is a fruit.</choicehint>
<choicehint selected="false">poor banana.</choicehint></choice> <choicehint selected="false">poor banana.</choicehint>
</choice>
<choice correct="false">Ice Cream</choice> <choice correct="false">Ice Cream</choice>
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint selected="true">Mushroom is a fungus, not a vegetable.</choicehint> <choicehint selected="true">Mushroom is a fungus, not a vegetable.</choicehint>
<choicehint selected="false">You're right that mushrooms aren't vegetables.</choicehint></choice> <choicehint selected="false">You're right that mushrooms aren't vegetables.</choicehint>
</choice>
<choice correct="true">Brussel Sprout <choice correct="true">Brussel Sprout
<choicehint selected="true">Brussel sprouts are vegetables.</choicehint> <choicehint selected="true">Brussel sprouts are vegetables.</choicehint>
<choicehint selected="false">Brussel sprout is the only vegetable in this list.</choicehint></choice> <choicehint selected="false">Brussel sprout is the only vegetable in this list.</choicehint>
</choice>
<compoundhint value="A*B">Making a banana split?</compoundhint> <compoundhint value="A*B">Making a banana split?</compoundhint>
<compoundhint value="B*D">That will make a horrible dessert: a brussel sprout split?</compoundhint> <compoundhint value="B*D">That will make a horrible dessert: a brussel sprout split?</compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
</problem> </problem>
""") """)
it 'produces xml also with demand hints', -> it 'produces xml also with demand hints', ->
data = MarkdownEditingDescriptor.markdownToXml(""" data = MarkdownEditingDescriptor.markdownToXml("""
>>Select all the fruits from the list<< >>Select all the fruits from the list<<
...@@ -245,12 +249,9 @@ describe 'Markdown to xml extended hint checkbox', -> ...@@ -245,12 +249,9 @@ describe 'Markdown to xml extended hint checkbox', ->
[ ] Mustang [ ] Mustang
[ ] Camero {{S:I don't know what a Camero is but it isn't a fruit.},{U:What is a camero anyway?}} [ ] Camero {{S:I don't know what a Camero is but it isn't a fruit.},{U:What is a camero anyway?}}
{{ ((A*B)) You're right that apple is a fruit, but there's one you're missing. Also, mushroom is not a fruit.}} {{ ((A*B)) You're right that apple is a fruit, but there's one you're missing. Also, mushroom is not a fruit.}}
{{ ((B*C)) You're right that grape is a fruit, but there's one you're missing. Also, mushroom is not a fruit.}} {{ ((B*C)) You're right that grape is a fruit, but there's one you're missing. Also, mushroom is not a fruit.}}
>>Select all the vegetables from the list<< >>Select all the vegetables from the list<<
[ ] Banana {{ selected: No, sorry, a banana is a fruit. }, {unselected: poor banana.}} [ ] Banana {{ selected: No, sorry, a banana is a fruit. }, {unselected: poor banana.}}
...@@ -258,57 +259,61 @@ describe 'Markdown to xml extended hint checkbox', -> ...@@ -258,57 +259,61 @@ describe 'Markdown to xml extended hint checkbox', ->
[ ] Mushroom {{U: You're right that mushrooms aren't vegatbles}, { selected: Mushroom is a fungus, not a vegetable.}} [ ] Mushroom {{U: You're right that mushrooms aren't vegatbles}, { selected: Mushroom is a fungus, not a vegetable.}}
[x] Brussel Sprout {{S: Brussel sprouts are vegetables.}, {u: Brussel sprout is the only vegetable in this list.}} [x] Brussel Sprout {{S: Brussel sprouts are vegetables.}, {u: Brussel sprout is the only vegetable in this list.}}
{{ ((A*B)) Making a banana split? }} {{ ((A*B)) Making a banana split? }}
{{ ((B*D)) That will make a horrible dessert: a brussel sprout split? }} {{ ((B*D)) That will make a horrible dessert: a brussel sprout split? }}
|| Hint one.|| || Hint one.||
|| Hint two. || || Hint two. ||
|| Hint three. || || Hint three. ||
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>Select all the fruits from the list</p> <label>Select all the fruits from the list</label>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the fruits from the list"> <checkboxgroup>
<choice correct="true">Apple <choice correct="true">Apple
<choicehint selected="true">You're right that apple is a fruit.</choicehint> <choicehint selected="true">You're right that apple is a fruit.</choicehint>
<choicehint selected="false">Remember that apple is also a fruit.</choicehint></choice> <choicehint selected="false">Remember that apple is also a fruit.</choicehint>
</choice>
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint selected="true">Mushroom is a fungus, not a fruit.</choicehint> <choicehint selected="true">Mushroom is a fungus, not a fruit.</choicehint>
<choicehint selected="false">You're right that mushrooms aren't fruit</choicehint></choice> <choicehint selected="false">You're right that mushrooms aren't fruit</choicehint>
</choice>
<choice correct="true">Grape <choice correct="true">Grape
<choicehint selected="true">You're right that grape is a fruit</choicehint> <choicehint selected="true">You're right that grape is a fruit</choicehint>
<choicehint selected="false">Remember that grape is also a fruit.</choicehint></choice> <choicehint selected="false">Remember that grape is also a fruit.</choicehint>
</choice>
<choice correct="false">Mustang</choice> <choice correct="false">Mustang</choice>
<choice correct="false">Camero <choice correct="false">Camero
<choicehint selected="true">I don't know what a Camero is but it isn't a fruit.</choicehint> <choicehint selected="true">I don't know what a Camero is but it isn't a fruit.</choicehint>
<choicehint selected="false">What is a camero anyway?</choicehint></choice> <choicehint selected="false">What is a camero anyway?</choicehint>
</choice>
<compoundhint value="A*B">You're right that apple is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint> <compoundhint value="A*B">You're right that apple is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint>
<compoundhint value="B*C">You're right that grape is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint> <compoundhint value="B*C">You're right that grape is a fruit, but there's one you're missing. Also, mushroom is not a fruit.</compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<p>Select all the vegetables from the list</p> <label>Select all the vegetables from the list</label>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Select all the vegetables from the list"> <checkboxgroup>
<choice correct="false">Banana <choice correct="false">Banana
<choicehint selected="true">No, sorry, a banana is a fruit.</choicehint> <choicehint selected="true">No, sorry, a banana is a fruit.</choicehint>
<choicehint selected="false">poor banana.</choicehint></choice> <choicehint selected="false">poor banana.</choicehint>
</choice>
<choice correct="false">Ice Cream</choice> <choice correct="false">Ice Cream</choice>
<choice correct="false">Mushroom <choice correct="false">Mushroom
<choicehint selected="true">Mushroom is a fungus, not a vegetable.</choicehint> <choicehint selected="true">Mushroom is a fungus, not a vegetable.</choicehint>
<choicehint selected="false">You're right that mushrooms aren't vegatbles</choicehint></choice> <choicehint selected="false">You're right that mushrooms aren't vegatbles</choicehint>
</choice>
<choice correct="true">Brussel Sprout <choice correct="true">Brussel Sprout
<choicehint selected="true">Brussel sprouts are vegetables.</choicehint> <choicehint selected="true">Brussel sprouts are vegetables.</choicehint>
<choicehint selected="false">Brussel sprout is the only vegetable in this list.</choicehint></choice> <choicehint selected="false">Brussel sprout is the only vegetable in this list.</choicehint>
</choice>
<compoundhint value="A*B">Making a banana split?</compoundhint> <compoundhint value="A*B">Making a banana split?</compoundhint>
<compoundhint value="B*D">That will make a horrible dessert: a brussel sprout split?</compoundhint> <compoundhint value="B*D">That will make a horrible dessert: a brussel sprout split?</compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<demandhint> <demandhint>
<hint>Hint one.</hint> <hint>Hint one.</hint>
<hint>Hint two.</hint> <hint>Hint two.</hint>
...@@ -335,25 +340,33 @@ describe 'Markdown to xml extended hint multiple choice', -> ...@@ -335,25 +340,33 @@ describe 'Markdown to xml extended hint multiple choice', ->
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>Select the fruit from the list</p> <label>Select the fruit from the list</label>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the fruit from the list" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choicehint>Mushroom is a fungus, not a fruit.</choicehint></choice> <choice correct="false">Mushroom
<choicehint>Mushroom is a fungus, not a fruit.</choicehint>
</choice>
<choice correct="false">Potato</choice> <choice correct="false">Potato</choice>
<choice correct="true">Apple <choicehint label="OUTSTANDING">Apple is indeed a fruit.</choicehint></choice> <choice correct="true">Apple
<choicehint label="OUTSTANDING">Apple is indeed a fruit.</choicehint>
</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<p>Select the vegetables from the list</p> <label>Select the vegetables from the list</label>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the vegetables from the list" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choicehint>Mushroom is a fungus, not a vegetable.</choicehint></choice> <choice correct="false">Mushroom
<choice correct="true">Potato <choicehint>Potato is a root vegetable.</choicehint></choice> <choicehint>Mushroom is a fungus, not a vegetable.</choicehint>
<choice correct="false">Apple <choicehint label="OOPS">Apple is a fruit.</choicehint></choice> </choice>
<choice correct="true">Potato
<choicehint>Potato is a root vegetable.</choicehint>
</choice>
<choice correct="false">Apple
<choicehint label="OOPS">Apple is a fruit.</choicehint>
</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
</problem> </problem>
""") """)
...@@ -365,43 +378,48 @@ describe 'Markdown to xml extended hint multiple choice', -> ...@@ -365,43 +378,48 @@ describe 'Markdown to xml extended hint multiple choice', ->
() Potato () Potato
(x) Apple {{ OUTSTANDING::Apple is indeed a fruit.}} (x) Apple {{ OUTSTANDING::Apple is indeed a fruit.}}
|| 0) spaces on previous line. || || 0) spaces on previous line. ||
|| 1) roses are red. || || 1) roses are red. ||
>>Select the vegetables from the list<< >>Select the vegetables from the list<<
() Mushroom {{ Mushroom is a fungus, not a vegetable.}} () Mushroom {{ Mushroom is a fungus, not a vegetable.}}
(x) Potato {{ Potato is a root vegetable. }} (x) Potato {{ Potato is a root vegetable. }}
() Apple {{ OOPS::Apple is a fruit.}} () Apple {{ OOPS::Apple is a fruit.}}
|| 2) where are the lions? || || 2) where are the lions? ||
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>Select the fruit from the list</p> <label>Select the fruit from the list</label>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the fruit from the list" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choicehint>Mushroom is a fungus, not a fruit.</choicehint></choice> <choice correct="false">Mushroom
<choicehint>Mushroom is a fungus, not a fruit.</choicehint>
</choice>
<choice correct="false">Potato</choice> <choice correct="false">Potato</choice>
<choice correct="true">Apple <choicehint label="OUTSTANDING">Apple is indeed a fruit.</choicehint></choice> <choice correct="true">Apple
<choicehint label="OUTSTANDING">Apple is indeed a fruit.</choicehint>
</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<p>Select the vegetables from the list</p> <label>Select the vegetables from the list</label>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Select the vegetables from the list" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Mushroom <choicehint>Mushroom is a fungus, not a vegetable.</choicehint></choice> <choice correct="false">Mushroom
<choice correct="true">Potato <choicehint>Potato is a root vegetable.</choicehint></choice> <choicehint>Mushroom is a fungus, not a vegetable.</choicehint>
<choice correct="false">Apple <choicehint label="OOPS">Apple is a fruit.</choicehint></choice> </choice>
<choice correct="true">Potato
<choicehint>Potato is a root vegetable.</choicehint>
</choice>
<choice correct="false">Apple
<choicehint label="OOPS">Apple is a fruit.</choicehint>
</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<demandhint> <demandhint>
<hint>0) spaces on previous line.</hint> <hint>0) spaces on previous line.</hint>
<hint>1) roses are red.</hint> <hint>1) roses are red.</hint>
...@@ -422,7 +440,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -422,7 +440,7 @@ describe 'Markdown to xml extended hint text input', ->
<stringresponse answer="France" type="ci"> <stringresponse answer="France" type="ci">
<label>In which country would you find the city of Paris?</label> <label>In which country would you find the city of Paris?</label>
<correcthint label="BRAVO">Viva la France!</correcthint> <correcthint label="BRAVO">Viva la France!</correcthint>
<textline label="In which country would you find the city of Paris?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
...@@ -442,7 +460,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -442,7 +460,7 @@ describe 'Markdown to xml extended hint text input', ->
<correcthint label="BRAVO">hint1</correcthint> <correcthint label="BRAVO">hint1</correcthint>
<additional_answer answer="USA"><correcthint label="meh">hint2</correcthint> <additional_answer answer="USA"><correcthint label="meh">hint2</correcthint>
</additional_answer> </additional_answer>
<textline label="Where Paris?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
...@@ -461,7 +479,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -461,7 +479,7 @@ describe 'Markdown to xml extended hint text input', ->
<label>Revenge is a dish best served</label> <label>Revenge is a dish best served</label>
<correcthint>khaaaaaan!</correcthint> <correcthint>khaaaaaan!</correcthint>
<stringequalhint answer="warm">feedback2</stringequalhint> <stringequalhint answer="warm">feedback2</stringequalhint>
<textline label="Revenge is a dish best served" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
...@@ -478,7 +496,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -478,7 +496,7 @@ describe 'Markdown to xml extended hint text input', ->
<stringresponse answer="2" type="ci"> <stringresponse answer="2" type="ci">
<label>q</label> <label>q</label>
<correcthint>feedback1</correcthint> <correcthint>feedback1</correcthint>
<textline label="q" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
...@@ -501,7 +519,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -501,7 +519,7 @@ describe 'Markdown to xml extended hint text input', ->
</additional_answer> </additional_answer>
<stringequalhint answer="no">feedback2</stringequalhint> <stringequalhint answer="no">feedback2</stringequalhint>
<additional_answer answer="ccc"/> <additional_answer answer="ccc"/>
<textline label="q" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
...@@ -523,7 +541,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -523,7 +541,7 @@ describe 'Markdown to xml extended hint text input', ->
<additional_answer answer="bbb"><correcthint>feedback2</correcthint> <additional_answer answer="bbb"><correcthint>feedback2</correcthint>
</additional_answer> </additional_answer>
<additional_answer answer="ccc"/> <additional_answer answer="ccc"/>
<textline label="q" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
...@@ -531,23 +549,22 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -531,23 +549,22 @@ describe 'Markdown to xml extended hint text input', ->
""") """)
it 'produces xml with each = making a new question', -> it 'produces xml with each = making a new question', ->
data = MarkdownEditingDescriptor.markdownToXml(""">>q<< data = MarkdownEditingDescriptor.markdownToXml("""
>>q<<
= aaa = aaa
or= bbb or= bbb
s= ccc s= ccc
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>q</p> <label>q</label>
<stringresponse answer="aaa" type="ci" > <stringresponse answer="aaa" type="ci">
<additional_answer answer="bbb"></additional_answer> <additional_answer answer="bbb"></additional_answer>
<textline label="q" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<stringresponse answer="ccc" type="ci" > <stringresponse answer="ccc" type="ci">
<textline size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
</problem> </problem>
""") """)
...@@ -566,17 +583,15 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -566,17 +583,15 @@ describe 'Markdown to xml extended hint text input', ->
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>paragraph</p> <p>paragraph</p>
<p>q</p> <label>q</label>
<stringresponse answer="aaa" type="ci" > <stringresponse answer="aaa" type="ci">
<additional_answer answer="bbb"></additional_answer> <additional_answer answer="bbb"></additional_answer>
<textline label="q" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<stringresponse answer="ccc" type="ci" > <stringresponse answer="ccc" type="ci">
<textline size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<p>paragraph 2</p> <p>paragraph 2</p>
</problem> </problem>
""") """)
...@@ -591,15 +606,15 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -591,15 +606,15 @@ describe 'Markdown to xml extended hint text input', ->
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>paragraph</p> <p>paragraph</p>
<p>q</p> <label>q</label>
<p>or= aaa</p> <p>or= aaa</p>
<p>paragraph 2</p> <p>paragraph 2</p>
</problem> </problem>
""") """)
it 'produces xml with each = with feedback making a new question', -> it 'produces xml with each = with feedback making a new question', ->
data = MarkdownEditingDescriptor.markdownToXml(""">>q<< data = MarkdownEditingDescriptor.markdownToXml("""
>>q<<
s= aaa s= aaa
or= bbb {{feedback1}} or= bbb {{feedback1}}
= ccc {{feedback2}} = ccc {{feedback2}}
...@@ -607,16 +622,17 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -607,16 +622,17 @@ describe 'Markdown to xml extended hint text input', ->
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>q</p> <label>q</label>
<stringresponse answer="aaa" type="ci" > <stringresponse answer="aaa" type="ci">
<additional_answer answer="bbb"><correcthint>feedback1</correcthint></additional_answer> <additional_answer answer="bbb">
<textline label="q" size="20"/> <correcthint>feedback1</correcthint>
</additional_answer>
<textline size="20"/>
</stringresponse> </stringresponse>
<stringresponse answer="ccc" type="ci" > <stringresponse answer="ccc" type="ci">
<correcthint>feedback2</correcthint> <correcthint>feedback2</correcthint>
<textline size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
</problem> </problem>
""") """)
...@@ -633,7 +649,7 @@ describe 'Markdown to xml extended hint text input', -> ...@@ -633,7 +649,7 @@ describe 'Markdown to xml extended hint text input', ->
<stringresponse answer="France" type="ci"> <stringresponse answer="France" type="ci">
<label>Where Paris?</label> <label>Where Paris?</label>
<correcthint label="BRAVO">hint1</correcthint> <correcthint label="BRAVO">hint1</correcthint>
<textline label="Where Paris?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<demandhint> <demandhint>
...@@ -655,30 +671,27 @@ describe 'Markdown to xml extended hint numeric input', -> ...@@ -655,30 +671,27 @@ describe 'Markdown to xml extended hint numeric input', ->
>>Enter the number of fingers on a human hand<< >>Enter the number of fingers on a human hand<<
= 5 = 5
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>Enter the numerical value of Pi:</p> <label>Enter the numerical value of Pi:</label>
<numericalresponse answer="3.14159"> <numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" /> <responseparam type="tolerance" default=".02"/>
<formulaequationinput label="Enter the numerical value of Pi:" /> <formulaequationinput/>
<correcthint>Pie for everyone!</correcthint> <correcthint>Pie for everyone!</correcthint>
</numericalresponse> </numericalresponse>
<p>Enter the approximate value of 502*9:</p> <label>Enter the approximate value of 502*9:</label>
<numericalresponse answer="4518"> <numericalresponse answer="4518">
<responseparam type="tolerance" default="15%" /> <responseparam type="tolerance" default="15%"/>
<formulaequationinput label="Enter the approximate value of 502*9:" /> <formulaequationinput/>
<correcthint label="PIE">No pie for you!</correcthint> <correcthint label="PIE">No pie for you!</correcthint>
</numericalresponse> </numericalresponse>
<p>Enter the number of fingers on a human hand</p> <label>Enter the number of fingers on a human hand</label>
<numericalresponse answer="5"> <numericalresponse answer="5">
<formulaequationinput label="Enter the number of fingers on a human hand" /> <formulaequationinput/>
</numericalresponse> </numericalresponse>
</problem> </problem>
""") """)
...@@ -697,14 +710,14 @@ describe 'Markdown to xml extended hint numeric input', -> ...@@ -697,14 +710,14 @@ describe 'Markdown to xml extended hint numeric input', ->
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>text1</p> <label>text1</label>
<numericalresponse answer="1"> <numericalresponse answer="1">
<formulaequationinput label="text1" /> <formulaequationinput/>
<correcthint>hint1</correcthint> <correcthint>hint1</correcthint>
</numericalresponse> </numericalresponse>
<p>text2</p> <label>text2</label>
<numericalresponse answer="2"> <numericalresponse answer="2">
<formulaequationinput label="text2" /> <formulaequationinput/>
<correcthint>hint2</correcthint> <correcthint>hint2</correcthint>
</numericalresponse> </numericalresponse>
...@@ -765,47 +778,60 @@ describe 'Markdown to xml extended hint with multiline hints', -> ...@@ -765,47 +778,60 @@ describe 'Markdown to xml extended hint with multiline hints', ->
""") """)
expect(data).toXMLEqual(""" expect(data).toXMLEqual("""
<problem> <problem>
<p>Checkboxes</p> <label>Checkboxes</label>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Checkboxes"> <checkboxgroup>
<choice correct="true">A <choice correct="true">A
<choicehint selected="true">aaa</choicehint> <choicehint selected="true">aaa</choicehint>
<choicehint selected="false">bbb</choicehint></choice> <choicehint selected="false">bbb</choicehint>
</choice>
<choice correct="false">B <choice correct="false">B
<choicehint selected="true">d.</choicehint> <choicehint selected="true">d.</choicehint>
<choicehint selected="false">c</choicehint></choice> <choicehint selected="false">c</choicehint>
</choice>
<compoundhint value="A*B">A*B hint</compoundhint> <compoundhint value="A*B">A*B hint</compoundhint>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<p>What is 1 + 1?</p> <label>What is 1 + 1?</label>
<numericalresponse answer="2"> <numericalresponse answer="2">
<formulaequationinput label="What is 1 + 1?" /> <formulaequationinput/>
<correcthint>part one, and part two</correcthint> <correcthint>part one, and part two</correcthint>
</numericalresponse> </numericalresponse>
<p>hello?</p> <label>hello?</label>
<stringresponse answer="hello" type="ci" > <stringresponse answer="hello" type="ci">
<correcthint>hello hint</correcthint> <correcthint>hello hint</correcthint>
<textline label="hello?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<p>multiple choice</p> <label>multiple choice</label>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="multiple choice" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="true">AA <choicehint>hint1</choicehint></choice> <choice correct="true">AA
<choice correct="false">BB <choicehint>hint2</choicehint></choice> <choicehint>hint1</choicehint>
<choice correct="false">CC <choicehint>hint3</choicehint></choice> </choice>
<choice correct="false">BB
<choicehint>hint2</choicehint>
</choice>
<choice correct="false">CC
<choicehint>hint3</choicehint>
</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<p>dropdown</p> <label>dropdown</label>
<optionresponse> <optionresponse>
<optioninput label="dropdown"> <optioninput>
<option correct="False">W1 <optionhint>no</optionhint></option> <option correct="False">W1
<option correct="False">W2 <optionhint>nope</optionhint></option> <optionhint>no</optionhint>
<option correct="True">C1 <optionhint>yes</optionhint></option> </option>
<option correct="False">W2
<optionhint>nope</optionhint>
</option>
<option correct="True">C1
<optionhint>yes</optionhint>
</option>
</optioninput> </optioninput>
</optionresponse> </optionresponse>
...@@ -834,8 +860,9 @@ describe 'Markdown to xml extended hint with tricky syntax cases', -> ...@@ -834,8 +860,9 @@ describe 'Markdown to xml extended hint with tricky syntax cases', ->
<problem> <problem>
<multiplechoiceresponse> <multiplechoiceresponse>
<label>á and Ø</label> <label>á and Ø</label>
<choicegroup label="á and Ø" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="true">Ø <choicehint>Ø</choicehint> <choice correct="true">Ø
<choicehint>Ø</choicehint>
</choice> </choice>
<choice correct="false">BB</choice> <choice correct="false">BB</choice>
</choicegroup> </choicegroup>
...@@ -858,15 +885,15 @@ describe 'Markdown to xml extended hint with tricky syntax cases', -> ...@@ -858,15 +885,15 @@ describe 'Markdown to xml extended hint with tricky syntax cases', ->
<problem> <problem>
<multiplechoiceresponse> <multiplechoiceresponse>
<label>"quotes" aren't `fun`</label> <label>"quotes" aren't `fun`</label>
<choicegroup label="&quot;quotes&quot; aren't `fun`" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">"hello" <choicehint>isn't</choicehint> <choice correct="false">"hello"
<choicehint>isn't</choicehint>
</choice> </choice>
<choice correct="true">"isn't" <choicehint>"hello"</choicehint> <choice correct="true">"isn't"
<choicehint>"hello"</choicehint>
</choice> </choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
</problem> </problem>
""") """)
...@@ -883,7 +910,7 @@ describe 'Markdown to xml extended hint with tricky syntax cases', -> ...@@ -883,7 +910,7 @@ describe 'Markdown to xml extended hint with tricky syntax cases', ->
<multiplechoiceresponse> <multiplechoiceresponse>
<label>q1</label> <label>q1</label>
<p>this (x)</p> <p>this (x)</p>
<choicegroup label="q1" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">a <choicehint>(hint)</choicehint> <choice correct="false">a <choicehint>(hint)</choicehint>
</choice> </choice>
<choice correct="true">b</choice> <choice correct="true">b</choice>
...@@ -909,7 +936,7 @@ describe 'Markdown to xml extended hint with tricky syntax cases', -> ...@@ -909,7 +936,7 @@ describe 'Markdown to xml extended hint with tricky syntax cases', ->
<choiceresponse> <choiceresponse>
<label>q1</label> <label>q1</label>
<p>this [x]</p> <p>this [x]</p>
<checkboxgroup label="q1"> <checkboxgroup>
<choice correct="false">a [square]</choice> <choice correct="false">a [square]</choice>
<choice correct="true">b {{ this hint passes through }}</choice> <choice correct="true">b {{ this hint passes through }}</choice>
</checkboxgroup> </checkboxgroup>
...@@ -942,7 +969,7 @@ describe 'Markdown to xml extended hint with tricky syntax cases', -> ...@@ -942,7 +969,7 @@ describe 'Markdown to xml extended hint with tricky syntax cases', ->
<problem> <problem>
<optionresponse> <optionresponse>
<label>q22</label> <label>q22</label>
<optioninput label="q22"> <optioninput>
<option correct="True">x <optionhint>hintx these span</optionhint> <option correct="True">x <optionhint>hintx these span</optionhint>
</option> </option>
<option correct="False">yy <optionhint label="meh">hinty</optionhint> <option correct="False">yy <optionhint label="meh">hinty</optionhint>
......
...@@ -197,7 +197,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -197,7 +197,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
demandHintTags = []; demandHintTags = [];
toXml = `function (markdown) { toXml = `function (markdown) {
var xml = markdown, var xml = markdown,
i, splits, scriptFlag; i, splits, makeParagraph;
var responseTypes = [ var responseTypes = [
'optionresponse', 'multiplechoiceresponse', 'stringresponse', 'numericalresponse', 'choiceresponse' 'optionresponse', 'multiplechoiceresponse', 'stringresponse', 'numericalresponse', 'choiceresponse'
]; ];
...@@ -209,6 +209,20 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -209,6 +209,20 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
xml = xml.replace(/(^.*?$)(?=\n\=\=+$)/gm, '<h3 class="hd hd-2 problem-header">$1</h3>'); xml = xml.replace(/(^.*?$)(?=\n\=\=+$)/gm, '<h3 class="hd hd-2 problem-header">$1</h3>');
xml = xml.replace(/\n^\=\=+$/gm, ''); xml = xml.replace(/\n^\=\=+$/gm, '');
// extract question and description(optional)
// >>question||description<< converts to
// <label>question</label> <description>description</description>
xml = xml.replace(/>>([^]+?)<</gm, function(match, questionText) {
var result = questionText.split('||'),
label = '<label>' + result[0] + '</label>' + '\n';
// don't add empty <description> tag
if (result.length === 1 || !result[1]) {
return label;
}
return label + '<description>' + result[1] + '</description>\n'
})
// Pull out demand hints, || a hint || // Pull out demand hints, || a hint ||
var demandhints = ''; var demandhints = '';
xml = xml.replace(/(^\s*\|\|.*?\|\|\s*$\n?)+/gm, function(match) { // $\n xml = xml.replace(/(^\s*\|\|.*?\|\|\s*$\n?)+/gm, function(match) { // $\n
...@@ -515,35 +529,6 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -515,35 +529,6 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
return selectString; return selectString;
}); });
// replace labels
// looks for >>arbitrary text<< and inserts it into the label attribute of the input type directly below the text.
var split = xml.split('\n');
var new_xml = [];
var line, i, curlabel, prevlabel = '';
var didinput = false;
for (i = 0; i < split.length; i++) {
line = split[i];
if (match = line.match(/>>(.*)<</)) {
curlabel = match[1].replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
// extract the question text and convert it to a <p> tag
line = line.replace(/>>(.*?)<</, "<p class='qtitle'>$1</p>");
} else if (line.match(/<\w+response/) && didinput && curlabel == prevlabel) {
// reset label to prevent gobbling up previous one (if multiple questions)
curlabel = '';
didinput = false;
} else if (line.match(/<(textline|optioninput|formulaequationinput|choicegroup|checkboxgroup)/) && curlabel != '' && curlabel != undefined) {
line = line.replace(/<(textline|optioninput|formulaequationinput|choicegroup|checkboxgroup)/, '<$1 label="' + curlabel + '"');
didinput = true;
prevlabel = curlabel;
}
new_xml.push(line);
}
xml = new_xml.join('\n');
// replace code blocks // replace code blocks
xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) { xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) {
var selectString = '<pre><code>\n' + p1 + '</code></pre>'; var selectString = '<pre><code>\n' + p1 + '</code></pre>';
...@@ -552,20 +537,23 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -552,20 +537,23 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
}); });
// split scripts and preformatted sections, and wrap paragraphs // split scripts and preformatted sections, and wrap paragraphs
splits = xml.split(/(\<\/?(?:script|pre).*?\>)/g); splits = xml.split(/(\<\/?(?:script|pre|label|description).*?\>)/g);
scriptFlag = false;
// Wrap a string by <p> tag when line is not already wrapped by another tag
// true when line is not already wrapped by another tag false otherwise
makeParagraph = true;
for (i = 0; i < splits.length; i += 1) { for (i = 0; i < splits.length; i += 1) {
if(/\<(script|pre)/.test(splits[i])) { if (/\<(script|pre|label|description)/.test(splits[i])) {
scriptFlag = true; makeParagraph = false;
} }
if(!scriptFlag) { if (makeParagraph) {
splits[i] = splits[i].replace(/(^(?!\s*\<|$).*$)/gm, '<p>$1</p>'); splits[i] = splits[i].replace(/(^(?!\s*\<|$).*$)/gm, '<p>$1</p>');
} }
if(/\<\/(script|pre)/.test(splits[i])) { if (/\<\/(script|pre|label|description)/.test(splits[i])) {
scriptFlag = false; makeParagraph = true;
} }
} }
...@@ -600,11 +588,6 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -600,11 +588,6 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
return; return;
} }
// replace <p> tag for question title with <label> tag
if (child.hasAttribute('class') && child.getAttribute('class') === 'qtitle') {
child = $('<label>' + child.textContent + '</label>')[0];
}
if (beforeInputtype) { if (beforeInputtype) {
// safe-lint: disable=javascript-jquery-insert-into-target // safe-lint: disable=javascript-jquery-insert-into-target
responseType[0].insertBefore(child, inputtype); responseType[0].insertBefore(child, inputtype);
......
...@@ -8,15 +8,13 @@ metadata: ...@@ -8,15 +8,13 @@ metadata:
You can use the following example problem as a model. You can use the following example problem as a model.
>>The following languages are in the Indo-European family:<< >>The following languages are in the Indo-European family:||Make sure you select all of the correct options—there may be more than one!<<
[x] Urdu [x] Urdu
[ ] Finnish [ ] Finnish
[x] Marathi [x] Marathi
[x] French [x] French
[ ] Hungarian [ ] Hungarian
Note: Make sure you select all of the correct options—there may be more than one!
[explanation] [explanation]
Urdu, Marathi, and French are all Indo-European languages, while Finnish and Hungarian are in the Uralic family. Urdu, Marathi, and French are all Indo-European languages, while Finnish and Hungarian are in the Uralic family.
[explanation] [explanation]
...@@ -28,14 +26,14 @@ data: | ...@@ -28,14 +26,14 @@ data: |
<p>When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.</p> <p>When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>You can use the following example problem as a model.</p> <p>You can use the following example problem as a model.</p>
<label>The following languages are in the Indo-European family:</label> <label>The following languages are in the Indo-European family:</label>
<checkboxgroup label="The following languages are in the Indo-European family:"> <description>Make sure you select all of the correct options—there may be more than one!</description>
<checkboxgroup>
<choice correct="true">Urdu</choice> <choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice> <choice correct="false">Finnish</choice>
<choice correct="true">Marathi</choice> <choice correct="true">Marathi</choice>
<choice correct="true">French</choice> <choice correct="true">French</choice>
<choice correct="false">Hungarian</choice> <choice correct="false">Hungarian</choice>
</checkboxgroup> </checkboxgroup>
<p>Note: Make sure you select all of the correct options—there may be more than one!</p>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -13,7 +13,7 @@ metadata: ...@@ -13,7 +13,7 @@ metadata:
Use the following example problem as a model. Use the following example problem as a model.
>>Which of the following is a fruit? Check all that apply.<< >>Which of the following is a fruit?||Make sure you select all of the correct options—there may be more than one!<<
[x] apple {{ selected: You are correct that an apple is a fruit because it is the fertilized ovary that comes from an apple tree and contains seeds. }, { unselected: Remember that an apple is also a fruit.}} [x] apple {{ selected: You are correct that an apple is a fruit because it is the fertilized ovary that comes from an apple tree and contains seeds. }, { unselected: Remember that an apple is also a fruit.}}
[x] pumpkin {{ selected: You are correct that a pumpkin is a fruit because it is the fertilized ovary of a squash plant and contains seeds. }, { unselected: Remember that a pumpkin is also a fruit.}} [x] pumpkin {{ selected: You are correct that a pumpkin is a fruit because it is the fertilized ovary of a squash plant and contains seeds. }, { unselected: Remember that a pumpkin is also a fruit.}}
...@@ -36,8 +36,9 @@ data: | ...@@ -36,8 +36,9 @@ data: |
<p>You can also add hints for learners.</p> <p>You can also add hints for learners.</p>
<p>Be sure to select Settings to specify a Display Name and other values that apply.</p> <p>Be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>Use the following example problem as a model.</p> <p>Use the following example problem as a model.</p>
<label>Which of the following is a fruit? Check all that apply.</label> <label>Which of the following is a fruit?</label>
<checkboxgroup label="Which of the following is a fruit? Check all that apply."> <description>Make sure you select all of the correct options—there may be more than one!</description>
<checkboxgroup>
<choice correct="true">apple <choice correct="true">apple
<choicehint selected="true">You are correct that an apple is a fruit because it is the fertilized ovary that comes from an apple tree and contains seeds.</choicehint> <choicehint selected="true">You are correct that an apple is a fruit because it is the fertilized ovary that comes from an apple tree and contains seeds.</choicehint>
<choicehint selected="false">Remember that an apple is also a fruit.</choicehint> <choicehint selected="false">Remember that an apple is also a fruit.</choicehint>
......
...@@ -41,8 +41,8 @@ data: | ...@@ -41,8 +41,8 @@ data: |
</script> </script>
<label>Enter two integers that sum to 10.</label> <label>Enter two integers that sum to 10.</label>
<textline size="40" correct_answer="3" label="Integer #1"/><br/> <textline size="40" correct_answer="3"/><br/>
<textline size="40" correct_answer="7" label="Integer #2"/> <textline size="40" correct_answer="7"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
...@@ -64,8 +64,8 @@ data: | ...@@ -64,8 +64,8 @@ data: |
</script> </script>
<label>Enter two integers that sum to 20.</label> <label>Enter two integers that sum to 20.</label>
<textline size="40" correct_answer="11" label="Integer #1"/><br/> <textline size="40" correct_answer="11"/><br/>
<textline size="40" correct_answer="9" label="Integer #2"/> <textline size="40" correct_answer="9"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -34,8 +34,9 @@ data: | ...@@ -34,8 +34,9 @@ data: |
<formularesponse type="ci" samples="R_1,R_2,R_3@1,2,3:3,4,5#10" answer="$VoVi"> <formularesponse type="ci" samples="R_1,R_2,R_3@1,2,3:3,4,5#10" answer="$VoVi">
<label>Write an expression for the product of \( R_1\), \( R_2\), and the inverse of \( R_3\).</label> <label>Write an expression for the product of \( R_1\), \( R_2\), and the inverse of \( R_3\).</label>
<description>Enter the equation</description>
<responseparam type="tolerance" default="0.00001"/> <responseparam type="tolerance" default="0.00001"/>
<formulaequationinput size="40" label="Enter the equation"/> <formulaequationinput size="40"/>
</formularesponse> </formularesponse>
<script type="loncapa/python"> <script type="loncapa/python">
...@@ -48,7 +49,8 @@ data: | ...@@ -48,7 +49,8 @@ data: |
<formularesponse type="ci" samples="x,n@1,2:3,4#10" answer="$derivative"> <formularesponse type="ci" samples="x,n@1,2:3,4#10" answer="$derivative">
<label>Let \( x\) be a variable, and let \( n\) be an arbitrary constant. What is the derivative of \( x^n\)?</label> <label>Let \( x\) be a variable, and let \( n\) be an arbitrary constant. What is the derivative of \( x^n\)?</label>
<description>Enter the equation</description>
<responseparam type="tolerance" default="0.00001"/> <responseparam type="tolerance" default="0.00001"/>
<formulaequationinput size="40" label="Enter the equation"/> <formulaequationinput size="40"/>
</formularesponse> </formularesponse>
</problem> </problem>
...@@ -159,7 +159,7 @@ data: | ...@@ -159,7 +159,7 @@ data: |
<label>What was the first post-secondary school in China to allow both male and female students?</label> <label>What was the first post-secondary school in China to allow both male and female students?</label>
<additional_answer>National Central University</additional_answer> <additional_answer>National Central University</additional_answer>
<additional_answer>Nanjing University</additional_answer> <additional_answer>Nanjing University</additional_answer>
<textline label="What was the first post-secondary school in China to allow both male and female students?" size="40"/> <textline size="40"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
...@@ -177,8 +177,8 @@ data: | ...@@ -177,8 +177,8 @@ data: |
return test_add(10, ans) return test_add(10, ans)
</script> </script>
<label>Enter two integers that sum to 10.</label> <label>Enter two integers that sum to 10.</label>
<textline size="40" correct_answer="3" label="Integer #1"/><br/> <textline size="40" correct_answer="3"/><br/>
<textline size="40" correct_answer="7" label="Integer #2"/> <textline size="40" correct_answer="7"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
...@@ -198,8 +198,8 @@ data: | ...@@ -198,8 +198,8 @@ data: |
return False return False
</script> </script>
<label>Enter two integers that sum to 20.</label> <label>Enter two integers that sum to 20.</label>
<textline size="40" correct_answer="11" label="Integer #1"/><br/> <textline size="40" correct_answer="11"/><br/>
<textline size="40" correct_answer="9" label="Integer #2"/> <textline size="40" correct_answer="9"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -8,7 +8,7 @@ metadata: ...@@ -8,7 +8,7 @@ metadata:
You can use the following example problem as a model. You can use the following example problem as a model.
>>Which of the following countries has the largest population?<< >>Which of the following countries has the largest population?||You can select only one option.<<
( ) Brazil {{ timely feedback -- explain why an almost correct answer is wrong }} ( ) Brazil {{ timely feedback -- explain why an almost correct answer is wrong }}
( ) Germany ( ) Germany
(x) Indonesia (x) Indonesia
...@@ -29,7 +29,8 @@ data: | ...@@ -29,7 +29,8 @@ data: |
<p>When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.</p> <p>When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>You can use the following example problem as a model.</p> <p>You can use the following example problem as a model.</p>
<label>Which of the following countries has the largest population?</label> <label>Which of the following countries has the largest population?</label>
<choicegroup label="Which of the following countries has the largest population?" type="MultipleChoice"> <description>You can select only one option.</description>
<choicegroup type="MultipleChoice">
<choice correct="false">Brazil <choice correct="false">Brazil
<choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint> <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint>
</choice> </choice>
......
...@@ -11,7 +11,7 @@ metadata: ...@@ -11,7 +11,7 @@ metadata:
Use the following example problem as a model. Use the following example problem as a model.
>>Which of the following is a vegetable?<< >>Which of the following is a vegetable?||You can select only one option.<<
( ) apple {{An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.}} ( ) apple {{An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.}}
( ) pumpkin {{A pumpkin is the fertilized ovary of a squash plant and contains seeds, meaning it is a fruit.}} ( ) pumpkin {{A pumpkin is the fertilized ovary of a squash plant and contains seeds, meaning it is a fruit.}}
(x) potato {{A potato is an edible part of a plant in tuber form and is a vegetable.}} (x) potato {{A potato is an edible part of a plant in tuber form and is a vegetable.}}
...@@ -29,7 +29,8 @@ data: | ...@@ -29,7 +29,8 @@ data: |
<p>Be sure to select Settings to specify a Display Name and other values that apply.</p> <p>Be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>Use the following example problem as a model.</p> <p>Use the following example problem as a model.</p>
<label>Which of the following is a vegetable?</label> <label>Which of the following is a vegetable?</label>
<choicegroup label="Which of the following is a vegetable?" type="MultipleChoice"> <description>You can select only one option.</description>
<choicegroup>
<choice correct="false">apple <choice correct="false">apple
<choicehint>An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.</choicehint> <choicehint>An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.</choicehint>
</choice> </choice>
......
...@@ -10,7 +10,7 @@ metadata: ...@@ -10,7 +10,7 @@ metadata:
You can use the following example problems as models. You can use the following example problems as models.
>>How many miles away from Earth is the sun? Use scientific notation to answer.<< >>How many miles away from Earth is the sun?||Use scientific notation to answer.<<
= 9.3*10^7 = 9.3*10^7
or= 9.296*10^7 or= 9.296*10^7
...@@ -21,7 +21,7 @@ metadata: ...@@ -21,7 +21,7 @@ metadata:
--- ---
>>The square of what number is -100?<< >>The square of what number is -100?||Use scientific notation to answer.<<
= 10*i = 10*i
...@@ -37,8 +37,9 @@ data: | ...@@ -37,8 +37,9 @@ data: |
for information about how to enter text into the field.</p> for information about how to enter text into the field.</p>
<p>When you add the problem, be sure to select <strong>Settings</strong> to specify a <strong>Display Name</strong> and other values that apply.</p> <p>When you add the problem, be sure to select <strong>Settings</strong> to specify a <strong>Display Name</strong> and other values that apply.</p>
<p>You can use the following example problems as models.</p> <p>You can use the following example problems as models.</p>
<label>How many miles away from Earth is the sun? Use scientific notation to answer.</label> <label>How many miles away from Earth is the sun?</label>
<formulaequationinput label="How many million miles are between Earth and the sun? Use scientific notation to answer."/> <description>Use scientific notation to answer.</description>
<formulaequationinput/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
...@@ -49,7 +50,8 @@ data: | ...@@ -49,7 +50,8 @@ data: |
<numericalresponse answer="10*i"> <numericalresponse answer="10*i">
<label>The square of what number is -100?</label> <label>The square of what number is -100?</label>
<formulaequationinput label="The square of what number is -100?"/> <description>Use scientific notation to answer.</description>
<formulaequationinput/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -13,7 +13,7 @@ metadata: ...@@ -13,7 +13,7 @@ metadata:
Use the following example problem as a model. Use the following example problem as a model.
>>What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)<< >>What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)||Use scientific notation to answer.<<
= 4 {{The mean for this set of numbers is 20 / 5, which equals 4.}} = 4 {{The mean for this set of numbers is 20 / 5, which equals 4.}}
...@@ -34,7 +34,8 @@ data: | ...@@ -34,7 +34,8 @@ data: |
<p>Be sure to select Settings to specify a Display Name and other values that apply.</p> <p>Be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>Use the following example problem as a model.</p> <p>Use the following example problem as a model.</p>
<label>What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)</label> <label>What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)</label>
<formulaequationinput label="What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)"/> <description>Use scientific notation to answer.</description>
<formulaequationinput/>
<correcthint>The mean for this set of numbers is 20 / 5, which equals 4.</correcthint> <correcthint>The mean for this set of numbers is 20 / 5, which equals 4.</correcthint>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
......
...@@ -8,7 +8,7 @@ metadata: ...@@ -8,7 +8,7 @@ metadata:
You can use the following example problem as a model. You can use the following example problem as a model.
>>Which of the following countries celebrates its independence on August 15?<< >>Which of the following countries celebrates its independence on August 15?||You can select only one option.<<
[[(India), Spain, China, Bermuda]] [[(India), Spain, China, Bermuda]]
...@@ -22,7 +22,8 @@ data: | ...@@ -22,7 +22,8 @@ data: |
<p>When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.</p> <p>When you add the problem, be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>You can use the following example problem as a model.</p> <p>You can use the following example problem as a model.</p>
<label>Which of the following countries celebrates its independence on August 15?</label> <label>Which of the following countries celebrates its independence on August 15?</label>
<optioninput label="Which of the following countries celebrates its independence on August 15?" options="('India','Spain','China','Bermuda')" correct="India"/> <description>You can select only one option.</description>
<optioninput options="('India','Spain','China','Bermuda')" correct="India"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -11,7 +11,7 @@ metadata: ...@@ -11,7 +11,7 @@ metadata:
Use the following example problem as a model. Use the following example problem as a model.
>> A/an ________ is a vegetable.<< >> A/an ________ is a vegetable.||You can select only one option.<<
[[ [[
apple {{An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.}} apple {{An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.}}
...@@ -32,7 +32,8 @@ data: | ...@@ -32,7 +32,8 @@ data: |
<p>Be sure to select Settings to specify a Display Name and other values that apply.</p> <p>Be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>Use the following example problem as a model.</p> <p>Use the following example problem as a model.</p>
<label>A/an ________ is a vegetable.</label> <label>A/an ________ is a vegetable.</label>
<optioninput label=" A/an ________ is a vegetable."> <description>You can select only one option.</description>
<optioninput>
<option correct="False">apple <option correct="False">apple
<optionhint>An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.</optionhint> <optionhint>An apple is the fertilized ovary that comes from an apple tree and contains seeds, meaning it is a fruit.</optionhint>
</option> </option>
......
...@@ -8,7 +8,7 @@ metadata: ...@@ -8,7 +8,7 @@ metadata:
You can use the following example problem as a model. You can use the following example problem as a model.
>>What was the first post-secondary school in China to allow both male and female students?<< >>What was the first post-secondary school in China to allow both male and female students?||Be sure to check your spelling.<<
= Nanjing Higher Normal Institute = Nanjing Higher Normal Institute
or= National Central University or= National Central University
...@@ -25,9 +25,10 @@ data: | ...@@ -25,9 +25,10 @@ data: |
<p>When you add the problem, be sure to select <strong>Settings</strong> to specify a <strong>Display Name</strong> and other values that apply.</p> <p>When you add the problem, be sure to select <strong>Settings</strong> to specify a <strong>Display Name</strong> and other values that apply.</p>
<p>You can use the following example problem as a model.</p> <p>You can use the following example problem as a model.</p>
<label>What was the first post-secondary school in China to allow both male and female students?</label> <label>What was the first post-secondary school in China to allow both male and female students?</label>
<description>Be sure to check your spelling.</description>
<additional_answer>National Central University</additional_answer> <additional_answer>National Central University</additional_answer>
<additional_answer>Nanjing University</additional_answer> <additional_answer>Nanjing University</additional_answer>
<textline label="What was the first post-secondary school in China to allow both male and female students?" size="40"/> <textline size="40"/>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -11,7 +11,7 @@ metadata: ...@@ -11,7 +11,7 @@ metadata:
Use the following example problem as a model. Use the following example problem as a model.
>>Which U.S. state has the largest land area?<< >>Which U.S. state has the largest land area?||Be sure to check your spelling.<<
=Alaska {{Alaska is 576,400 square miles, more than double the land area =Alaska {{Alaska is 576,400 square miles, more than double the land area
of the second largest state, Texas.}} of the second largest state, Texas.}}
...@@ -32,10 +32,11 @@ data: | ...@@ -32,10 +32,11 @@ data: |
<p>Be sure to select Settings to specify a Display Name and other values that apply.</p> <p>Be sure to select Settings to specify a Display Name and other values that apply.</p>
<p>Use the following example problem as a model.</p> <p>Use the following example problem as a model.</p>
<label>Which U.S. state has the largest land area?</label> <label>Which U.S. state has the largest land area?</label>
<description>Be sure to check your spelling.</description>
<correcthint>Alaska is 576,400 square miles, more than double the land area of the second largest state, Texas.</correcthint> <correcthint>Alaska is 576,400 square miles, more than double the land area of the second largest state, Texas.</correcthint>
<stringequalhint answer="Texas">While many people think Texas is the largest state, it is actually the second largest, with 261,797 square miles.</stringequalhint> <stringequalhint answer="Texas">While many people think Texas is the largest state, it is actually the second largest, with 261,797 square miles.</stringequalhint>
<stringequalhint answer="California">California is the third largest state, with 155,959 square miles.</stringequalhint> <stringequalhint answer="California">California is the third largest state, with 155,959 square miles.</stringequalhint>
<textline label="Which U.S. state has the largest land area?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<demandhint> <demandhint>
<hint>Consider the square miles, not population.</hint> <hint>Consider the square miles, not population.</hint>
......
...@@ -21,6 +21,7 @@ from webob.multidict import MultiDict ...@@ -21,6 +21,7 @@ from webob.multidict import MultiDict
import xmodule import xmodule
from xmodule.tests import DATA_DIR from xmodule.tests import DATA_DIR
from capa import responsetypes from capa import responsetypes
from capa.capa_problem import DEFAULT_QUESTION_TEXT
from capa.responsetypes import (StudentInputError, LoncapaProblemError, from capa.responsetypes import (StudentInputError, LoncapaProblemError,
ResponseError) ResponseError)
from capa.xqueue_interface import XQueueInterface from capa.xqueue_interface import XQueueInterface
...@@ -1762,7 +1763,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1762,7 +1763,7 @@ class CapaDescriptorTest(unittest.TestCase):
<p>The following languages are in the Indo-European family:</p> <p>The following languages are in the Indo-European family:</p>
<choiceresponse> <choiceresponse>
<checkboxgroup label="The following languages are in the Indo-European family:"> <checkboxgroup>
<choice correct="true">Urdu</choice> <choice correct="true">Urdu</choice>
<choice correct="false">Finnish</choice> <choice correct="false">Finnish</choice>
<choice correct="true">Marathi</choice> <choice correct="true">Marathi</choice>
...@@ -1797,7 +1798,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1797,7 +1798,7 @@ class CapaDescriptorTest(unittest.TestCase):
<optionresponse> <optionresponse>
<optioninput label="lbl" options="('India','Spain','China','Bermuda')" correct="India"></optioninput> <optioninput options="('India','Spain','China','Bermuda')" correct="India"></optioninput>
</optionresponse> </optionresponse>
<solution> <solution>
...@@ -1822,7 +1823,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1822,7 +1823,7 @@ class CapaDescriptorTest(unittest.TestCase):
<p>Which of the following countries has the largest population?</p> <p>Which of the following countries has the largest population?</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Which of the following countries has the largest population?" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">Brazil <choice correct="false">Brazil
<choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint> <choicehint>timely feedback -- explain why an almost correct answer is wrong</choicehint>
</choice> </choice>
...@@ -1866,14 +1867,13 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1866,14 +1867,13 @@ class CapaDescriptorTest(unittest.TestCase):
<p>How many miles away from Earth is the sun? Use scientific notation to answer.</p> <p>How many miles away from Earth is the sun? Use scientific notation to answer.</p>
<numericalresponse answer="9.3*10^7"> <numericalresponse answer="9.3*10^7">
<formulaequationinput label="How many miles away from Earth is the sun? <formulaequationinput/>
Use scientific notation to answer." />
</numericalresponse> </numericalresponse>
<p>The square of what number is -100?</p> <p>The square of what number is -100?</p>
<numericalresponse answer="10*i"> <numericalresponse answer="10*i">
<formulaequationinput label="The square of what number is -100?" /> <formulaequationinput/>
</numericalresponse> </numericalresponse>
<solution> <solution>
...@@ -1906,8 +1906,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1906,8 +1906,7 @@ class CapaDescriptorTest(unittest.TestCase):
<stringresponse answer="Nanjing Higher Normal Institute" type="ci" > <stringresponse answer="Nanjing Higher Normal Institute" type="ci" >
<additional_answer answer="National Central University"></additional_answer> <additional_answer answer="National Central University"></additional_answer>
<additional_answer answer="Nanjing University"></additional_answer> <additional_answer answer="Nanjing University"></additional_answer>
<textline label="What was the first post-secondary school in China to allow both male and female <textline size="20"/>
students?" size="20"/>
</stringresponse> </stringresponse>
<solution> <solution>
...@@ -1939,7 +1938,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1939,7 +1938,7 @@ class CapaDescriptorTest(unittest.TestCase):
<p>Which of the following is a fruit? Check all that apply.</p> <p>Which of the following is a fruit? Check all that apply.</p>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Which of the following is a fruit? Check all that apply."> <checkboxgroup>
<choice correct="true">apple <choice correct="true">apple
<choicehint selected="true">You are correct that an apple is a fruit because it is the fertilized <choicehint selected="true">You are correct that an apple is a fruit because it is the fertilized
ovary that comes from an apple tree and contains seeds.</choicehint> ovary that comes from an apple tree and contains seeds.</choicehint>
...@@ -1987,7 +1986,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -1987,7 +1986,7 @@ class CapaDescriptorTest(unittest.TestCase):
<p> A/an ________ is a vegetable.</p> <p> A/an ________ is a vegetable.</p>
<optionresponse> <optionresponse>
<optioninput label=" A/an ________ is a vegetable."> <optioninput>
<option correct="False">apple <optionhint>An apple is the fertilized ovary that comes from an apple <option correct="False">apple <optionhint>An apple is the fertilized ovary that comes from an apple
tree and contains seeds, meaning it is a fruit.</optionhint></option> tree and contains seeds, meaning it is a fruit.</optionhint></option>
<option correct="False">pumpkin <optionhint>A pumpkin is the fertilized ovary of a squash plant and <option correct="False">pumpkin <optionhint>A pumpkin is the fertilized ovary of a squash plant and
...@@ -2019,7 +2018,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -2019,7 +2018,7 @@ class CapaDescriptorTest(unittest.TestCase):
<p>Which of the following is a vegetable?</p> <p>Which of the following is a vegetable?</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Which of the following is a vegetable?" type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false">apple <choicehint>An apple is the fertilized ovary that comes from an apple <choice correct="false">apple <choicehint>An apple is the fertilized ovary that comes from an apple
tree and contains seeds, meaning it is a fruit.</choicehint></choice> tree and contains seeds, meaning it is a fruit.</choicehint></choice>
<choice correct="false">pumpkin <choicehint>A pumpkin is the fertilized ovary of a squash plant and <choice correct="false">pumpkin <choicehint>A pumpkin is the fertilized ovary of a squash plant and
...@@ -2056,8 +2055,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -2056,8 +2055,7 @@ class CapaDescriptorTest(unittest.TestCase):
<p>What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)</p> <p>What is the arithmetic mean for the following set of numbers? (1, 5, 6, 3, 5)</p>
<numericalresponse answer="4"> <numericalresponse answer="4">
<formulaequationinput label="What is the arithmetic mean for the following set of numbers? <formulaequationinput/>
(1, 5, 6, 3, 5)" />
<correcthint>The mean for this set of numbers is 20 / 5, which equals 4.</correcthint> <correcthint>The mean for this set of numbers is 20 / 5, which equals 4.</correcthint>
</numericalresponse> </numericalresponse>
<solution> <solution>
...@@ -2098,7 +2096,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -2098,7 +2096,7 @@ class CapaDescriptorTest(unittest.TestCase):
second largest, with 261,797 square miles.</stringequalhint> second largest, with 261,797 square miles.</stringequalhint>
<stringequalhint answer="California">California is the third largest state, with 155,959 square miles. <stringequalhint answer="California">California is the third largest state, with 155,959 square miles.
</stringequalhint> </stringequalhint>
<textline label="Which U.S. state has the largest land area?" size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<demandhint> <demandhint>
...@@ -2175,7 +2173,7 @@ class CapaDescriptorTest(unittest.TestCase): ...@@ -2175,7 +2173,7 @@ class CapaDescriptorTest(unittest.TestCase):
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<optionresponse> <optionresponse>
<optioninput label="Option" options="('1','2')" correct="2"></optioninput> <optioninput options="('1','2')" correct="2"></optioninput>
</optionresponse> </optionresponse>
</problem> </problem>
""") """)
...@@ -2557,12 +2555,13 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2557,12 +2555,13 @@ class TestProblemCheckTracking(unittest.TestCase):
def test_choice_answer_text(self): def test_choice_answer_text(self):
xml = """\ xml = """\
<problem display_name="Multiple Choice Questions"> <problem display_name="Multiple Choice Questions">
<p>What color is the open ocean on a sunny day?</p>
<optionresponse> <optionresponse>
<optioninput options="('yellow','blue','green')" correct="blue" label="What color is the open ocean on a sunny day?"/> <label>What color is the open ocean on a sunny day?</label>
<optioninput options="('yellow','blue','green')" correct="blue"/>
</optionresponse> </optionresponse>
<p>Which piece of furniture is built for sitting?</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<label>Which piece of furniture is built for sitting?</label>
<choicegroup type="MultipleChoice"> <choicegroup type="MultipleChoice">
<choice correct="false"><text>a table</text></choice> <choice correct="false"><text>a table</text></choice>
<choice correct="false"><text>a desk</text></choice> <choice correct="false"><text>a desk</text></choice>
...@@ -2570,9 +2569,10 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2570,9 +2569,10 @@ class TestProblemCheckTracking(unittest.TestCase):
<choice correct="false"><text>a bookshelf</text></choice> <choice correct="false"><text>a bookshelf</text></choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<p>Which of the following are musical instruments?</p>
<choiceresponse> <choiceresponse>
<checkboxgroup label="Which of the following are musical instruments?"> <label>Which of the following are musical instruments?</label>
<checkboxgroup>
<choice correct="true">a piano</choice> <choice correct="true">a piano</choice>
<choice correct="false">a tree</choice> <choice correct="false">a tree</choice>
<choice correct="true">a guitar</choice> <choice correct="true">a guitar</choice>
...@@ -2604,7 +2604,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2604,7 +2604,7 @@ class TestProblemCheckTracking(unittest.TestCase):
'variant': '', 'variant': '',
}, },
factory.answer_key(3): { factory.answer_key(3): {
'question': '', 'question': 'Which piece of furniture is built for sitting?',
'answer': u'<text>a table</text>', 'answer': u'<text>a table</text>',
'response_type': 'multiplechoiceresponse', 'response_type': 'multiplechoiceresponse',
'input_type': 'choicegroup', 'input_type': 'choicegroup',
...@@ -2652,7 +2652,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2652,7 +2652,7 @@ class TestProblemCheckTracking(unittest.TestCase):
event = self.get_event_for_answers(module, answer_input_dict) event = self.get_event_for_answers(module, answer_input_dict)
self.assertEquals(event['submission'], { self.assertEquals(event['submission'], {
factory.answer_key(2): { factory.answer_key(2): {
'question': '', 'question': DEFAULT_QUESTION_TEXT,
'answer': '3.14', 'answer': '3.14',
'response_type': 'numericalresponse', 'response_type': 'numericalresponse',
'input_type': 'textline', 'input_type': 'textline',
...@@ -2683,7 +2683,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2683,7 +2683,7 @@ class TestProblemCheckTracking(unittest.TestCase):
event = self.get_event_for_answers(module, answer_input_dict) event = self.get_event_for_answers(module, answer_input_dict)
self.assertEquals(event['submission'], { self.assertEquals(event['submission'], {
factory.answer_key(2, 1): { factory.answer_key(2, 1): {
'question': '', 'question': DEFAULT_QUESTION_TEXT,
'answer': 'blue', 'answer': 'blue',
'response_type': 'optionresponse', 'response_type': 'optionresponse',
'input_type': 'optioninput', 'input_type': 'optioninput',
...@@ -2691,7 +2691,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2691,7 +2691,7 @@ class TestProblemCheckTracking(unittest.TestCase):
'variant': '', 'variant': '',
}, },
factory.answer_key(2, 2): { factory.answer_key(2, 2): {
'question': '', 'question': DEFAULT_QUESTION_TEXT,
'answer': 'yellow', 'answer': 'yellow',
'response_type': 'optionresponse', 'response_type': 'optionresponse',
'input_type': 'optioninput', 'input_type': 'optioninput',
...@@ -2748,7 +2748,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2748,7 +2748,7 @@ class TestProblemCheckTracking(unittest.TestCase):
event = self.get_event_for_answers(module, answer_input_dict) event = self.get_event_for_answers(module, answer_input_dict)
self.assertEquals(event['submission'], { self.assertEquals(event['submission'], {
factory.answer_key(2, 1): { factory.answer_key(2, 1): {
'question': '', 'question': DEFAULT_QUESTION_TEXT,
'answer': 'apple', 'answer': 'apple',
'response_type': 'optionresponse', 'response_type': 'optionresponse',
'input_type': 'optioninput', 'input_type': 'optioninput',
...@@ -2756,7 +2756,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2756,7 +2756,7 @@ class TestProblemCheckTracking(unittest.TestCase):
'variant': '', 'variant': '',
}, },
factory.answer_key(2, 2): { factory.answer_key(2, 2): {
'question': '', 'question': DEFAULT_QUESTION_TEXT,
'answer': 'cucumber', 'answer': 'cucumber',
'response_type': 'optionresponse', 'response_type': 'optionresponse',
'input_type': 'optioninput', 'input_type': 'optioninput',
...@@ -2776,7 +2776,7 @@ class TestProblemCheckTracking(unittest.TestCase): ...@@ -2776,7 +2776,7 @@ class TestProblemCheckTracking(unittest.TestCase):
event = self.get_event_for_answers(module, answer_input_dict) event = self.get_event_for_answers(module, answer_input_dict)
self.assertEquals(event['submission'], { self.assertEquals(event['submission'], {
factory.answer_key(2): { factory.answer_key(2): {
'question': '', 'question': DEFAULT_QUESTION_TEXT,
'answer': '3.14', 'answer': '3.14',
'response_type': 'numericalresponse', 'response_type': 'numericalresponse',
'input_type': 'textline', 'input_type': 'textline',
......
...@@ -221,3 +221,17 @@ class ProblemPage(PageObject): ...@@ -221,3 +221,17 @@ class ProblemPage(PageObject):
if not self.q(xpath=xpath.format(choice)).is_present(): if not self.q(xpath=xpath.format(choice)).is_present():
return False return False
return True return True
@property
def problem_question(self):
"""
Return the question text of the problem.
"""
return self.q(css="div.problem .wrapper-problem-response legend").text[0]
@property
def problem_question_descriptions(self):
"""
Return a list of question descriptions of the problem.
"""
return self.q(css="div.problem .wrapper-problem-response .question-description").text
...@@ -71,9 +71,9 @@ class EntranceExamPassTest(EntranceExamTest): ...@@ -71,9 +71,9 @@ class EntranceExamPassTest(EntranceExamTest):
""" """
xml = dedent(""" xml = dedent("""
<problem> <problem>
<p>What is height of eiffel tower without the antenna?.</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="What is height of eiffel tower without the antenna?" type="MultipleChoice"> <label>What is height of eiffel tower without the antenna?.</label>
<choicegroup type="MultipleChoice">
<choice correct="false">324 meters<choicehint>Antenna is 24 meters high</choicehint></choice> <choice correct="false">324 meters<choicehint>Antenna is 24 meters high</choicehint></choice>
<choice correct="true">300 meters</choice> <choice correct="true">300 meters</choice>
<choice correct="false">224 meters</choice> <choice correct="false">224 meters</choice>
......
...@@ -4,6 +4,7 @@ Bok choy acceptance tests for problems in the LMS ...@@ -4,6 +4,7 @@ Bok choy acceptance tests for problems in the LMS
See also old lettuce tests in lms/djangoapps/courseware/features/problems.feature See also old lettuce tests in lms/djangoapps/courseware/features/problems.feature
""" """
from nose.plugins.attrib import attr
from textwrap import dedent from textwrap import dedent
from common.test.acceptance.tests.helpers import UniqueCourseTest from common.test.acceptance.tests.helpers import UniqueCourseTest
...@@ -77,7 +78,8 @@ class ProblemClarificationTest(ProblemsTest): ...@@ -77,7 +78,8 @@ class ProblemClarificationTest(ProblemsTest):
<clarification>Return on Investment <strong>(per year)</strong></clarification> over 20 years. <clarification>Return on Investment <strong>(per year)</strong></clarification> over 20 years.
</p> </p>
<numericalresponse answer="6.5"> <numericalresponse answer="6.5">
<textline label="Enter the annual ROI" trailing_text="%" /> <label>Enter the annual ROI</label>
<textline trailing_text="%" />
</numericalresponse> </numericalresponse>
</text> </text>
</problem> </problem>
...@@ -263,7 +265,8 @@ class ProblemWithMathjax(ProblemsTest): ...@@ -263,7 +265,8 @@ class ProblemWithMathjax(ProblemsTest):
<problem> <problem>
<p>Check mathjax has rendered [mathjax]E=mc^2[/mathjax]</p> <p>Check mathjax has rendered [mathjax]E=mc^2[/mathjax]</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup label="Answer this?" type="MultipleChoice"> <label>Answer this?</label>
<choicegroup type="MultipleChoice">
<choice correct="true">Choice1 <choicehint>Correct choice message</choicehint></choice> <choice correct="true">Choice1 <choicehint>Correct choice message</choicehint></choice>
<choice correct="false">Choice2<choicehint>Wrong choice message</choicehint></choice> <choice correct="false">Choice2<choicehint>Wrong choice message</choicehint></choice>
</choicegroup> </choicegroup>
...@@ -310,7 +313,8 @@ class ProblemPartialCredit(ProblemsTest): ...@@ -310,7 +313,8 @@ class ProblemPartialCredit(ProblemsTest):
<problem> <problem>
<p>The answer is 1. Partial credit for -1.</p> <p>The answer is 1. Partial credit for -1.</p>
<numericalresponse answer="1" partial_credit="list"> <numericalresponse answer="1" partial_credit="list">
<formulaequationinput label="How many miles away from Earth is the sun? Use scientific notation to answer." /> <label>How many miles away from Earth is the sun? Use scientific notation to answer.</label>
<formulaequationinput/>
<responseparam type="tolerance" default="0.01" /> <responseparam type="tolerance" default="0.01" />
<responseparam partial_answers="-1" /> <responseparam partial_answers="-1" />
</numericalresponse> </numericalresponse>
...@@ -343,9 +347,9 @@ class LogoutDuringAnswering(ProblemsTest): ...@@ -343,9 +347,9 @@ class LogoutDuringAnswering(ProblemsTest):
""" """
xml = dedent(""" xml = dedent("""
<problem> <problem>
<p>The answer is 1</p>
<numericalresponse answer="1"> <numericalresponse answer="1">
<formulaequationinput label="where are the songs of spring?" /> <label>The answer is 1</label>
<formulaequationinput/>
<responseparam type="tolerance" default="0.01" /> <responseparam type="tolerance" default="0.01" />
</numericalresponse> </numericalresponse>
</problem> </problem>
...@@ -412,3 +416,94 @@ class LogoutDuringAnswering(ProblemsTest): ...@@ -412,3 +416,94 @@ class LogoutDuringAnswering(ProblemsTest):
self.assertTrue(problem_page.is_browser_on_page()) self.assertTrue(problem_page.is_browser_on_page())
self.assertEqual(problem_page.problem_name, 'TEST PROBLEM') self.assertEqual(problem_page.problem_name, 'TEST PROBLEM')
class ProblemQuestionDescriptionTest(ProblemsTest):
"""TestCase Class to verify question and description rendering."""
descriptions = [
"A vegetable is an edible part of a plant in tuber form.",
"A fruit is a fertilized ovary of a plant and contains seeds."
]
def get_problem(self):
"""
Create a problem with question and description.
"""
xml = dedent("""
<problem>
<choiceresponse>
<label>Eggplant is a _____?</label>
<description>{}</description>
<description>{}</description>
<checkboxgroup>
<choice correct="true">vegetable</choice>
<choice correct="false">fruit</choice>
</checkboxgroup>
</choiceresponse>
</problem>
""".format(*self.descriptions))
return XBlockFixtureDesc('problem', 'Label with Description', data=xml)
def test_question_with_description(self):
"""
Scenario: Test that question and description are rendered as expected.
Given I am enrolled in a course.
When I visit a unit page with a CAPA question.
Then label and description should be rendered correctly.
"""
self.courseware_page.visit()
problem_page = ProblemPage(self.browser)
problem_page.wait_for_element_visibility(problem_page.CSS_PROBLEM_HEADER, 'wait for problem header')
self.assertEqual(problem_page.problem_name, 'Label with Description')
self.assertEqual(problem_page.problem_question, 'Eggplant is a _____?')
self.assertEqual(problem_page.problem_question_descriptions, self.descriptions)
@attr('a11y')
class CAPAProblemQuestionDescriptionA11yTest(ProblemsTest):
"""TestCase Class to verify CAPA problem questions accessibility."""
def get_problem(self):
"""
Problem structure.
"""
xml = dedent("""
<problem>
<choiceresponse>
<label>question 1 text here</label>
<description>description 2 text 1</description>
<description>description 2 text 2</description>
<checkboxgroup>
<choice correct="true">True</choice>
<choice correct="false">False</choice>
</checkboxgroup>
</choiceresponse>
<multiplechoiceresponse>
<label>question 2 text here</label>
<description>description 2 text 1</description>
<description>description 2 text 2</description>
<choicegroup type="MultipleChoice">
<choice correct="false">Alpha <choicehint>A hint</choicehint></choice>
<choice correct="true">Beta</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>
""")
return XBlockFixtureDesc('problem', 'Problem A11Y TEST', data=xml)
def test_a11y(self):
"""
Scenario: Verifies that each question and description has unique id.
Given I am enrolled in a course.
And I visit a unit page with two CAPA problems
Then I check question and description has unique IDs
"""
self.courseware_page.visit()
problem_page = ProblemPage(self.browser)
# Set the scope to the problem question
problem_page.a11y_audit.config.set_scope(
include=['section.wrapper-problem-response']
)
# Run the accessibility audit.
problem_page.a11y_audit.check_for_accessibility_errors()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment