Make it impossible to click "final check" without selecting a choice
This commit is contained in:
committed by
Dmitry Viskov
parent
47e4804b52
commit
61127b613b
@@ -247,6 +247,95 @@ describe 'Problem', ->
|
||||
runs ->
|
||||
expect(@problem.checkButtonLabel.text).toHaveBeenCalledWith 'Check'
|
||||
|
||||
describe 'check button on problems', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
@checkDisabled = (v) -> expect(@problem.checkButton.hasClass('is-disabled')).toBe(v)
|
||||
|
||||
describe 'some basic tests for check button', ->
|
||||
it 'should become enabled after a value is entered into the text box', ->
|
||||
$('#input_example_1').val('test').trigger('input')
|
||||
@checkDisabled false
|
||||
$('#input_example_1').val('').trigger('input')
|
||||
@checkDisabled true
|
||||
|
||||
describe 'some advanced tests for check button', ->
|
||||
it 'should become enabled after a checkbox is checked', ->
|
||||
html = '''
|
||||
<div class="choicegroup">
|
||||
<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>
|
||||
</div>
|
||||
'''
|
||||
$('#input_example_1').replaceWith(html)
|
||||
@problem.checkAnswersAndCheckButton true
|
||||
@checkDisabled true
|
||||
$('#input_1_1_1').attr('checked', true).trigger('click')
|
||||
@checkDisabled false
|
||||
$('#input_1_1_1').attr('checked', false).trigger('click')
|
||||
@checkDisabled true
|
||||
|
||||
it 'should become enabled after a radiobutton is checked', ->
|
||||
html = '''
|
||||
<div class="choicegroup">
|
||||
<label for="input_1_1_1"><input type="radio" name="input_1_1" id="input_1_1_1" value="1"> One</label>
|
||||
<label for="input_1_1_2"><input type="radio" name="input_1_1" id="input_1_1_2" value="2"> Two</label>
|
||||
<label for="input_1_1_3"><input type="radio" name="input_1_1" id="input_1_1_3" value="3"> Three</label>
|
||||
</div>
|
||||
'''
|
||||
$('#input_example_1').replaceWith(html)
|
||||
@problem.checkAnswersAndCheckButton true
|
||||
@checkDisabled true
|
||||
$('#input_1_1_1').attr('checked', true).trigger('click')
|
||||
@checkDisabled false
|
||||
$('#input_1_1_1').attr('checked', false).trigger('click')
|
||||
@checkDisabled true
|
||||
|
||||
it 'should become enabled after a value is selected in a selector', ->
|
||||
html = '''
|
||||
<div id="problem_sel">
|
||||
<select>
|
||||
<option value="val0"></option>
|
||||
<option value="val1">1</option>
|
||||
<option value="val2">2</option>
|
||||
</select>
|
||||
</div>
|
||||
'''
|
||||
$('#input_example_1').replaceWith(html)
|
||||
@problem.checkAnswersAndCheckButton true
|
||||
@checkDisabled true
|
||||
$("#problem_sel select").val("val2").trigger('change')
|
||||
@checkDisabled false
|
||||
$("#problem_sel select").val("val0").trigger('change')
|
||||
@checkDisabled true
|
||||
|
||||
it 'should become enabled after a radiobutton is checked and a value is entered into the text box', ->
|
||||
html = '''
|
||||
<div class="choicegroup">
|
||||
<label for="input_1_1_1"><input type="radio" name="input_1_1" id="input_1_1_1" value="1"> One</label>
|
||||
<label for="input_1_1_2"><input type="radio" name="input_1_1" id="input_1_1_2" value="2"> Two</label>
|
||||
<label for="input_1_1_3"><input type="radio" name="input_1_1" id="input_1_1_3" value="3"> Three</label>
|
||||
</div>
|
||||
'''
|
||||
$(html).insertAfter('#input_example_1')
|
||||
@problem.checkAnswersAndCheckButton true
|
||||
@checkDisabled true
|
||||
$('#input_1_1_1').attr('checked', true).trigger('click')
|
||||
@checkDisabled true
|
||||
$('#input_example_1').val('111').trigger('input')
|
||||
@checkDisabled false
|
||||
$('#input_1_1_1').attr('checked', false).trigger('click')
|
||||
@checkDisabled true
|
||||
|
||||
it 'should become enabled if there are only hidden input fields', ->
|
||||
html = '''
|
||||
<input type="text" name="test" id="test" aria-describedby="answer_test" value="" style="display:none;">
|
||||
'''
|
||||
$('#input_example_1').replaceWith(html)
|
||||
@problem.checkAnswersAndCheckButton true
|
||||
@checkDisabled false
|
||||
|
||||
describe 'reset', ->
|
||||
beforeEach ->
|
||||
@problem = new Problem($('.xblock-student_view'))
|
||||
|
||||
@@ -49,6 +49,8 @@ class @Problem
|
||||
window.globalTooltipManager.hide()
|
||||
|
||||
@bindResetCorrectness()
|
||||
if @checkButton.length
|
||||
@checkAnswersAndCheckButton true
|
||||
|
||||
# Collapsibles
|
||||
Collapsible.setCollapsibles(@el)
|
||||
@@ -452,6 +454,58 @@ class @Problem
|
||||
element.CodeMirror.save() if element.CodeMirror.save
|
||||
@answers = @inputs.serialize()
|
||||
|
||||
checkAnswersAndCheckButton: (bind=false) =>
|
||||
# Used to check available answers and if something is checked (or the answer is set in some textbox)
|
||||
# "Check"/"Final check" button becomes enabled. Otherwise it is disabled by default.
|
||||
# params:
|
||||
# 'bind' used on the first check to attach event handlers to input fields
|
||||
# to change "Check"/"Final check" enable status in case of some manipulations with answers
|
||||
answered = true
|
||||
|
||||
at_least_one_text_input_found = false
|
||||
one_text_input_filled = false
|
||||
@el.find("input:text").each (i, text_field) =>
|
||||
if $(text_field).is(':visible')
|
||||
at_least_one_text_input_found = true
|
||||
if $(text_field).val() isnt ''
|
||||
one_text_input_filled = true
|
||||
if bind
|
||||
$(text_field).on 'input', (e) =>
|
||||
@checkAnswersAndCheckButton()
|
||||
return
|
||||
return
|
||||
if at_least_one_text_input_found and not one_text_input_filled
|
||||
answered = false
|
||||
|
||||
@el.find(".choicegroup").each (i, choicegroup_block) =>
|
||||
checked = false
|
||||
$(choicegroup_block).find("input[type=checkbox], input[type=radio]").each (j, checkbox_or_radio) =>
|
||||
if $(checkbox_or_radio).is(':checked')
|
||||
checked = true
|
||||
if bind
|
||||
$(checkbox_or_radio).on 'click', (e) =>
|
||||
@checkAnswersAndCheckButton()
|
||||
return
|
||||
return
|
||||
if not checked
|
||||
answered = false
|
||||
return
|
||||
|
||||
@el.find("select").each (i, select_field) =>
|
||||
selected_option = $(select_field).find("option:selected").text().trim()
|
||||
if selected_option is ''
|
||||
answered = false
|
||||
if bind
|
||||
$(select_field).on 'change', (e) =>
|
||||
@checkAnswersAndCheckButton()
|
||||
return
|
||||
return
|
||||
|
||||
if answered
|
||||
@enableCheckButton true
|
||||
else
|
||||
@enableCheckButton false, false
|
||||
|
||||
bindResetCorrectness: ->
|
||||
# Loop through all input types
|
||||
# Bind the reset functions at that scope.
|
||||
|
||||
@@ -6,6 +6,7 @@ See also lettuce tests in lms/djangoapps/courseware/features/problems.feature
|
||||
import random
|
||||
import textwrap
|
||||
|
||||
from nose import SkipTest
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from nose.plugins.attrib import attr
|
||||
from selenium.webdriver import ActionChains
|
||||
@@ -135,6 +136,8 @@ class ProblemTypeTestMixin(object):
|
||||
"""
|
||||
Test cases shared amongst problem types.
|
||||
"""
|
||||
can_submit_blank = False
|
||||
|
||||
@attr('shard_7')
|
||||
def test_answer_correctly(self):
|
||||
"""
|
||||
@@ -200,15 +203,34 @@ class ProblemTypeTestMixin(object):
|
||||
Then my "<ProblemType>" answer is marked "incorrect"
|
||||
And The "<ProblemType>" problem displays a "blank" answer
|
||||
"""
|
||||
if not self.can_submit_blank:
|
||||
raise SkipTest("Test incompatible with the current problem type")
|
||||
|
||||
self.problem_page.wait_for(
|
||||
lambda: self.problem_page.problem_name == self.problem_name,
|
||||
"Make sure the correct problem is on the page"
|
||||
)
|
||||
|
||||
# Leave the problem unchanged and click check.
|
||||
self.assertNotIn('is-disabled', self.problem_page.q(css='div.problem button.check').attrs('class')[0])
|
||||
self.problem_page.click_check()
|
||||
self.wait_for_status('incorrect')
|
||||
|
||||
@attr('shard_7')
|
||||
def test_cant_submit_blank_answer(self):
|
||||
"""
|
||||
Scenario: I can't submit a blank answer
|
||||
When I try to submit blank answer
|
||||
Then I can't check a problem
|
||||
"""
|
||||
if self.can_submit_blank:
|
||||
raise SkipTest("Test incompatible with the current problem type")
|
||||
|
||||
self.problem_page.wait_for(
|
||||
lambda: self.problem_page.problem_name == self.problem_name,
|
||||
"Make sure the correct problem is on the page"
|
||||
)
|
||||
self.assertIn('is-disabled', self.problem_page.q(css='div.problem button.check').attrs('class')[0])
|
||||
|
||||
@attr('a11y')
|
||||
def test_problem_type_a11y(self):
|
||||
"""
|
||||
@@ -236,6 +258,8 @@ class AnnotationProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
|
||||
|
||||
factory = AnnotationResponseXMLFactory()
|
||||
|
||||
can_submit_blank = True
|
||||
|
||||
factory_kwargs = {
|
||||
'title': 'Annotation Problem',
|
||||
'text': 'The text being annotated',
|
||||
@@ -686,6 +710,13 @@ class CodeProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_cant_submit_blank_answer(self):
|
||||
"""
|
||||
Overridden for script test because the testing grader always responds
|
||||
with "correct"
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ChoiceTextProbelmTypeTestBase(ProblemTypeTestBase):
|
||||
"""
|
||||
@@ -801,6 +832,8 @@ class ImageProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
|
||||
|
||||
factory = ImageResponseXMLFactory()
|
||||
|
||||
can_submit_blank = True
|
||||
|
||||
factory_kwargs = {
|
||||
'src': '/static/images/placeholder-image.png',
|
||||
'rectangle': '(0,0)-(50,50)',
|
||||
|
||||
@@ -180,16 +180,22 @@ Feature: LMS.Answer problems
|
||||
|
||||
Examples:
|
||||
| ProblemType | Points Possible |
|
||||
| drop down | 1 point possible |
|
||||
| multiple choice | 1 point possible |
|
||||
| checkbox | 1 point possible |
|
||||
| radio | 1 point possible |
|
||||
#| string | 1 point possible |
|
||||
| numerical | 1 point possible |
|
||||
| formula | 1 point possible |
|
||||
| script | 2 points possible |
|
||||
| image | 1 point possible |
|
||||
|
||||
Scenario: I can't submit a blank answer
|
||||
Given I am viewing a "<ProblemType>" problem
|
||||
Then I can't check a problem
|
||||
|
||||
Examples:
|
||||
| ProblemType |
|
||||
| drop down |
|
||||
| multiple choice |
|
||||
| checkbox |
|
||||
| radio |
|
||||
| string |
|
||||
| numerical |
|
||||
| formula |
|
||||
| script |
|
||||
|
||||
Scenario: I can reset the correctness of a problem after changing my answer
|
||||
Given I am viewing a "<ProblemType>" problem
|
||||
@@ -234,21 +240,3 @@ Feature: LMS.Answer problems
|
||||
| multiple choice | incorrect | correct |
|
||||
| radio | correct | incorrect |
|
||||
| radio | incorrect | correct |
|
||||
|
||||
|
||||
Scenario: I can reset the correctness of a problem after submitting a blank answer
|
||||
Given I am viewing a "<ProblemType>" problem
|
||||
When I check a problem
|
||||
And I input an answer on a "<ProblemType>" problem "correctly"
|
||||
Then my "<ProblemType>" answer is marked "unanswered"
|
||||
|
||||
Examples:
|
||||
| ProblemType |
|
||||
| drop down |
|
||||
| multiple choice |
|
||||
| checkbox |
|
||||
| radio |
|
||||
#| string |
|
||||
| numerical |
|
||||
| formula |
|
||||
| script |
|
||||
|
||||
@@ -92,12 +92,21 @@ def check_problem(step):
|
||||
# first scroll down so the loading mathjax button does not
|
||||
# cover up the Check button
|
||||
world.browser.execute_script("window.scrollTo(0,1024)")
|
||||
assert world.is_css_not_present("button.check.is-disabled")
|
||||
world.css_click("button.check")
|
||||
|
||||
# Wait for the problem to finish re-rendering
|
||||
world.wait_for_ajax_complete()
|
||||
|
||||
|
||||
@step(u"I can't check a problem")
|
||||
def assert_cant_check_problem(step): # pylint: disable=unused-argument
|
||||
# first scroll down so the loading mathjax button does not
|
||||
# cover up the Check button
|
||||
world.browser.execute_script("window.scrollTo(0,1024)")
|
||||
assert world.is_css_present("button.check.is-disabled")
|
||||
|
||||
|
||||
@step(u'The "([^"]*)" problem displays a "([^"]*)" answer')
|
||||
def assert_problem_has_answer(step, problem_type, answer_class):
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user