Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
61127b61
Commit
61127b61
authored
Dec 23, 2015
by
Dmitry Viskov
Committed by
Dmitry Viskov
Apr 21, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make it impossible to click "final check" without selecting a choice
parent
47e4804b
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
200 additions
and
27 deletions
+200
-27
common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
+89
-0
common/lib/xmodule/xmodule/js/src/capa/display.coffee
+54
-0
common/test/acceptance/tests/lms/test_problem_types.py
+34
-1
lms/djangoapps/courseware/features/problems.feature
+14
-26
lms/djangoapps/courseware/features/problems.py
+9
-0
No files found.
common/lib/xmodule/xmodule/js/spec/capa/display_spec.coffee
View file @
61127b61
...
...
@@ -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'
))
...
...
common/lib/xmodule/xmodule/js/src/capa/display.coffee
View file @
61127b61
...
...
@@ -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.
...
...
common/test/acceptance/tests/lms/test_problem_types.py
View file @
61127b61
...
...
@@ -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)'
,
...
...
lms/djangoapps/courseware/features/problems.feature
View file @
61127b61
...
...
@@ -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
|
lms/djangoapps/courseware/features/problems.py
View file @
61127b61
...
...
@@ -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
):
'''
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment