Commit 055fd6ea by Albert St. Aubin

Refactored the status icon and span to a unified template and updated tests

parent 04fa2a42
......@@ -39,25 +39,27 @@ graded status as'status'
# makes sense, but a bunch of problems have markup that assumes block. Bigger TODO: figure out a
# general css and layout strategy for capa, document it, then implement it.
import time
import json
import logging
from lxml import etree
import re
import shlex # for splitting quoted strings
import sys
import pyparsing
import html5lib
import bleach
import time
from datetime import datetime
from .util import sanitize_html
from .registry import TagRegistry
from chem import chemcalc
import bleach
import html5lib
import pyparsing
import re
from calc.preview import latex_preview
from chem import chemcalc
from lxml import etree
from openedx.core.djangolib.markup import HTML, Text
import xqueue_interface
from xqueue_interface import XQUEUE_TIMEOUT
from datetime import datetime
from xmodule.stringify import stringify_children
from capa.xqueue_interface import XQUEUE_TIMEOUT
from .registry import TagRegistry
from .util import sanitize_html
log = logging.getLogger(__name__)
......@@ -232,7 +234,8 @@ class InputTypeBase(object):
# put hint above msg if it should be displayed
if self.hintmode == 'always':
self.msg = self.hint + ('<br/>' if self.msg else '') + self.msg
self.msg = HTML('{hint}<br/>{msg}' if self.msg else '{hint}').format(hint=HTML(self.hint),
msg=HTML(self.msg))
self.status = state.get('status', 'unanswered')
......@@ -322,15 +325,18 @@ class InputTypeBase(object):
'msg': self.msg,
'response_data': self.response_data,
'STATIC_URL': self.capa_system.STATIC_URL,
'describedby_html': '',
'describedby_html': HTML(''),
}
# 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_html': 'aria-describedby="{}"'.format(description_ids)}
)
# Generate the list of ids to be used with the aria-describedby field.
# Every list should contain the status id
status_id = 'status_' + self.input_id
descriptions = list([status_id])
descriptions.extend(self.response_data.get('descriptions', {}).keys())
description_ids = ' '.join(descriptions)
context.update(
{'describedby_html': HTML('aria-describedby="{}"').format(description_ids)}
)
context.update(
(a, v) for (a, v) in self.loaded_attributes.iteritems() if a in self.to_render
......@@ -522,9 +528,10 @@ class ChoiceGroup(InputTypeBase):
choices.append((choice.get("name"), stringify_children(choice)))
else:
if choice.tag != 'compoundhint':
msg = u'[capa.inputtypes.extract_choices] {error_message}'.format(
# Translators: '<choice>' and '<compoundhint>' are tag names and should not be translated.
error_message=_('Expected a <choice> or <compoundhint> tag; got {given_tag} instead').format(
msg = Text('[capa.inputtypes.extract_choices] {error_message}').format(
error_message=Text(
# Translators: '<choice>' and '<compoundhint>' are tag names and should not be translated.
_('Expected a <choice> or <compoundhint> tag; got {given_tag} instead')).format(
given_tag=choice.tag
)
)
......@@ -939,13 +946,13 @@ class MatlabInput(CodeInput):
queue_msg = self.queue_msg
if len(self.queue_msg) > 0: # An empty string cannot be parsed as XML but is okay to include in the template.
try:
etree.XML(u'<div>{0}</div>'.format(self.queue_msg))
etree.XML(HTML(u'<div>{0}</div>').format(HTML(self.queue_msg)))
except etree.XMLSyntaxError:
try:
html5lib.parseFragment(self.queue_msg, treebuilder='lxml', namespaceHTMLElements=False)[0]
except (IndexError, ValueError):
# If neither can parse queue_msg, it contains invalid xml.
queue_msg = u"<span>{0}</span>".format(_("Error running code."))
queue_msg = HTML("<span>{0}</span>").format(_("Error running code."))
extra_context = {
'queue_len': str(self.queue_len),
......@@ -1797,10 +1804,10 @@ class ChoiceTextGroup(InputTypeBase):
for choice in element:
if choice.tag != 'choice':
msg = u"[capa.inputtypes.extract_choices] {0}".format(
msg = Text("[capa.inputtypes.extract_choices] {0}").format(
# Translators: a "tag" is an XML element, such as "<b>" in HTML
_("Expected a {expected_tag} tag; got {given_tag} instead").format(
expected_tag=u"<choice>",
Text(_("Expected a {expected_tag} tag; got {given_tag} instead")).format(
expected_tag="<choice>",
given_tag=choice.tag,
)
)
......
......@@ -22,12 +22,10 @@
% for option in options:
<li>
% if has_options_value:
% if all([c == 'correct' for c in option['choice'], status]):
<span class="tag-status correct" id="status_${id}" aria-describedby="input_${id}_comment"><span class="sr">Status: Correct</span></span>
% elif all([c == 'partially-correct' for c in option['choice'], status]):
<span class="tag-status partially-correct" id="status_${id}" aria-describedby="input_${id}_comment"><span class="sr">Status: Partially Correct</span></span>
% elif all([c == 'incorrect' for c in option['choice'], status]):
<span class="tag-status incorrect" id="status_${id}" aria-describedby="input_${id}_comment"><span class="sr">Status: Incorrect</span></span>
% if all([c == status.classname for c in option['choice'], status]):
<span class="tag-status ${status.classname}" aria-describedby="input_${id}_comment">
<%include file="status_span.html" args="status=status"/>
</span>
% endif
% endif
......@@ -53,7 +51,7 @@
<input type="hidden" class="value" name="input_${id}" id="input_${id}" value="${value|h}" />
% endif
<span class="status ${status.classname}" id="status_${id}" aria-describedby="label_${id}"><span class="sr">${status.display_name}</span></span>
<%include file="status_span.html" args="status=status, status_id=id"/>
<p id="answer_${id}" class="answer answer-annotation"></p>
</div>
......
......@@ -2,23 +2,21 @@
<div id="chemicalequationinput_${id}" class="chemicalequationinput">
<div class="script_placeholder" data-src="${previewer}"/>
<div class="${status.classname}" id="status_${id}">
<div class="${status.classname}">
<input type="text" name="input_${id}" id="input_${id}" aria-label="${remove_markup(response_data['label'])}" aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}"
% if size:
size="${size}"
% endif
/>
<input type="text" name="input_${id}" id="input_${id}" aria-label="${remove_markup(response_data['label'])}"
aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}"
% if size:
size="${size}"
% endif
/>
<p class="status" aria-describedby="input_${id}">
<p class="status">
${value|h}
<span class="sr">${status.display_name}</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</p>
<div id="input_${id}_preview" class="equation"></div>
<p id="answer_${id}" class="answer"></p>
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
</div>
% endif
<p id="answer_${id}" class="answer"></p>
</div>
</div>
......@@ -7,7 +7,7 @@
))
%>
<form class="choicegroup capa_inputtype" id="inputtype_${id}">
<fieldset ${HTML(describedby_html)}>
<fieldset ${describedby_html}>
% if response_data['label']:
<legend id="${id}-legend" class="response-fieldset-legend field-group-hd">${response_data['label']}</legend>
% endif
......@@ -37,7 +37,7 @@
% endif
% endif
class="${label_class}"
${HTML(describedby_html)}
${describedby_html}
>
<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...
......@@ -49,8 +49,8 @@
/> ${HTML(choice_label)}
% if is_radio_input(choice_id):
% if status in ('correct', 'partially-correct', 'incorrect') and not show_correctness == 'never':
<span class="sr status" id="${id}-${choice_id}-labeltext">${status.display_name}</span>
% if not show_correctness == 'never' and status.classname != 'unanswered':
<%include file="status_span.html" args="status=status, status_id=id"/>
% endif
% endif
</label>
......@@ -59,10 +59,12 @@
<span id="answer_${id}"></span>
</fieldset>
<div class="indicator-container">
% if input_type == 'checkbox' or not value:
<span class="status ${status.classname if show_correctness != 'never' else 'unanswered'}" id="status_${id}" aria-describedby="${id}-legend" data-tooltip="${status.display_tooltip}">
<span class="sr">${status.display_tooltip}</span>
</span>
% if input_type == 'checkbox' or status.classname == 'unanswered':
% if show_correctness != 'never':
<%include file="status_span.html" args="status=status, status_id=id"/>
% else:
<%include file="status_span.html" args="status=status, status_id=id, hide_correctness=True"/>
% endif
% endif
</div>
% if show_correctness == "never" and (value or status not in ['unsubmitted']):
......
......@@ -66,9 +66,7 @@ from openedx.core.djangolib.markup import HTML
<div class="indicator-container">
% if input_type == 'checkbox' or not element_checked:
<span class="status ${status.classname}" id="status_${id}">
<span class="sr">${status.display_name}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
% endif
</div>
......
......@@ -26,12 +26,9 @@ from openedx.core.djangolib.markup import HTML
</span>
<div class="grader-status" tabindex="-1">
<span id="status_${id}"
class="${status.classname}"
aria-describedby="input_${id}"
>
<span class="status sr">${status.display_name}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
% if status == 'queued':
<span style="display:none;" class="xqueue" id="${id}">${queue_len}</span>
% endif
......
......@@ -16,9 +16,7 @@
<input type="text" name="input_${id}" aria-describedby="answer_${id}" id="input_${id}" value="${value|h}" style="display:none;"/>
<p class="status" aria-describedby="input_${id}">
<span class="sr">${status.display_name}</span>
</p>
<%include file="status_span.html" args="status=status, status_id=id"/>
<p id="answer_${id}" class="answer"></p>
......
......@@ -10,9 +10,7 @@
<input type="hidden" name="target_shape" id="target_shape" value ="${target_shape}"></input>
<input type="hidden" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" value="${value|h}"/>
<p class="status" aria-describedby="input_${id}">
<span class="sr">${status.display_name}</span>
</p>
<%include file="status_span.html" args="status=status, status_id=id"/>
<p id="answer_${id}" class="answer"></p>
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
......
......@@ -17,8 +17,9 @@
<input type="text" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" value="${value|h}"
style="display:none;"/>
<p class="status drag-and-drop--status" aria-describedby="input_${id}">
<span class="sr">${status.display_name}</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</p>
<p id="answer_${id}" class="answer"></p>
......
......@@ -3,7 +3,7 @@
<div class="script_placeholder" data-src="${applet_loader}"/>
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
<div class="${status.classname}" id="status_${id}">
<div class="${status.classname}">
% endif
<div id="genex_container"></div>
......@@ -12,7 +12,7 @@
<input type="hidden" name="input_${id}" aria-describedby="answer_${id}" id="input_${id}" value="${value|h}"/>
<p class="status" aria-describedby="input_${id}">
<span class="sr">${status.display_name}</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</p>
<p id="answer_${id}" class="answer"></p>
......
......@@ -2,7 +2,7 @@
<div class="script_placeholder" data-src="${applet_loader}"/>
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
<div class="${status.classname}" id="status_${id}">
<div class="${status.classname}">
% endif
<div id="applet_${id}" class="applet" data-molfile-src="${file}" style="display:block;width:500px;height:400px">
......@@ -17,7 +17,7 @@
<p id="answer_${id}" class="answer"></p>
<p class="status" aria-describedby="input_${id}">
<span class="sr">${status.display_name}</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</p>
<div class="error_message" style="padding: 5px 5px 5px 5px; background-color:#FA6666; height:60px;width:400px; display: none"></div>
......
......@@ -2,7 +2,7 @@
<%! from openedx.core.djangolib.markup import HTML %>
<% doinline = 'style="display:inline-block;vertical-align:top"' if inline else "" %>
<div id="formulaequationinput_${id}" class="inputtype formulaequationinput" ${doinline | n, decode.utf8}>
<div class="${status.classname}" id="status_${id}">
<div class="${status.classname}">
% if response_data['label']:
<label class="problem-group-label" for="input_${id}" id="label_${id}">${response_data['label']}</label>
% endif
......@@ -11,16 +11,14 @@
% endfor
<input type="text" name="input_${id}" id="input_${id}"
data-input-id="${id}" value="${value}"
${HTML(describedby_html)}
${describedby_html}
% if size:
size="${size}"
% endif
/>
<span class="trailing_text">${trailing_text}</span>
<span class="status" id="${id}_status" aria-describedby="label_${id}" data-tooltip="${status.display_tooltip}">
<span class="sr">${status.display_tooltip}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
<p id="answer_${id}" class="answer"></p>
......
......@@ -40,11 +40,5 @@
(new ImageInput('${id}'));
</script>
<span
class="status ${status.classname}"
id="status_${id}"
aria-describedby="input_${id}"
>
<span class="sr">${status.display_name}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</div>
<%page expression_filter="h"/>
<form class="javascriptinput capa_inputtype" id="inputtype_${id}">
<input type="hidden" name="input_${id}" id="input_${id}" class="javascriptinput_input"/>
<div class="javascriptinput_data" data-display_class="${display_class}"
data-problem_state="${problem_state}" data-params="${params}"
data-submission="${value|h}" data-evaluation="${msg|h}">
data-submission="${value}" data-evaluation="${msg}">
</div>
<div class="script_placeholder" data-src="/static/js/${display_file}"></div>
<div class="javascriptinput_container"></div>
......
......@@ -22,7 +22,7 @@
<div class="script_placeholder" data-src="${jschannel_loader}"/>
<div class="script_placeholder" data-src="${jsinput_loader}"/>
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
<div class="${status.classname}" id="status_${id}">
<div class="${status.classname}">
% endif
<iframe name="iframe_${id}"
......@@ -42,7 +42,7 @@
<p id="answer_${id}" class="answer"></p>
<p class="status">
<span class="sr">${status.display_name}</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</p>
<div class="error_message" style="padding: 5px 5px 5px 5px; background-color:#FA6666; height:60px;width:400px; display: none"></div>
......
......@@ -17,12 +17,9 @@
>${value|h}</textarea>
<div class="grader-status" tabindex="-1">
<span id="status_${id}"
class="${status.classname}"
aria-describedby="input_${id}"
>
<span class="status sr">${status.display_name}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
% if status == 'queued':
<span style="display:none;" class="xqueue" id="${id}">${queue_len}</span>
% endif
......
......@@ -11,7 +11,7 @@
<p class="question-description" id="${description_id}">${description_text}</p>
% endfor
<select name="input_${id}" id="input_${id}" ${HTML(describedby_html)}>
<select name="input_${id}" id="input_${id}" ${describedby_html}>
<option value="option_${id}_dummy_default">${default_option_text}</option>
% for option_id, option_description in options:
<option value="${option_id}"
......@@ -23,11 +23,7 @@
</select>
<div class="indicator-container">
<span class="status ${status.classname}"
id="status_${id}"
aria-describedby="label_${id}" data-tooltip="${status.display_tooltip}">
<span class="sr">${status.display_tooltip}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</div>
<p class="answer" id="answer_${id}"></p>
% if msg:
......
......@@ -18,7 +18,7 @@
<span id="answer_${id}"></span>
<div class="indicator-container">
<span class="status ${status.classname}" id="status_${id}" aria-describedby="input_${id}"></span>
<span class="sr">${status.display_tooltip}</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
</div>
</div>
<%page expression_filter="h"/>
<div class="solution-span">
<span id="solution_${id}"></span>
</div>
<%page expression_filter="h" args="status, status_id='', hide_correctness=False"/>
% if status_id == '':
<span class="status ${'' if hide_correctness == True else status.classname}"
data-tooltip="${'' if hide_correctness == True else status.display_tooltip}">
% else:
<span class="status ${'' if hide_correctness == True else status.classname}" id="status_${status_id}"
data-tooltip="${'' if hide_correctness == True else status.display_tooltip}">
% endif
% if hide_correctness == False:
<span class="sr">${status.display_name}</span><span class="status-icon" aria-hidden="true"></span>
% endif
</span>
......@@ -9,7 +9,7 @@
% endif
% if status in ('unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete'):
<div class="${status.classname} ${doinline}" id="status_${id}">
<div class="${status.classname} ${doinline}">
% endif
% if hidden:
......@@ -23,7 +23,7 @@
% for description_id, description_text in response_data['descriptions'].items():
<p class="question-description" id="${description_id}">${description_text}</p>
% endfor
<input type="text" name="input_${id}" id="input_${id}" ${HTML(describedby_html)} value="${value}"
<input type="text" name="input_${id}" id="input_${id}" ${describedby_html} value="${value}"
% if do_math:
class="math"
% endif
......@@ -36,9 +36,7 @@
/>
<span class="trailing_text">${trailing_text}</span>
<span class="status" aria-describedby="label_${id}" data-tooltip="${status.display_tooltip}">
<span class="sr">${status.display_tooltip}</span>
</span>
<%include file="status_span.html" args="status=status, status_id=id"/>
<p id="answer_${id}" class="answer"></p>
......
......@@ -22,7 +22,8 @@ def get_template(template_name):
Return template for a capa inputtype.
"""
return TemplateLookup(
directories=[path(__file__).dirname().dirname() / 'templates']
directories=[path(__file__).dirname().dirname() / 'templates'],
default_filters=['decode.utf8']
).get_template(template_name)
......
"""
CAPA HTML rendering tests.
"""
import ddt
import unittest
from lxml import etree
import os
import textwrap
import unittest
import ddt
import mock
import os
from capa.tests.helpers import test_capa_system, new_loncapa_problem
from lxml import etree
from openedx.core.djangolib.markup import HTML
from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFactory
from capa.tests.helpers import test_capa_system, new_loncapa_problem
@ddt.ddt
......@@ -190,7 +191,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
'trailing_text': '',
'size': None,
'response_data': {'label': 'Test question', 'descriptions': {}},
'describedby_html': ''
'describedby_html': HTML('aria-describedby="status_1_2_1"')
}
expected_solution_context = {'id': '1_solution_1'}
......
......@@ -2,15 +2,16 @@
Tests for the logic in input type mako templates.
"""
from collections import OrderedDict
import unittest
import capa
import os.path
import json
import unittest
from collections import OrderedDict
from capa.inputtypes import Status
from capa.tests.helpers import capa_render_template
from lxml import etree
from mako.template import Template as MakoTemplate
from mako import exceptions
from capa.inputtypes import Status
from openedx.core.djangolib.markup import HTML
from xmodule.stringify import stringify_children
......@@ -23,7 +24,7 @@ class TemplateError(Exception):
class TemplateTestCase(unittest.TestCase):
"""
Utilitites for testing templates.
Utilities for testing templates.
"""
# Subclasses override this to specify the file name of the template
......@@ -46,16 +47,9 @@ class TemplateTestCase(unittest.TestCase):
def setUp(self):
"""
Load the template under test.
Initialize the context.
"""
super(TemplateTestCase, self).setUp()
capa_path = capa.__path__[0]
self.template_path = os.path.join(capa_path,
'templates',
self.TEMPLATE_NAME)
with open(self.template_path) as f:
self.template = MakoTemplate(f.read(), default_filters=['decode.utf8'])
self.context = {}
def render_to_xml(self, context_dict):
......@@ -66,7 +60,7 @@ class TemplateTestCase(unittest.TestCase):
# add dummy STATIC_URL to template context
context_dict.setdefault("STATIC_URL", "/dummy-static/")
try:
xml_str = self.template.render_unicode(**context_dict)
xml_str = capa_render_template(self.TEMPLATE_NAME, context_dict)
except:
raise TemplateError(exceptions.text_error_template().render())
......@@ -196,10 +190,10 @@ class TemplateTestCase(unittest.TestCase):
# (used to by CSS to draw the green check / red x)
self.assert_has_text(
xml,
"//span[@class=normalize-space('status {}')]/span[@class='sr']".format(
"//span[@class='status {}']/span[@class='sr']".format(
div_class if status_class else ''
),
self.context['status'].display_tooltip
self.context['status'].display_name
)
def assert_label(self, xpath=None, aria_label=False):
......@@ -259,7 +253,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
'name_array_suffix': '1',
'value': '3',
'response_data': self.RESPONSE_DATA,
'describedby_html': self.DESCRIBEDBY,
'describedby_html': HTML(self.DESCRIBEDBY),
}
def test_problem_marked_correct(self):
......@@ -290,11 +284,9 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
(not a particular option) is marked incorrect.
"""
conditions = [
{'status': Status('incorrect'), 'input_type': 'radio', 'value': ''},
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': []},
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': ['2']},
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': ['2', '3']},
{'status': Status('incomplete'), 'input_type': 'radio', 'value': ''},
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': []},
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': ['2']},
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': ['2', '3']}]
......@@ -506,7 +498,7 @@ class TextlineTemplateTest(TemplateTestCase):
'preprocessor': None,
'trailing_text': None,
'response_data': self.RESPONSE_DATA,
'describedby_html': self.DESCRIBEDBY,
'describedby_html': HTML(self.DESCRIBEDBY),
}
def test_section_class(self):
......@@ -526,7 +518,7 @@ class TextlineTemplateTest(TemplateTestCase):
"""
Verify status information.
"""
self.assert_status(status_div=True)
self.assert_status(status_class=True)
def test_label(self):
"""
......@@ -632,7 +624,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
'reported_status': 'REPORTED_STATUS',
'trailing_text': None,
'response_data': self.RESPONSE_DATA,
'describedby_html': self.DESCRIBEDBY,
'describedby_html': HTML(self.DESCRIBEDBY),
}
def test_no_size(self):
......@@ -657,7 +649,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
"""
Verify status information.
"""
self.assert_status(status_div=True)
self.assert_status(status_class=True)
def test_label(self):
"""
......@@ -852,7 +844,7 @@ class OptionInputTemplateTest(TemplateTestCase):
'value': 0,
'default_option_text': 'Select an option',
'response_data': self.RESPONSE_DATA,
'describedby_html': self.DESCRIBEDBY,
'describedby_html': HTML(self.DESCRIBEDBY),
}
def test_select_options(self):
......@@ -929,8 +921,8 @@ class DragAndDropTemplateTest(TemplateTestCase):
xpath = "//div[@class='{0}']".format(expected_css_class)
self.assert_has_xpath(xml, xpath, self.context)
# Expect a <p> with the status
xpath = "//p[@class='status drag-and-drop--status']/span[@class='sr']"
# Expect a <span> with the status
xpath = "//span[@class='status {0}']/span[@class='sr']".format(expected_css_class)
self.assert_has_text(xml, xpath, expected_text, exact=False)
def test_drag_and_drop_json_html(self):
......@@ -1206,7 +1198,7 @@ class CodeinputTemplateTest(TemplateTestCase):
'aria_label': 'python editor',
'code_mirror_exit_message': 'Press ESC then TAB or click outside of the code editor to exit',
'response_data': self.RESPONSE_DATA,
'describedby': self.DESCRIBEDBY,
'describedby': HTML(self.DESCRIBEDBY),
}
def test_label(self):
......
......@@ -23,9 +23,6 @@
// ====================
$annotation-yellow: rgba(255,255,10,0.3);
$color-copy-tip: rgb(100,100,100);
$correct: $green-d2;
$partially-correct: $green-d2;
$incorrect: $red;
// FontAwesome Icon code
// ====================
......@@ -50,11 +47,13 @@ $asterisk-icon: '\f069'; // .fa-asterisk
// ====================
@mixin status-icon($color: $gray, $fontAwesomeIcon: "\f00d"){
&:after {
@extend %use-font-awesome;
color: $color;
font-size: 1.2em;
content: $fontAwesomeIcon;
.status-icon {
&:after {
@extend %use-font-awesome;
color: $color;
font-size: 1.2em;
content: $fontAwesomeIcon;
}
}
}
......@@ -318,6 +317,12 @@ div.problem {
@include status-icon($incorrect, $cross-icon);
}
&.unsubmitted,
&.unanswered {
.status-icon {
content: '';
}
}
}
}
}
......@@ -818,12 +823,14 @@ div.problem {
}
.status {
&:after {
content: ''; // clear out correct or incorrect icon
.status-icon {
&:after {
content: '';
}
}
}
}
}
.trailing_text {
......
......@@ -962,7 +962,6 @@
$status = $('#status_' + id);
if ($status[0]) {
$status.removeClass().addClass('unanswered');
$status.empty().css('display', 'inline-block');
} else {
$('<span>', {
class: 'unanswered',
......@@ -979,8 +978,8 @@
id = ($select.attr('id').match(/^input_(.*)$/))[1];
return $select.on('change', function() {
return $('#status_' + id).removeClass().addClass('unanswered')
.find('span')
.text(gettext('Status: unsubmitted'));
.find('.sr')
.text(gettext('unsubmitted'));
});
},
textline: function(element) {
......
......@@ -144,6 +144,13 @@ $success-color: rgb(0, 155, 0) !default;
$warning-color: rgb(255, 192, 31) !default;
$warning-color-accent: rgb(255, 252, 221) !default;
// CAPA correctness color to be consistent with Alert styles above
$correct: $success-color;
$partially-correct: $success-color;
$incorrect: $error-color;
// BUTTONS
// disabled button
......
......@@ -617,12 +617,12 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
metadata={'graded': True},
display_name='Problem Vertical'
)
self.define_option_problem(u'Pröblem1', parent=vertical)
self.define_option_problem(u'Problem1', parent=vertical)
self.submit_student_answer(self.student_1.username, u'Pröblem1', ['Option 1'])
self.submit_student_answer(self.student_1.username, u'Problem1', ['Option 1'])
result = upload_problem_grade_report(None, None, self.course.id, None, 'graded')
self.assertDictContainsSubset({'action_name': 'graded', 'attempted': 2, 'succeeded': 2, 'failed': 0}, result)
problem_name = u'Homework 1: Problem - Pröblem1'
problem_name = u'Homework 1: Problem - Problem1'
header_row = self.csv_header_row + [problem_name + ' (Earned)', problem_name + ' (Possible)']
self.verify_rows_in_csv([
dict(zip(
......@@ -646,7 +646,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
@patch('lms.djangoapps.instructor_task.tasks_helper._get_current_task')
@patch('lms.djangoapps.instructor_task.tasks_helper.iterate_grades_for')
@ddt.data(u'Cannöt grade student', '')
@ddt.data(u'Cannot grade student', '')
def test_grading_failure(self, error_message, mock_iterate_grades_for, _mock_current_task):
"""
Test that any grading errors are properly reported in the progress
......@@ -683,8 +683,8 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
def setUp(self):
super(TestProblemReportSplitTestContent, self).setUp()
self.problem_a_url = u'pröblem_a_url'
self.problem_b_url = u'pröblem_b_url'
self.problem_a_url = u'problem_a_url'
self.problem_b_url = u'problem_b_url'
self.define_option_problem(self.problem_a_url, parent=self.vertical_a)
self.define_option_problem(self.problem_b_url, parent=self.vertical_b)
......@@ -711,7 +711,7 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
{'action_name': 'graded', 'attempted': 2, 'succeeded': 2, 'failed': 0}, result
)
problem_names = [u'Homework 1: Problem - pröblem_a_url', u'Homework 1: Problem - pröblem_b_url']
problem_names = [u'Homework 1: Problem - problem_a_url', u'Homework 1: Problem - problem_b_url']
header_row = [u'Student ID', u'Email', u'Username', u'Final Grade']
for problem in problem_names:
header_row += [problem + ' (Earned)', problem + ' (Possible)']
......@@ -817,12 +817,12 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
display_name='Problem Vertical'
)
self.define_option_problem(
u"Pröblem0",
u"Problem0",
parent=vertical,
group_access={self.course.user_partitions[0].id: [self.course.user_partitions[0].groups[0].id]}
)
self.define_option_problem(
u"Pröblem1",
u"Problem1",
parent=vertical,
group_access={self.course.user_partitions[0].id: [self.course.user_partitions[0].groups[1].id]}
)
......@@ -845,20 +845,20 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
))
def test_cohort_content(self):
self.submit_student_answer(self.alpha_user.username, u'Pröblem0', ['Option 1', 'Option 1'])
resp = self.submit_student_answer(self.alpha_user.username, u'Pröblem1', ['Option 1', 'Option 1'])
self.submit_student_answer(self.alpha_user.username, u'Problem0', ['Option 1', 'Option 1'])
resp = self.submit_student_answer(self.alpha_user.username, u'Problem1', ['Option 1', 'Option 1'])
self.assertEqual(resp.status_code, 404)
resp = self.submit_student_answer(self.beta_user.username, u'Pröblem0', ['Option 1', 'Option 2'])
resp = self.submit_student_answer(self.beta_user.username, u'Problem0', ['Option 1', 'Option 2'])
self.assertEqual(resp.status_code, 404)
self.submit_student_answer(self.beta_user.username, u'Pröblem1', ['Option 1', 'Option 2'])
self.submit_student_answer(self.beta_user.username, u'Problem1', ['Option 1', 'Option 2'])
with patch('lms.djangoapps.instructor_task.tasks_helper._get_current_task'):
result = upload_problem_grade_report(None, None, self.course.id, None, 'graded')
self.assertDictContainsSubset(
{'action_name': 'graded', 'attempted': 4, 'succeeded': 4, 'failed': 0}, result
)
problem_names = [u'Homework 1: Problem - Pröblem0', u'Homework 1: Problem - Pröblem1']
problem_names = [u'Homework 1: Problem - Problem0', u'Homework 1: Problem - Problem1']
header_row = [u'Student ID', u'Email', u'Username', u'Final Grade']
for problem in problem_names:
header_row += [problem + ' (Earned)', problem + ' (Possible)']
......
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