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
a70a23a4
Commit
a70a23a4
authored
Mar 18, 2013
by
Jay Zoldak
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1680 from MITx/feature/will/courseware_lettuce_tests
Feature/will/courseware lettuce tests
parents
c50bf567
568f557d
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
577 additions
and
106 deletions
+577
-106
common/djangoapps/terrain/browser.py
+5
-0
common/djangoapps/terrain/factories.py
+31
-4
common/djangoapps/terrain/steps.py
+25
-14
common/lib/capa/capa/tests/response_xml_factory.py
+40
-34
lms/djangoapps/courseware/features/common.py
+84
-3
lms/djangoapps/courseware/features/courses.py
+1
-0
lms/djangoapps/courseware/features/courseware.feature
+0
-11
lms/djangoapps/courseware/features/high-level-tabs.feature
+15
-18
lms/djangoapps/courseware/features/homepage.feature
+2
-2
lms/djangoapps/courseware/features/login.py
+1
-0
lms/djangoapps/courseware/features/openended.feature
+2
-2
lms/djangoapps/courseware/features/problems.feature
+73
-0
lms/djangoapps/courseware/features/problems.py
+271
-0
lms/djangoapps/courseware/features/registration.feature
+4
-3
lms/djangoapps/courseware/features/registration.py
+6
-5
lms/djangoapps/courseware/features/smart-accordion.feature
+1
-2
lms/envs/acceptance.py
+16
-8
No files found.
common/djangoapps/terrain/browser.py
View file @
a70a23a4
...
@@ -3,6 +3,11 @@ from splinter.browser import Browser
...
@@ -3,6 +3,11 @@ from splinter.browser import Browser
from
logging
import
getLogger
from
logging
import
getLogger
import
time
import
time
# Let the LMS and CMS do their one-time setup
# For example, setting up mongo caches
from
lms
import
one_time_startup
from
cms
import
one_time_startup
logger
=
getLogger
(
__name__
)
logger
=
getLogger
(
__name__
)
logger
.
info
(
"Loading the lettuce acceptance testing terrain file..."
)
logger
.
info
(
"Loading the lettuce acceptance testing terrain file..."
)
...
...
common/djangoapps/terrain/factories.py
View file @
a70a23a4
...
@@ -121,21 +121,41 @@ class XModuleItemFactory(Factory):
...
@@ -121,21 +121,41 @@ class XModuleItemFactory(Factory):
@classmethod
@classmethod
def
_create
(
cls
,
target_class
,
*
args
,
**
kwargs
):
def
_create
(
cls
,
target_class
,
*
args
,
**
kwargs
):
"""
"""
kwargs must include parent_location, template. Can contain display_name
Uses *kwargs*:
target_class is ignored
*parent_location* (required): the location of the parent module
(e.g. the parent course or section)
*template* (required): the template to create the item from
(e.g. i4x://templates/section/Empty)
*data* (optional): the data for the item
(e.g. XML problem definition for a problem item)
*display_name* (optional): the display name of the item
*metadata* (optional): dictionary of metadata attributes
*target_class* is ignored
"""
"""
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
parent_location
=
Location
(
kwargs
.
get
(
'parent_location'
))
parent_location
=
Location
(
kwargs
.
get
(
'parent_location'
))
template
=
Location
(
kwargs
.
get
(
'template'
))
template
=
Location
(
kwargs
.
get
(
'template'
))
data
=
kwargs
.
get
(
'data'
)
display_name
=
kwargs
.
get
(
'display_name'
)
display_name
=
kwargs
.
get
(
'display_name'
)
metadata
=
kwargs
.
get
(
'metadata'
,
{})
store
=
modulestore
(
'direct'
)
store
=
modulestore
(
'direct'
)
# This code was based off that in cms/djangoapps/contentstore/views.py
# This code was based off that in cms/djangoapps/contentstore/views.py
parent
=
store
.
get_item
(
parent_location
)
parent
=
store
.
get_item
(
parent_location
)
dest_location
=
parent_location
.
_replace
(
category
=
template
.
category
,
name
=
uuid4
()
.
hex
)
# If a display name is set, use that
dest_name
=
display_name
.
replace
(
" "
,
"_"
)
if
display_name
is
not
None
else
uuid4
()
.
hex
dest_location
=
parent_location
.
_replace
(
category
=
template
.
category
,
name
=
dest_name
)
new_item
=
store
.
clone_item
(
template
,
dest_location
)
new_item
=
store
.
clone_item
(
template
,
dest_location
)
...
@@ -143,7 +163,14 @@ class XModuleItemFactory(Factory):
...
@@ -143,7 +163,14 @@ class XModuleItemFactory(Factory):
if
display_name
is
not
None
:
if
display_name
is
not
None
:
new_item
.
display_name
=
display_name
new_item
.
display_name
=
display_name
store
.
update_metadata
(
new_item
.
location
.
url
(),
own_metadata
(
new_item
))
# Add additional metadata or override current metadata
item_metadata
=
own_metadata
(
new_item
)
item_metadata
.
update
(
metadata
)
store
.
update_metadata
(
new_item
.
location
.
url
(),
item_metadata
)
# replace the data with the optional *data* parameter
if
data
is
not
None
:
store
.
update_item
(
new_item
.
location
,
data
)
if
new_item
.
location
.
category
not
in
DETACHED_CATEGORIES
:
if
new_item
.
location
.
category
not
in
DETACHED_CATEGORIES
:
store
.
update_children
(
parent_location
,
parent
.
children
+
[
new_item
.
location
.
url
()])
store
.
update_children
(
parent_location
,
parent
.
children
+
[
new_item
.
location
.
url
()])
...
...
common/djangoapps/terrain/steps.py
View file @
a70a23a4
...
@@ -69,6 +69,11 @@ def the_page_title_should_be(step, title):
...
@@ -69,6 +69,11 @@ def the_page_title_should_be(step, title):
assert_equals
(
world
.
browser
.
title
,
title
)
assert_equals
(
world
.
browser
.
title
,
title
)
@step
(
u'the page title should contain "([^"]*)"$'
)
def
the_page_title_should_contain
(
step
,
title
):
assert
(
title
in
world
.
browser
.
title
)
@step
(
'I am a logged in user$'
)
@step
(
'I am a logged in user$'
)
def
i_am_logged_in_user
(
step
):
def
i_am_logged_in_user
(
step
):
create_user
(
'robot'
)
create_user
(
'robot'
)
...
@@ -80,18 +85,6 @@ def i_am_not_logged_in(step):
...
@@ -80,18 +85,6 @@ def i_am_not_logged_in(step):
world
.
browser
.
cookies
.
delete
()
world
.
browser
.
cookies
.
delete
()
@step
(
'I am registered for a course$'
)
def
i_am_registered_for_a_course
(
step
):
create_user
(
'robot'
)
u
=
User
.
objects
.
get
(
username
=
'robot'
)
CourseEnrollment
.
objects
.
get_or_create
(
user
=
u
,
course_id
=
'MITx/6.002x/2012_Fall'
)
@step
(
'I am registered for course "([^"]*)"$'
)
def
i_am_registered_for_course_by_id
(
step
,
course_id
):
register_by_course_id
(
course_id
)
@step
(
'I am staff for course "([^"]*)"$'
)
@step
(
'I am staff for course "([^"]*)"$'
)
def
i_am_staff_for_course_by_id
(
step
,
course_id
):
def
i_am_staff_for_course_by_id
(
step
,
course_id
):
register_by_course_id
(
course_id
,
True
)
register_by_course_id
(
course_id
,
True
)
...
@@ -108,6 +101,7 @@ def i_am_an_edx_user(step):
...
@@ -108,6 +101,7 @@ def i_am_an_edx_user(step):
#### helper functions
#### helper functions
@world.absorb
@world.absorb
def
scroll_to_bottom
():
def
scroll_to_bottom
():
# Maximize the browser
# Maximize the browser
...
@@ -116,6 +110,11 @@ def scroll_to_bottom():
...
@@ -116,6 +110,11 @@ def scroll_to_bottom():
@world.absorb
@world.absorb
def
create_user
(
uname
):
def
create_user
(
uname
):
# If the user already exists, don't try to create it again
if
len
(
User
.
objects
.
filter
(
username
=
uname
))
>
0
:
return
portal_user
=
UserFactory
.
build
(
username
=
uname
,
email
=
uname
+
'@edx.org'
)
portal_user
=
UserFactory
.
build
(
username
=
uname
,
email
=
uname
+
'@edx.org'
)
portal_user
.
set_password
(
'test'
)
portal_user
.
set_password
(
'test'
)
portal_user
.
save
()
portal_user
.
save
()
...
@@ -133,13 +132,25 @@ def log_in(email, password):
...
@@ -133,13 +132,25 @@ def log_in(email, password):
world
.
browser
.
visit
(
django_url
(
'/'
))
world
.
browser
.
visit
(
django_url
(
'/'
))
world
.
browser
.
is_element_present_by_css
(
'header.global'
,
10
)
world
.
browser
.
is_element_present_by_css
(
'header.global'
,
10
)
world
.
browser
.
click_link_by_href
(
'#login-modal'
)
world
.
browser
.
click_link_by_href
(
'#login-modal'
)
login_form
=
world
.
browser
.
find_by_css
(
'form#login_form'
)
# Wait for the login dialog to load
# This is complicated by the fact that sometimes a second #login_form
# dialog loads, while the first one remains hidden.
# We give them both time to load, starting with the second one.
world
.
browser
.
is_element_present_by_css
(
'section.content-wrapper form#login_form'
,
wait_time
=
4
)
world
.
browser
.
is_element_present_by_css
(
'form#login_form'
,
wait_time
=
2
)
# For some reason, the page sometimes includes two #login_form
# elements, the first of which is not visible.
# To avoid this, we always select the last of the two #login_form dialogs
login_form
=
world
.
browser
.
find_by_css
(
'form#login_form'
)
.
last
login_form
.
find_by_name
(
'email'
)
.
fill
(
email
)
login_form
.
find_by_name
(
'email'
)
.
fill
(
email
)
login_form
.
find_by_name
(
'password'
)
.
fill
(
password
)
login_form
.
find_by_name
(
'password'
)
.
fill
(
password
)
login_form
.
find_by_name
(
'submit'
)
.
click
()
login_form
.
find_by_name
(
'submit'
)
.
click
()
# wait for the page to redraw
# wait for the page to redraw
assert
world
.
browser
.
is_element_present_by_css
(
'.content-wrapper'
,
10
)
assert
world
.
browser
.
is_element_present_by_css
(
'.content-wrapper'
,
wait_time
=
10
)
@world.absorb
@world.absorb
...
...
common/lib/capa/capa/tests/response_xml_factory.py
View file @
a70a23a4
from
lxml
import
etree
from
lxml
import
etree
from
abc
import
ABCMeta
,
abstractmethod
from
abc
import
ABCMeta
,
abstractmethod
class
ResponseXMLFactory
(
object
):
class
ResponseXMLFactory
(
object
):
""" Abstract base class for capa response XML factories.
""" Abstract base class for capa response XML factories.
Subclasses override create_response_element and
Subclasses override create_response_element and
...
@@ -13,7 +14,7 @@ class ResponseXMLFactory(object):
...
@@ -13,7 +14,7 @@ class ResponseXMLFactory(object):
""" Subclasses override to return an etree element
""" Subclasses override to return an etree element
representing the capa response XML
representing the capa response XML
(e.g. <numericalresponse>).
(e.g. <numericalresponse>).
The tree should NOT contain any input elements
The tree should NOT contain any input elements
(such as <textline />) as these will be added later."""
(such as <textline />) as these will be added later."""
return
None
return
None
...
@@ -25,7 +26,7 @@ class ResponseXMLFactory(object):
...
@@ -25,7 +26,7 @@ class ResponseXMLFactory(object):
return
None
return
None
def
build_xml
(
self
,
**
kwargs
):
def
build_xml
(
self
,
**
kwargs
):
""" Construct an XML string for a capa response
""" Construct an XML string for a capa response
based on **kwargs.
based on **kwargs.
**kwargs is a dictionary that will be passed
**kwargs is a dictionary that will be passed
...
@@ -37,7 +38,7 @@ class ResponseXMLFactory(object):
...
@@ -37,7 +38,7 @@ class ResponseXMLFactory(object):
*question_text*: The text of the question to display,
*question_text*: The text of the question to display,
wrapped in <p> tags.
wrapped in <p> tags.
*explanation_text*: The detailed explanation that will
*explanation_text*: The detailed explanation that will
be shown if the user answers incorrectly.
be shown if the user answers incorrectly.
...
@@ -75,7 +76,7 @@ class ResponseXMLFactory(object):
...
@@ -75,7 +76,7 @@ class ResponseXMLFactory(object):
for
i
in
range
(
0
,
int
(
num_responses
)):
for
i
in
range
(
0
,
int
(
num_responses
)):
response_element
=
self
.
create_response_element
(
**
kwargs
)
response_element
=
self
.
create_response_element
(
**
kwargs
)
root
.
append
(
response_element
)
root
.
append
(
response_element
)
# Add input elements
# Add input elements
for
j
in
range
(
0
,
int
(
num_inputs
)):
for
j
in
range
(
0
,
int
(
num_inputs
)):
input_element
=
self
.
create_input_element
(
**
kwargs
)
input_element
=
self
.
create_input_element
(
**
kwargs
)
...
@@ -135,7 +136,7 @@ class ResponseXMLFactory(object):
...
@@ -135,7 +136,7 @@ class ResponseXMLFactory(object):
# Names of group elements
# Names of group elements
group_element_names
=
{
'checkbox'
:
'checkboxgroup'
,
group_element_names
=
{
'checkbox'
:
'checkboxgroup'
,
'radio'
:
'radiogroup'
,
'radio'
:
'radiogroup'
,
'multiple'
:
'choicegroup'
}
'multiple'
:
'choicegroup'
}
# Retrieve **kwargs
# Retrieve **kwargs
choices
=
kwargs
.
get
(
'choices'
,
[
True
])
choices
=
kwargs
.
get
(
'choices'
,
[
True
])
...
@@ -151,13 +152,11 @@ class ResponseXMLFactory(object):
...
@@ -151,13 +152,11 @@ class ResponseXMLFactory(object):
choice_element
=
etree
.
SubElement
(
group_element
,
"choice"
)
choice_element
=
etree
.
SubElement
(
group_element
,
"choice"
)
choice_element
.
set
(
"correct"
,
"true"
if
correct_val
else
"false"
)
choice_element
.
set
(
"correct"
,
"true"
if
correct_val
else
"false"
)
# Add some text describing the choice
etree
.
SubElement
(
choice_element
,
"startouttext"
)
etree
.
text
=
"Choice description"
etree
.
SubElement
(
choice_element
,
"endouttext"
)
# Add a name identifying the choice, if one exists
# Add a name identifying the choice, if one exists
# For simplicity, we use the same string as both the
# name attribute and the text of the element
if
name
:
if
name
:
choice_element
.
text
=
str
(
name
)
choice_element
.
set
(
"name"
,
str
(
name
))
choice_element
.
set
(
"name"
,
str
(
name
))
return
group_element
return
group_element
...
@@ -217,7 +216,7 @@ class CustomResponseXMLFactory(ResponseXMLFactory):
...
@@ -217,7 +216,7 @@ class CustomResponseXMLFactory(ResponseXMLFactory):
*answer*: Inline script that calculates the answer
*answer*: Inline script that calculates the answer
"""
"""
# Retrieve **kwargs
# Retrieve **kwargs
cfn
=
kwargs
.
get
(
'cfn'
,
None
)
cfn
=
kwargs
.
get
(
'cfn'
,
None
)
expect
=
kwargs
.
get
(
'expect'
,
None
)
expect
=
kwargs
.
get
(
'expect'
,
None
)
...
@@ -247,7 +246,7 @@ class SchematicResponseXMLFactory(ResponseXMLFactory):
...
@@ -247,7 +246,7 @@ class SchematicResponseXMLFactory(ResponseXMLFactory):
def
create_response_element
(
self
,
**
kwargs
):
def
create_response_element
(
self
,
**
kwargs
):
""" Create the <schematicresponse> XML element.
""" Create the <schematicresponse> XML element.
Uses *kwargs*:
Uses *kwargs*:
*answer*: The Python script used to evaluate the answer.
*answer*: The Python script used to evaluate the answer.
...
@@ -274,6 +273,7 @@ class SchematicResponseXMLFactory(ResponseXMLFactory):
...
@@ -274,6 +273,7 @@ class SchematicResponseXMLFactory(ResponseXMLFactory):
For testing, we create a bare-bones version of <schematic>."""
For testing, we create a bare-bones version of <schematic>."""
return
etree
.
Element
(
"schematic"
)
return
etree
.
Element
(
"schematic"
)
class
CodeResponseXMLFactory
(
ResponseXMLFactory
):
class
CodeResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for creating <coderesponse> XML trees """
""" Factory for creating <coderesponse> XML trees """
...
@@ -286,9 +286,9 @@ class CodeResponseXMLFactory(ResponseXMLFactory):
...
@@ -286,9 +286,9 @@ class CodeResponseXMLFactory(ResponseXMLFactory):
def
create_response_element
(
self
,
**
kwargs
):
def
create_response_element
(
self
,
**
kwargs
):
""" Create a <coderesponse> XML element:
""" Create a <coderesponse> XML element:
Uses **kwargs:
Uses **kwargs:
*initial_display*: The code that initially appears in the textbox
*initial_display*: The code that initially appears in the textbox
[DEFAULT: "Enter code here"]
[DEFAULT: "Enter code here"]
*answer_display*: The answer to display to the student
*answer_display*: The answer to display to the student
...
@@ -328,6 +328,7 @@ class CodeResponseXMLFactory(ResponseXMLFactory):
...
@@ -328,6 +328,7 @@ class CodeResponseXMLFactory(ResponseXMLFactory):
# return None here
# return None here
return
None
return
None
class
ChoiceResponseXMLFactory
(
ResponseXMLFactory
):
class
ChoiceResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for creating <choiceresponse> XML trees """
""" Factory for creating <choiceresponse> XML trees """
...
@@ -356,13 +357,13 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
...
@@ -356,13 +357,13 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
*num_samples*: The number of times to sample the student's answer
*num_samples*: The number of times to sample the student's answer
to numerically compare it to the correct answer.
to numerically compare it to the correct answer.
*tolerance*: The tolerance within which answers will be accepted
*tolerance*: The tolerance within which answers will be accepted
[DEFAULT: 0.01]
[DEFAULT: 0.01]
*answer*: The answer to the problem. Can be a formula string
*answer*: The answer to the problem. Can be a formula string
or a Python variable defined in a script
or a Python variable defined in a script
(e.g. "$calculated_answer" for a Python variable
(e.g. "$calculated_answer" for a Python variable
called calculated_answer)
called calculated_answer)
[REQUIRED]
[REQUIRED]
...
@@ -387,7 +388,7 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
...
@@ -387,7 +388,7 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
# Set the sample information
# Set the sample information
sample_str
=
self
.
_sample_str
(
sample_dict
,
num_samples
,
tolerance
)
sample_str
=
self
.
_sample_str
(
sample_dict
,
num_samples
,
tolerance
)
response_element
.
set
(
"samples"
,
sample_str
)
response_element
.
set
(
"samples"
,
sample_str
)
# Set the tolerance
# Set the tolerance
responseparam_element
=
etree
.
SubElement
(
response_element
,
"responseparam"
)
responseparam_element
=
etree
.
SubElement
(
response_element
,
"responseparam"
)
...
@@ -408,7 +409,7 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
...
@@ -408,7 +409,7 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
# We could sample a different range, but for simplicity,
# We could sample a different range, but for simplicity,
# we use the same sample string for the hints
# we use the same sample string for the hints
# that we used previously.
# that we used previously.
formulahint_element
.
set
(
"samples"
,
sample_str
)
formulahint_element
.
set
(
"samples"
,
sample_str
)
formulahint_element
.
set
(
"answer"
,
str
(
hint_prompt
))
formulahint_element
.
set
(
"answer"
,
str
(
hint_prompt
))
...
@@ -436,10 +437,11 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
...
@@ -436,10 +437,11 @@ class FormulaResponseXMLFactory(ResponseXMLFactory):
high_range_vals
=
[
str
(
f
[
1
])
for
f
in
sample_dict
.
values
()]
high_range_vals
=
[
str
(
f
[
1
])
for
f
in
sample_dict
.
values
()]
sample_str
=
(
","
.
join
(
sample_dict
.
keys
())
+
"@"
+
sample_str
=
(
","
.
join
(
sample_dict
.
keys
())
+
"@"
+
","
.
join
(
low_range_vals
)
+
":"
+
","
.
join
(
low_range_vals
)
+
":"
+
","
.
join
(
high_range_vals
)
+
","
.
join
(
high_range_vals
)
+
"#"
+
str
(
num_samples
))
"#"
+
str
(
num_samples
))
return
sample_str
return
sample_str
class
ImageResponseXMLFactory
(
ResponseXMLFactory
):
class
ImageResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <imageresponse> XML """
""" Factory for producing <imageresponse> XML """
...
@@ -450,9 +452,9 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
...
@@ -450,9 +452,9 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
def
create_input_element
(
self
,
**
kwargs
):
def
create_input_element
(
self
,
**
kwargs
):
""" Create the <imageinput> element.
""" Create the <imageinput> element.
Uses **kwargs:
Uses **kwargs:
*src*: URL for the image file [DEFAULT: "/static/image.jpg"]
*src*: URL for the image file [DEFAULT: "/static/image.jpg"]
*width*: Width of the image [DEFAULT: 100]
*width*: Width of the image [DEFAULT: 100]
...
@@ -490,7 +492,7 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
...
@@ -490,7 +492,7 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
input_element
.
set
(
"src"
,
str
(
src
))
input_element
.
set
(
"src"
,
str
(
src
))
input_element
.
set
(
"width"
,
str
(
width
))
input_element
.
set
(
"width"
,
str
(
width
))
input_element
.
set
(
"height"
,
str
(
height
))
input_element
.
set
(
"height"
,
str
(
height
))
if
rectangle
:
if
rectangle
:
input_element
.
set
(
"rectangle"
,
rectangle
)
input_element
.
set
(
"rectangle"
,
rectangle
)
...
@@ -499,6 +501,7 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
...
@@ -499,6 +501,7 @@ class ImageResponseXMLFactory(ResponseXMLFactory):
return
input_element
return
input_element
class
JavascriptResponseXMLFactory
(
ResponseXMLFactory
):
class
JavascriptResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <javascriptresponse> XML """
""" Factory for producing <javascriptresponse> XML """
...
@@ -522,7 +525,7 @@ class JavascriptResponseXMLFactory(ResponseXMLFactory):
...
@@ -522,7 +525,7 @@ class JavascriptResponseXMLFactory(ResponseXMLFactory):
# Both display_src and display_class given,
# Both display_src and display_class given,
# or neither given
# or neither given
assert
((
display_src
and
display_class
)
or
assert
((
display_src
and
display_class
)
or
(
not
display_src
and
not
display_class
))
(
not
display_src
and
not
display_class
))
# Create the <javascriptresponse> element
# Create the <javascriptresponse> element
...
@@ -552,6 +555,7 @@ class JavascriptResponseXMLFactory(ResponseXMLFactory):
...
@@ -552,6 +555,7 @@ class JavascriptResponseXMLFactory(ResponseXMLFactory):
""" Create the <javascriptinput> element """
""" Create the <javascriptinput> element """
return
etree
.
Element
(
"javascriptinput"
)
return
etree
.
Element
(
"javascriptinput"
)
class
MultipleChoiceResponseXMLFactory
(
ResponseXMLFactory
):
class
MultipleChoiceResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <multiplechoiceresponse> XML """
""" Factory for producing <multiplechoiceresponse> XML """
...
@@ -564,6 +568,7 @@ class MultipleChoiceResponseXMLFactory(ResponseXMLFactory):
...
@@ -564,6 +568,7 @@ class MultipleChoiceResponseXMLFactory(ResponseXMLFactory):
kwargs
[
'choice_type'
]
=
'multiple'
kwargs
[
'choice_type'
]
=
'multiple'
return
ResponseXMLFactory
.
choicegroup_input_xml
(
**
kwargs
)
return
ResponseXMLFactory
.
choicegroup_input_xml
(
**
kwargs
)
class
TrueFalseResponseXMLFactory
(
ResponseXMLFactory
):
class
TrueFalseResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <truefalseresponse> XML """
""" Factory for producing <truefalseresponse> XML """
...
@@ -576,6 +581,7 @@ class TrueFalseResponseXMLFactory(ResponseXMLFactory):
...
@@ -576,6 +581,7 @@ class TrueFalseResponseXMLFactory(ResponseXMLFactory):
kwargs
[
'choice_type'
]
=
'multiple'
kwargs
[
'choice_type'
]
=
'multiple'
return
ResponseXMLFactory
.
choicegroup_input_xml
(
**
kwargs
)
return
ResponseXMLFactory
.
choicegroup_input_xml
(
**
kwargs
)
class
OptionResponseXMLFactory
(
ResponseXMLFactory
):
class
OptionResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for producing <optionresponse> XML"""
""" Factory for producing <optionresponse> XML"""
...
@@ -620,7 +626,7 @@ class StringResponseXMLFactory(ResponseXMLFactory):
...
@@ -620,7 +626,7 @@ class StringResponseXMLFactory(ResponseXMLFactory):
def
create_response_element
(
self
,
**
kwargs
):
def
create_response_element
(
self
,
**
kwargs
):
""" Create a <stringresponse> XML element.
""" Create a <stringresponse> XML element.
Uses **kwargs:
Uses **kwargs:
*answer*: The correct answer (a string) [REQUIRED]
*answer*: The correct answer (a string) [REQUIRED]
...
@@ -642,7 +648,7 @@ class StringResponseXMLFactory(ResponseXMLFactory):
...
@@ -642,7 +648,7 @@ class StringResponseXMLFactory(ResponseXMLFactory):
# Create the <stringresponse> element
# Create the <stringresponse> element
response_element
=
etree
.
Element
(
"stringresponse"
)
response_element
=
etree
.
Element
(
"stringresponse"
)
# Set the answer attribute
# Set the answer attribute
response_element
.
set
(
"answer"
,
str
(
answer
))
response_element
.
set
(
"answer"
,
str
(
answer
))
# Set the case sensitivity
# Set the case sensitivity
...
@@ -667,6 +673,7 @@ class StringResponseXMLFactory(ResponseXMLFactory):
...
@@ -667,6 +673,7 @@ class StringResponseXMLFactory(ResponseXMLFactory):
def
create_input_element
(
self
,
**
kwargs
):
def
create_input_element
(
self
,
**
kwargs
):
return
ResponseXMLFactory
.
textline_input_xml
(
**
kwargs
)
return
ResponseXMLFactory
.
textline_input_xml
(
**
kwargs
)
class
AnnotationResponseXMLFactory
(
ResponseXMLFactory
):
class
AnnotationResponseXMLFactory
(
ResponseXMLFactory
):
""" Factory for creating <annotationresponse> XML trees """
""" Factory for creating <annotationresponse> XML trees """
def
create_response_element
(
self
,
**
kwargs
):
def
create_response_element
(
self
,
**
kwargs
):
...
@@ -679,17 +686,17 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory):
...
@@ -679,17 +686,17 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory):
input_element
=
etree
.
Element
(
"annotationinput"
)
input_element
=
etree
.
Element
(
"annotationinput"
)
text_children
=
[
text_children
=
[
{
'tag'
:
'title'
,
'text'
:
kwargs
.
get
(
'title'
,
'super cool annotation'
)
},
{
'tag'
:
'title'
,
'text'
:
kwargs
.
get
(
'title'
,
'super cool annotation'
)},
{
'tag'
:
'text'
,
'text'
:
kwargs
.
get
(
'text'
,
'texty text'
)
},
{
'tag'
:
'text'
,
'text'
:
kwargs
.
get
(
'text'
,
'texty text'
)},
{
'tag'
:
'comment'
,
'text'
:
kwargs
.
get
(
'comment'
,
'blah blah erudite comment blah blah'
)
},
{
'tag'
:
'comment'
,
'text'
:
kwargs
.
get
(
'comment'
,
'blah blah erudite comment blah blah'
)},
{
'tag'
:
'comment_prompt'
,
'text'
:
kwargs
.
get
(
'comment_prompt'
,
'type a commentary below'
)
},
{
'tag'
:
'comment_prompt'
,
'text'
:
kwargs
.
get
(
'comment_prompt'
,
'type a commentary below'
)},
{
'tag'
:
'tag_prompt'
,
'text'
:
kwargs
.
get
(
'tag_prompt'
,
'select one tag'
)
}
{
'tag'
:
'tag_prompt'
,
'text'
:
kwargs
.
get
(
'tag_prompt'
,
'select one tag'
)}
]
]
for
child
in
text_children
:
for
child
in
text_children
:
etree
.
SubElement
(
input_element
,
child
[
'tag'
])
.
text
=
child
[
'text'
]
etree
.
SubElement
(
input_element
,
child
[
'tag'
])
.
text
=
child
[
'text'
]
default_options
=
[(
'green'
,
'correct'
),(
'eggs'
,
'incorrect'
),(
'ham'
,
'partially-correct'
)]
default_options
=
[(
'green'
,
'correct'
),(
'eggs'
,
'incorrect'
),
(
'ham'
,
'partially-correct'
)]
options
=
kwargs
.
get
(
'options'
,
default_options
)
options
=
kwargs
.
get
(
'options'
,
default_options
)
options_element
=
etree
.
SubElement
(
input_element
,
'options'
)
options_element
=
etree
.
SubElement
(
input_element
,
'options'
)
...
@@ -698,4 +705,3 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory):
...
@@ -698,4 +705,3 @@ class AnnotationResponseXMLFactory(ResponseXMLFactory):
option_element
.
text
=
description
option_element
.
text
=
description
return
input_element
return
input_element
lms/djangoapps/courseware/features/common.py
View file @
a70a23a4
...
@@ -5,6 +5,10 @@ from lettuce.django import django_url
...
@@ -5,6 +5,10 @@ from lettuce.django import django_url
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
terrain.factories
import
CourseFactory
,
ItemFactory
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
_MODULESTORES
,
modulestore
from
xmodule.templates
import
update_templates
import
time
import
time
from
logging
import
getLogger
from
logging
import
getLogger
...
@@ -81,14 +85,57 @@ def i_am_not_logged_in(step):
...
@@ -81,14 +85,57 @@ def i_am_not_logged_in(step):
world
.
browser
.
cookies
.
delete
()
world
.
browser
.
cookies
.
delete
()
@step
(
u'I am registered for a course$'
)
TEST_COURSE_ORG
=
'edx'
def
i_am_registered_for_a_course
(
step
):
TEST_COURSE_NAME
=
'Test Course'
TEST_SECTION_NAME
=
"Problem"
@step
(
u'The course "([^"]*)" exists$'
)
def
create_course
(
step
,
course
):
# First clear the modulestore so we don't try to recreate
# the same course twice
# This also ensures that the necessary templates are loaded
flush_xmodule_store
()
# Create the course
# We always use the same org and display name,
# but vary the course identifier (e.g. 600x or 191x)
course
=
CourseFactory
.
create
(
org
=
TEST_COURSE_ORG
,
number
=
course
,
display_name
=
TEST_COURSE_NAME
)
# Add a section to the course to contain problems
section
=
ItemFactory
.
create
(
parent_location
=
course
.
location
,
display_name
=
TEST_SECTION_NAME
)
problem_section
=
ItemFactory
.
create
(
parent_location
=
section
.
location
,
template
=
'i4x://edx/templates/sequential/Empty'
,
display_name
=
TEST_SECTION_NAME
)
@step
(
u'I am registered for the course "([^"]*)"$'
)
def
i_am_registered_for_the_course
(
step
,
course
):
# Create the course
create_course
(
step
,
course
)
# Create the user
world
.
create_user
(
'robot'
)
world
.
create_user
(
'robot'
)
u
=
User
.
objects
.
get
(
username
=
'robot'
)
u
=
User
.
objects
.
get
(
username
=
'robot'
)
CourseEnrollment
.
objects
.
create
(
user
=
u
,
course_id
=
'MITx/6.002x/2012_Fall'
)
# If the user is not already enrolled, enroll the user.
CourseEnrollment
.
objects
.
get_or_create
(
user
=
u
,
course_id
=
course_id
(
course
))
world
.
log_in
(
'robot@edx.org'
,
'test'
)
world
.
log_in
(
'robot@edx.org'
,
'test'
)
@step
(
u'The course "([^"]*)" has extra tab "([^"]*)"$'
)
def
add_tab_to_course
(
step
,
course
,
extra_tab_name
):
section_item
=
ItemFactory
.
create
(
parent_location
=
course_location
(
course
),
template
=
"i4x://edx/templates/static_tab/Empty"
,
display_name
=
str
(
extra_tab_name
))
@step
(
u'I am an edX user$'
)
@step
(
u'I am an edX user$'
)
def
i_am_an_edx_user
(
step
):
def
i_am_an_edx_user
(
step
):
world
.
create_user
(
'robot'
)
world
.
create_user
(
'robot'
)
...
@@ -97,3 +144,37 @@ def i_am_an_edx_user(step):
...
@@ -97,3 +144,37 @@ def i_am_an_edx_user(step):
@step
(
u'User "([^"]*)" is an edX user$'
)
@step
(
u'User "([^"]*)" is an edX user$'
)
def
registered_edx_user
(
step
,
uname
):
def
registered_edx_user
(
step
,
uname
):
world
.
create_user
(
uname
)
world
.
create_user
(
uname
)
def
flush_xmodule_store
():
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
_MODULESTORES
=
{}
modulestore
()
.
collection
.
drop
()
update_templates
()
def
course_id
(
course_num
):
return
"
%
s/
%
s/
%
s"
%
(
TEST_COURSE_ORG
,
course_num
,
TEST_COURSE_NAME
.
replace
(
" "
,
"_"
))
def
course_location
(
course_num
):
return
Location
(
loc_or_tag
=
"i4x"
,
org
=
TEST_COURSE_ORG
,
course
=
course_num
,
category
=
'course'
,
name
=
TEST_COURSE_NAME
.
replace
(
" "
,
"_"
))
def
section_location
(
course_num
):
return
Location
(
loc_or_tag
=
"i4x"
,
org
=
TEST_COURSE_ORG
,
course
=
course_num
,
category
=
'sequential'
,
name
=
TEST_SECTION_NAME
.
replace
(
" "
,
"_"
))
lms/djangoapps/courseware/features/courses.py
View file @
a70a23a4
...
@@ -9,6 +9,7 @@ logger = getLogger(__name__)
...
@@ -9,6 +9,7 @@ logger = getLogger(__name__)
## support functions
## support functions
def
get_courses
():
def
get_courses
():
'''
'''
Returns dict of lists of courses available, keyed by course.org (ie university).
Returns dict of lists of courses available, keyed by course.org (ie university).
...
...
lms/djangoapps/courseware/features/courseware.feature
deleted
100644 → 0
View file @
c50bf567
Feature
:
View the Courseware Tab
As a student in an edX course
In order to work on the course
I want to view the info on the courseware tab
Scenario
:
I
can get to the courseware tab when logged in
Given
I am registered for a course
And
I log in
And
I click on View Courseware
When
I click on the
"Courseware"
tab
Then
the
"Courseware"
tab is active
lms/djangoapps/courseware/features/high-level-tabs.feature
View file @
a70a23a4
...
@@ -3,21 +3,18 @@ Feature: All the high level tabs should work
...
@@ -3,21 +3,18 @@ Feature: All the high level tabs should work
As a student
As a student
I want to navigate through the high level tabs
I want to navigate through the high level tabs
# Note this didn't work as a scenario outline because
Scenario
:
I
can navigate to all high -level tabs in a course
# before each scenario was not flushing the database
Given
:
I
am registered for the course
"6.002x"
# TODO: break this apart so that if one fails the others
And
The course
"6.002x"
has extra tab
"Custom Tab"
# will still run
And
I log in
Scenario
:
A
student can see all tabs of the course
And
I click on View Courseware
Given
I am registered for a course
When
I click on the
"<TabName>"
tab
And
I log in
Then
the page title should contain
"<PageTitle>"
And
I click on View Courseware
When
I click on the
"Courseware"
tab
Examples
:
Then
the page title should be
"6.002x Courseware"
|
TabName
|
PageTitle
|
When
I click on the
"Course Info"
tab
|
Courseware
|
6.002x
Courseware
|
Then
the page title should be
"6.002x Course Info"
|
Course
Info
|
6.002x
Course
Info
|
When
I click on the
"Textbook"
tab
|
Custom
Tab
|
6.002x
Custom
Tab
|
Then
the page title should be
"6.002x Textbook"
|
Wiki
|
edX
Wiki
|
When
I click on the
"Wiki"
tab
|
Progress
|
6.002x
Progress
|
Then
the page title should be
"6.002x | edX Wiki"
When
I click on the
"Progress"
tab
Then
the page title should be
"6.002x Progress"
lms/djangoapps/courseware/features/homepage.feature
View file @
a70a23a4
...
@@ -39,9 +39,9 @@ Feature: Homepage for web users
...
@@ -39,9 +39,9 @@ Feature: Homepage for web users
|
MITx
|
|
MITx
|
|
HarvardX
|
|
HarvardX
|
|
BerkeleyX
|
|
BerkeleyX
|
|
UTx
|
|
UTx
|
|
WellesleyX
|
|
WellesleyX
|
|
GeorgetownX
|
|
GeorgetownX
|
# # TODO: Add scenario that tests the courses available
# # TODO: Add scenario that tests the courses available
# # using a policy or a configuration file
# # using a policy or a configuration file
lms/djangoapps/courseware/features/login.py
View file @
a70a23a4
...
@@ -34,6 +34,7 @@ def click_the_dropdown(step):
...
@@ -34,6 +34,7 @@ def click_the_dropdown(step):
#### helper functions
#### helper functions
def
user_is_an_unactivated_user
(
uname
):
def
user_is_an_unactivated_user
(
uname
):
u
=
User
.
objects
.
get
(
username
=
uname
)
u
=
User
.
objects
.
get
(
username
=
uname
)
u
.
is_active
=
False
u
.
is_active
=
False
...
...
lms/djangoapps/courseware/features/openended.feature
View file @
a70a23a4
...
@@ -3,10 +3,10 @@ Feature: Open ended grading
...
@@ -3,10 +3,10 @@ Feature: Open ended grading
In order to complete the courseware questions
In order to complete the courseware questions
I want the machine learning grading to be functional
I want the machine learning grading to be functional
# Commenting these all out right now until we can
# Commenting these all out right now until we can
# make a reference implementation for a course with
# make a reference implementation for a course with
# an open ended grading problem that is always available
# an open ended grading problem that is always available
#
#
# Scenario: An answer that is too short is rejected
# Scenario: An answer that is too short is rejected
# Given I navigate to an openended question
# Given I navigate to an openended question
# And I enter the answer "z"
# And I enter the answer "z"
...
...
lms/djangoapps/courseware/features/problems.feature
0 → 100644
View file @
a70a23a4
Feature
:
Answer choice problems
As a student in an edX course
In order to test my understanding of the material
I want to answer choice based problems
Scenario
:
I
can answer a problem correctly
Given
I am viewing a
"<ProblemType>"
problem
When
I answer a
"<ProblemType>"
problem
"correctly"
Then
My
"<ProblemType>"
answer is marked
"correct"
Examples
:
|
ProblemType
|
|
drop
down
|
|
multiple
choice
|
|
checkbox
|
|
string
|
|
numerical
|
|
formula
|
|
script
|
Scenario
:
I
can answer a problem incorrectly
Given
I am viewing a
"<ProblemType>"
problem
When
I answer a
"<ProblemType>"
problem
"incorrectly"
Then
My
"<ProblemType>"
answer is marked
"incorrect"
Examples
:
|
ProblemType
|
|
drop
down
|
|
multiple
choice
|
|
checkbox
|
|
string
|
|
numerical
|
|
formula
|
|
script
|
Scenario
:
I
can submit a blank answer
Given
I am viewing a
"<ProblemType>"
problem
When
I check a problem
Then
My
"<ProblemType>"
answer is marked
"incorrect"
Examples
:
|
ProblemType
|
|
drop
down
|
|
multiple
choice
|
|
checkbox
|
|
string
|
|
numerical
|
|
formula
|
|
script
|
Scenario
:
I
can reset a problem
Given
I am viewing a
"<ProblemType>"
problem
And
I answer a
"<ProblemType>"
problem
"<Correctness>ly"
When
I reset the problem
Then
My
"<ProblemType>"
answer is marked
"unanswered"
Examples
:
|
ProblemType
|
Correctness
|
|
drop
down
|
correct
|
|
drop
down
|
incorrect
|
|
multiple
choice
|
correct
|
|
multiple
choice
|
incorrect
|
|
checkbox
|
correct
|
|
checkbox
|
incorrect
|
|
string
|
correct
|
|
string
|
incorrect
|
|
numerical
|
correct
|
|
numerical
|
incorrect
|
|
formula
|
correct
|
|
formula
|
incorrect
|
|
script
|
correct
|
|
script
|
incorrect
|
lms/djangoapps/courseware/features/problems.py
0 → 100644
View file @
a70a23a4
from
lettuce
import
world
,
step
from
lettuce.django
import
django_url
from
selenium.webdriver.support.ui
import
Select
import
random
import
textwrap
from
common
import
i_am_registered_for_the_course
,
TEST_SECTION_NAME
,
section_location
from
terrain.factories
import
ItemFactory
from
capa.tests.response_xml_factory
import
OptionResponseXMLFactory
,
\
ChoiceResponseXMLFactory
,
MultipleChoiceResponseXMLFactory
,
\
StringResponseXMLFactory
,
NumericalResponseXMLFactory
,
\
FormulaResponseXMLFactory
,
CustomResponseXMLFactory
# Factories from capa.tests.response_xml_factory that we will use
# to generate the problem XML, with the keyword args used to configure
# the output.
PROBLEM_FACTORY_DICT
=
{
'drop down'
:
{
'factory'
:
OptionResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'The correct answer is Option 2'
,
'options'
:
[
'Option 1'
,
'Option 2'
,
'Option 3'
,
'Option 4'
],
'correct_option'
:
'Option 2'
}},
'multiple choice'
:
{
'factory'
:
MultipleChoiceResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'The correct answer is Choice 3'
,
'choices'
:
[
False
,
False
,
True
,
False
],
'choice_names'
:
[
'choice_1'
,
'choice_2'
,
'choice_3'
,
'choice_4'
]}},
'checkbox'
:
{
'factory'
:
ChoiceResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'The correct answer is Choices 1 and 3'
,
'choice_type'
:
'checkbox'
,
'choices'
:
[
True
,
False
,
True
,
False
,
False
],
'choice_names'
:
[
'Choice 1'
,
'Choice 2'
,
'Choice 3'
,
'Choice 4'
]}},
'string'
:
{
'factory'
:
StringResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'The answer is "correct string"'
,
'case_sensitive'
:
False
,
'answer'
:
'correct string'
}},
'numerical'
:
{
'factory'
:
NumericalResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'The answer is pi + 1'
,
'answer'
:
'4.14159'
,
'tolerance'
:
'0.00001'
,
'math_display'
:
True
}},
'formula'
:
{
'factory'
:
FormulaResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'The solution is [mathjax]x^2+2x+y[/mathjax]'
,
'sample_dict'
:
{
'x'
:
(
-
100
,
100
),
'y'
:
(
-
100
,
100
)},
'num_samples'
:
10
,
'tolerance'
:
0.00001
,
'math_display'
:
True
,
'answer'
:
'x^2+2*x+y'
}},
'script'
:
{
'factory'
:
CustomResponseXMLFactory
(),
'kwargs'
:
{
'question_text'
:
'Enter two integers that sum to 10.'
,
'cfn'
:
'test_add_to_ten'
,
'expect'
:
'10'
,
'num_inputs'
:
2
,
'script'
:
textwrap
.
dedent
(
"""
def test_add_to_ten(expect,ans):
try:
a1=int(ans[0])
a2=int(ans[1])
except ValueError:
a1=0
a2=0
return (a1+a2)==int(expect)
"""
)}},
}
def
add_problem_to_course
(
course
,
problem_type
):
assert
(
problem_type
in
PROBLEM_FACTORY_DICT
)
# Generate the problem XML using capa.tests.response_xml_factory
factory_dict
=
PROBLEM_FACTORY_DICT
[
problem_type
]
problem_xml
=
factory_dict
[
'factory'
]
.
build_xml
(
**
factory_dict
[
'kwargs'
])
# Create a problem item using our generated XML
# We set rerandomize=always in the metadata so that the "Reset" button
# will appear.
problem_item
=
ItemFactory
.
create
(
parent_location
=
section_location
(
course
),
template
=
"i4x://edx/templates/problem/Blank_Common_Problem"
,
display_name
=
str
(
problem_type
),
data
=
problem_xml
,
metadata
=
{
'rerandomize'
:
'always'
})
@step
(
u'I am viewing a "([^"]*)" problem'
)
def
view_problem
(
step
,
problem_type
):
i_am_registered_for_the_course
(
step
,
'model_course'
)
# Ensure that the course has this problem type
add_problem_to_course
(
'model_course'
,
problem_type
)
# Go to the one section in the factory-created course
# which should be loaded with the correct problem
chapter_name
=
TEST_SECTION_NAME
.
replace
(
" "
,
"_"
)
section_name
=
chapter_name
url
=
django_url
(
'/courses/edx/model_course/Test_Course/courseware/
%
s/
%
s'
%
(
chapter_name
,
section_name
))
world
.
browser
.
visit
(
url
)
@step
(
u'I answer a "([^"]*)" problem "([^"]*)ly"'
)
def
answer_problem
(
step
,
problem_type
,
correctness
):
""" Mark a given problem type correct or incorrect, then submit it.
*problem_type* is a string representing the type of problem (e.g. 'drop down')
*correctness* is in ['correct', 'incorrect']
"""
assert
(
correctness
in
[
'correct'
,
'incorrect'
])
if
problem_type
==
"drop down"
:
select_name
=
"input_i4x-edx-model_course-problem-drop_down_2_1"
option_text
=
'Option 2'
if
correctness
==
'correct'
else
'Option 3'
world
.
browser
.
select
(
select_name
,
option_text
)
elif
problem_type
==
"multiple choice"
:
if
correctness
==
'correct'
:
inputfield
(
'multiple choice'
,
choice
=
'choice_3'
)
.
check
()
else
:
inputfield
(
'multiple choice'
,
choice
=
'choice_2'
)
.
check
()
elif
problem_type
==
"checkbox"
:
if
correctness
==
'correct'
:
inputfield
(
'checkbox'
,
choice
=
'choice_0'
)
.
check
()
inputfield
(
'checkbox'
,
choice
=
'choice_2'
)
.
check
()
else
:
inputfield
(
'checkbox'
,
choice
=
'choice_3'
)
.
check
()
elif
problem_type
==
'string'
:
textvalue
=
'correct string'
if
correctness
==
'correct'
else
'incorrect'
inputfield
(
'string'
)
.
fill
(
textvalue
)
elif
problem_type
==
'numerical'
:
textvalue
=
"pi + 1"
if
correctness
==
'correct'
else
str
(
random
.
randint
(
-
2
,
2
))
inputfield
(
'numerical'
)
.
fill
(
textvalue
)
elif
problem_type
==
'formula'
:
textvalue
=
"x^2+2*x+y"
if
correctness
==
'correct'
else
'x^2'
inputfield
(
'formula'
)
.
fill
(
textvalue
)
elif
problem_type
==
'script'
:
# Correct answer is any two integers that sum to 10
first_addend
=
random
.
randint
(
-
100
,
100
)
second_addend
=
10
-
first_addend
# If we want an incorrect answer, then change
# the second addend so they no longer sum to 10
if
correctness
==
'incorrect'
:
second_addend
+=
random
.
randint
(
1
,
10
)
inputfield
(
'script'
,
input_num
=
1
)
.
fill
(
str
(
first_addend
))
inputfield
(
'script'
,
input_num
=
2
)
.
fill
(
str
(
second_addend
))
# Submit the problem
check_problem
(
step
)
@step
(
u'I check a problem'
)
def
check_problem
(
step
):
world
.
browser
.
find_by_css
(
"input.check"
)
.
click
()
@step
(
u'I reset the problem'
)
def
reset_problem
(
step
):
world
.
browser
.
find_by_css
(
'input.reset'
)
.
click
()
@step
(
u'My "([^"]*)" answer is marked "([^"]*)"'
)
def
assert_answer_mark
(
step
,
problem_type
,
correctness
):
""" Assert that the expected answer mark is visible for a given problem type.
*problem_type* is a string identifying the type of problem (e.g. 'drop down')
*correctness* is in ['correct', 'incorrect', 'unanswered']
Asserting that a problem is marked 'unanswered' means that
the problem is NOT marked correct and NOT marked incorrect.
This can occur, for example, if the user has reset the problem. """
# Dictionaries that map problem types to the css selectors
# for correct/incorrect marks.
# The elements are lists of selectors because a particular problem type
# might be marked in multiple ways.
# For example, multiple choice is marked incorrect differently
# depending on whether the user selects an incorrect
# item or submits without selecting any item)
correct_selectors
=
{
'drop down'
:
[
'span.correct'
],
'multiple choice'
:
[
'label.choicegroup_correct'
],
'checkbox'
:
[
'span.correct'
],
'string'
:
[
'div.correct'
],
'numerical'
:
[
'div.correct'
],
'formula'
:
[
'div.correct'
],
'script'
:
[
'div.correct'
],
}
incorrect_selectors
=
{
'drop down'
:
[
'span.incorrect'
],
'multiple choice'
:
[
'label.choicegroup_incorrect'
,
'span.incorrect'
],
'checkbox'
:
[
'span.incorrect'
],
'string'
:
[
'div.incorrect'
],
'numerical'
:
[
'div.incorrect'
],
'formula'
:
[
'div.incorrect'
],
'script'
:
[
'div.incorrect'
]}
assert
(
correctness
in
[
'correct'
,
'incorrect'
,
'unanswered'
])
assert
(
problem_type
in
correct_selectors
and
problem_type
in
incorrect_selectors
)
# Assert that the question has the expected mark
# (either correct or incorrect)
if
correctness
in
[
"correct"
,
"incorrect"
]:
selector_dict
=
correct_selectors
if
correctness
==
"correct"
else
incorrect_selectors
# At least one of the correct selectors should be present
for
sel
in
selector_dict
[
problem_type
]:
has_expected_mark
=
world
.
browser
.
is_element_present_by_css
(
sel
,
wait_time
=
4
)
# As soon as we find the selector, break out of the loop
if
has_expected_mark
:
break
# Expect that we found the right mark (correct or incorrect)
assert
(
has_expected_mark
)
# Assert that the question has neither correct nor incorrect
# because it is unanswered (possibly reset)
else
:
# Get all the correct/incorrect selectors for this problem type
selector_list
=
correct_selectors
[
problem_type
]
+
incorrect_selectors
[
problem_type
]
# Assert that none of the correct/incorrect selectors are present
for
sel
in
selector_list
:
assert
(
world
.
browser
.
is_element_not_present_by_css
(
sel
,
wait_time
=
4
))
def
inputfield
(
problem_type
,
choice
=
None
,
input_num
=
1
):
""" Return the <input> element for *problem_type*.
For example, if problem_type is 'string', return
the text field for the string problem in the test course.
*choice* is the name of the checkbox input in a group
of checkboxes. """
sel
=
(
"input#input_i4x-edx-model_course-problem-
%
s_2_
%
s"
%
(
problem_type
.
replace
(
" "
,
"_"
),
str
(
input_num
)))
if
choice
is
not
None
:
base
=
"_choice_"
if
problem_type
==
"multiple choice"
else
"_"
sel
=
sel
+
base
+
str
(
choice
)
# If the input element doesn't exist, fail immediately
assert
(
world
.
browser
.
is_element_present_by_css
(
sel
,
wait_time
=
4
))
# Retrieve the input element
return
world
.
browser
.
find_by_css
(
sel
)
lms/djangoapps/courseware/features/registration.feature
View file @
a70a23a4
...
@@ -4,13 +4,14 @@ Feature: Register for a course
...
@@ -4,13 +4,14 @@ Feature: Register for a course
I want to register for a class on the edX website
I want to register for a class on the edX website
Scenario
:
I
can register for a course
Scenario
:
I
can register for a course
Given
I am logged in
Given
The course
"6.002x"
exists
And
I am logged in
And
I visit the courses page
And
I visit the courses page
When
I register for the course numbered
"6.002x"
When
I register for the course
"6.002x"
Then
I should see the course numbered
"6.002x"
in my dashboard
Then
I should see the course numbered
"6.002x"
in my dashboard
Scenario
:
I
can unregister for a course
Scenario
:
I
can unregister for a course
Given
I am registered for
a course
Given
I am registered for
the course
"6.002x"
And
I visit the dashboard
And
I visit the dashboard
When
I click the link with the text
"Unregister"
When
I click the link with the text
"Unregister"
And
I press the
"Unregister"
button in the Unenroll dialog
And
I press the
"Unregister"
button in the Unenroll dialog
...
...
lms/djangoapps/courseware/features/registration.py
View file @
a70a23a4
from
lettuce
import
world
,
step
from
lettuce
import
world
,
step
from
lettuce.django
import
django_url
from
common
import
TEST_COURSE_ORG
,
TEST_COURSE_NAME
@step
(
'I register for the course
numbered
"([^"]*)"$'
)
@step
(
'I register for the course "([^"]*)"$'
)
def
i_register_for_the_course
(
step
,
course
):
def
i_register_for_the_course
(
step
,
course
):
courses_section
=
world
.
browser
.
find_by_css
(
'section.courses'
)
cleaned_name
=
TEST_COURSE_NAME
.
replace
(
' '
,
'_'
)
course_link_css
=
'article[id*="
%
s"] > div'
%
course
url
=
django_url
(
'courses/
%
s/
%
s/
%
s/about'
%
(
TEST_COURSE_ORG
,
course
,
cleaned_name
))
course_link
=
courses_section
.
find_by_css
(
course_link_css
)
.
first
world
.
browser
.
visit
(
url
)
course_link
.
click
()
intro_section
=
world
.
browser
.
find_by_css
(
'section.intro'
)
intro_section
=
world
.
browser
.
find_by_css
(
'section.intro'
)
register_link
=
intro_section
.
find_by_css
(
'a.register'
)
register_link
=
intro_section
.
find_by_css
(
'a.register'
)
...
...
lms/djangoapps/courseware/features/smart-accordion.feature
View file @
a70a23a4
...
@@ -60,4 +60,4 @@ Feature: There are courses on the homepage
...
@@ -60,4 +60,4 @@ Feature: There are courses on the homepage
# Scenario: Navigate through course BerkeleyX/CS184.1x/2012_Fall
# Scenario: Navigate through course BerkeleyX/CS184.1x/2012_Fall
# Given I am registered for course "BerkeleyX/CS184.1x/2012_Fall"
# Given I am registered for course "BerkeleyX/CS184.1x/2012_Fall"
# And I log in
# And I log in
# Then I verify all the content of each course
# Then I verify all the content of each course
\ No newline at end of file
lms/envs/acceptance.py
View file @
a70a23a4
...
@@ -8,16 +8,24 @@ from .test import *
...
@@ -8,16 +8,24 @@ from .test import *
# otherwise the browser will not render the pages correctly
# otherwise the browser will not render the pages correctly
DEBUG
=
True
DEBUG
=
True
# Show the courses that are in the data directory
# Use the mongo store for acceptance tests
COURSES_ROOT
=
ENV_ROOT
/
"data"
modulestore_options
=
{
DATA_DIR
=
COURSES_ROOT
'default_class'
:
'xmodule.raw_module.RawDescriptor'
,
'host'
:
'localhost'
,
'db'
:
'test_xmodule'
,
'collection'
:
'modulestore'
,
'fs_root'
:
GITHUB_REPO_ROOT
,
'render_template'
:
'mitxmako.shortcuts.render_to_string'
,
}
MODULESTORE
=
{
MODULESTORE
=
{
'default'
:
{
'default'
:
{
'ENGINE'
:
'xmodule.modulestore.xml.XMLModuleStore'
,
'ENGINE'
:
'xmodule.modulestore.mongo.MongoModuleStore'
,
'OPTIONS'
:
{
'OPTIONS'
:
modulestore_options
'data_dir'
:
DATA_DIR
,
},
'default_class'
:
'xmodule.hidden_module.HiddenDescriptor'
,
'direct'
:
{
}
'ENGINE'
:
'xmodule.modulestore.mongo.MongoModuleStore'
,
'OPTIONS'
:
modulestore_options
}
}
}
}
...
...
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