Show checkmarks when "Show Answer" is clicked.
TNL-6357
This commit is contained in:
@@ -20,6 +20,7 @@ except ImportError:
|
||||
from pytz import utc
|
||||
|
||||
from capa.capa_problem import LoncapaProblem, LoncapaSystem
|
||||
from capa.inputtypes import Status
|
||||
from capa.responsetypes import StudentInputError, ResponseError, LoncapaProblemError
|
||||
from capa.util import convert_files_to_filenames, get_inner_html_from_xpath
|
||||
from xblock.fields import Boolean, Dict, Float, Integer, Scope, String, XMLString
|
||||
@@ -942,7 +943,12 @@ class CapaMixin(CapaFields):
|
||||
"""
|
||||
For the "show answer" button.
|
||||
|
||||
Returns the answers: {'answers' : answers}
|
||||
Returns the answers and rendered "correct status span" HTML:
|
||||
{'answers' : answers, 'correct_status_html': correct_status_span_html}.
|
||||
The "correct status span" HTML is injected beside the correct answers
|
||||
for radio button and checkmark problems, so that there is a visual
|
||||
indication of the correct answers that is not solely based on color
|
||||
(and also screen reader text).
|
||||
"""
|
||||
event_info = dict()
|
||||
event_info['problem_id'] = self.location.to_deprecated_string()
|
||||
@@ -968,7 +974,13 @@ class CapaMixin(CapaFields):
|
||||
new_answer = {answer_id: answers[answer_id]}
|
||||
new_answers.update(new_answer)
|
||||
|
||||
return {'answers': new_answers}
|
||||
return {
|
||||
'answers': new_answers,
|
||||
'correct_status_html': self.runtime.render_template(
|
||||
'status_span.html',
|
||||
{'status': Status('correct', self.runtime.service(self, "i18n").ugettext)}
|
||||
)
|
||||
}
|
||||
|
||||
# Figure out if we should move these to capa_problem?
|
||||
def get_problem(self, _data):
|
||||
|
||||
@@ -457,24 +457,6 @@ describe 'Problem', ->
|
||||
|
||||
expect(window.SR.readText).toHaveBeenCalledWith 'Answers to this problem are now shown. Navigate through the problem to review it with answers inline.'
|
||||
|
||||
describe 'multiple choice question', ->
|
||||
beforeEach ->
|
||||
@problem.el.prepend '''
|
||||
<label for="input_1_1_1"><input type="checkbox" name="input_1_1" id="input_1_1_1" value="1"> One</label>
|
||||
<label for="input_1_1_2"><input type="checkbox" name="input_1_1" id="input_1_1_2" value="2"> Two</label>
|
||||
<label for="input_1_1_3"><input type="checkbox" name="input_1_1" id="input_1_1_3" value="3"> Three</label>
|
||||
<label for="input_1_2_1"><input type="radio" name="input_1_2" id="input_1_2_1" value="1"> Other</label>
|
||||
'''
|
||||
|
||||
it 'set the correct_answer attribute on the choice', ->
|
||||
spyOn($, 'postWithPrefix').and.callFake (url, callback) ->
|
||||
callback answers: '1_1': [2, 3]
|
||||
@problem.show()
|
||||
expect($('label[for="input_1_1_1"]')).not.toHaveAttr 'correct_answer', 'true'
|
||||
expect($('label[for="input_1_1_2"]')).toHaveAttr 'correct_answer', 'true'
|
||||
expect($('label[for="input_1_1_3"]')).toHaveAttr 'correct_answer', 'true'
|
||||
expect($('label[for="input_1_2_1"]')).not.toHaveAttr 'correct_answer', 'true'
|
||||
|
||||
describe 'radio text question', ->
|
||||
radio_text_xml='''
|
||||
<section class="problem">
|
||||
|
||||
@@ -681,17 +681,8 @@
|
||||
var answers;
|
||||
answers = response.answers;
|
||||
$.each(answers, function(key, value) {
|
||||
var answer, choice, i, len, results;
|
||||
if ($.isArray(value)) {
|
||||
results = [];
|
||||
for (i = 0, len = value.length; i < len; i++) {
|
||||
choice = value[i];
|
||||
results.push(that.$('label[for="input_' + key + '_' + choice + '"]').attr({
|
||||
correct_answer: 'true'
|
||||
}));
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
var answer;
|
||||
if (!$.isArray(value)) {
|
||||
answer = that.$('#answer_' + key + ', #solution_' + key);
|
||||
edx.HtmlUtils.setHtml(answer, edx.HtmlUtils.HTML(value));
|
||||
Collapsible.setCollapsibles(answer);
|
||||
@@ -722,7 +713,7 @@
|
||||
display = that.inputtypeDisplays[$(inputtype).attr('id')];
|
||||
showMethod = that.inputtypeShowAnswerMethods[cls];
|
||||
if (showMethod != null) {
|
||||
results.push(showMethod(inputtype, display, answers));
|
||||
results.push(showMethod(inputtype, display, answers, response.correct_status_html));
|
||||
} else {
|
||||
results.push(void 0);
|
||||
}
|
||||
@@ -947,10 +938,10 @@
|
||||
var $status;
|
||||
$status = $('#status_' + id);
|
||||
if ($status[0]) {
|
||||
$status.removeAttr('class').addClass('unanswered');
|
||||
$status.removeAttr('class').addClass('status unanswered');
|
||||
} else {
|
||||
$('<span>', {
|
||||
class: 'unanswered',
|
||||
class: 'status unanswered',
|
||||
style: 'display: inline-block;',
|
||||
id: 'status_' + id
|
||||
});
|
||||
@@ -1030,16 +1021,30 @@
|
||||
};
|
||||
|
||||
Problem.prototype.inputtypeShowAnswerMethods = {
|
||||
choicegroup: function(element, display, answers) {
|
||||
var answer, choice, inputId, i, len, results, $element;
|
||||
choicegroup: function(element, display, answers, correctStatusHtml) {
|
||||
var answer, choice, inputId, i, len, results, $element, $inputLabel, $inputStatus;
|
||||
$element = $(element);
|
||||
inputId = $element.attr('id').replace(/inputtype_/, '');
|
||||
answer = answers[inputId];
|
||||
results = [];
|
||||
for (i = 0, len = answer.length; i < len; i++) {
|
||||
choice = answer[i];
|
||||
results.push($element.find('#input_' + inputId + '_' + choice).parent('label').
|
||||
addClass('choicegroup_correct'));
|
||||
$inputLabel = $element.find('#input_' + inputId + '_' + choice).parent('label');
|
||||
$inputStatus = $inputLabel.find('#status_' + inputId);
|
||||
// If the correct answer was already Submitted before "Show Answer" was selected,
|
||||
// the status HTML will already be present. Otherwise, inject the status HTML.
|
||||
|
||||
// If the learner clicked a different answer after Submit, their submitted answers
|
||||
// will be marked as "unanswered". In that case, for correct answers update the
|
||||
// classes accordingly.
|
||||
if ($inputStatus.hasClass('unanswered')) {
|
||||
$inputStatus.removeAttr('class').addClass('status correct');
|
||||
$inputLabel.addClass('choicegroup_correct');
|
||||
} else if (!$inputLabel.hasClass('choicegroup_correct')) {
|
||||
// If the status HTML is not already present (due to clicking Submit), append
|
||||
// the status HTML for correct answers.
|
||||
results.push($inputLabel.addClass('choicegroup_correct').append(correctStatusHtml));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
@@ -413,10 +413,17 @@ class ProblemPage(PageObject):
|
||||
"""
|
||||
Check if correct answer/choice highlighted for choice group.
|
||||
"""
|
||||
xpath = '//fieldset/div[contains(@class, "field")][{0}]/label[contains(@class, "choicegroup_correct")]'
|
||||
correct_status_xpath = '//fieldset/div[contains(@class, "field")][{0}]/label[contains(@class, "choicegroup_correct")]/span[contains(@class, "status correct")]' # pylint: disable=line-too-long
|
||||
any_status_xpath = '//fieldset/div[contains(@class, "field")][{0}]/label/span'
|
||||
for choice in correct_choices:
|
||||
if not self.q(xpath=xpath.format(choice)).is_present():
|
||||
if not self.q(xpath=correct_status_xpath.format(choice)).is_present():
|
||||
return False
|
||||
|
||||
# Check that there is only a single status span, as there were some bugs with multiple
|
||||
# spans (with various classes) being appended.
|
||||
if not len(self.q(xpath=any_status_xpath.format(choice)).results) == 1:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
|
||||
@@ -535,6 +535,35 @@ class MultipleChoiceProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
|
||||
else:
|
||||
self.problem_page.click_choice("choice_choice_2")
|
||||
|
||||
@attr(shard=7)
|
||||
def test_can_show_answer(self):
|
||||
"""
|
||||
Scenario: Verifies that show answer button is working as expected.
|
||||
|
||||
Given that I am on courseware page
|
||||
And I can see a CAPA problem with show answer button
|
||||
When I click "Show Answer" button
|
||||
The correct answer is displayed with a single correctness indicator.
|
||||
"""
|
||||
# Click the correct answer, but don't submit yet. No correctness shows.
|
||||
self.answer_problem('correct')
|
||||
self.assertFalse(self.problem_page.is_correct_choice_highlighted(correct_choices=[3]))
|
||||
|
||||
# After submit, the answer should be marked as correct.
|
||||
self.problem_page.click_submit()
|
||||
self.assertTrue(self.problem_page.is_correct_choice_highlighted(correct_choices=[3]))
|
||||
|
||||
# Switch to an incorrect answer. This will hide the correctness indicator.
|
||||
self.answer_problem('incorrect')
|
||||
self.assertFalse(self.problem_page.is_correct_choice_highlighted(correct_choices=[3]))
|
||||
|
||||
# Now click Show Answer. A single correctness indicator should be shown.
|
||||
self.problem_page.click_show()
|
||||
self.assertTrue(self.problem_page.is_correct_choice_highlighted(correct_choices=[3]))
|
||||
|
||||
# Finally, make sure that clicking Show Answer moved focus to the correct place.
|
||||
self.problem_page.wait_for_focus_on_problem_meta()
|
||||
|
||||
|
||||
class RadioProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user