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
dbfc38df
Commit
dbfc38df
authored
Oct 16, 2013
by
Jay Zoldak
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1370 from edx/zoldak/refactor-cms-acceptance-js
refactor studio component creation in acceptance tests
parents
4d7606f0
33f6d3b8
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
234 additions
and
268 deletions
+234
-268
cms/djangoapps/contentstore/features/common.py
+48
-12
cms/djangoapps/contentstore/features/component.feature
+0
-6
cms/djangoapps/contentstore/features/component.py
+11
-76
cms/djangoapps/contentstore/features/component_settings_editor_helpers.py
+63
-34
cms/djangoapps/contentstore/features/course-overview.feature
+0
-17
cms/djangoapps/contentstore/features/course-team.py
+4
-4
cms/djangoapps/contentstore/features/course_import.py
+1
-0
cms/djangoapps/contentstore/features/discussion-editor.feature
+1
-5
cms/djangoapps/contentstore/features/discussion-editor.py
+3
-13
cms/djangoapps/contentstore/features/grading.py
+1
-1
cms/djangoapps/contentstore/features/html-editor.py
+9
-8
cms/djangoapps/contentstore/features/problem-editor.py
+11
-14
cms/djangoapps/contentstore/features/section.py
+0
-7
cms/djangoapps/contentstore/features/textbooks.py
+3
-3
cms/djangoapps/contentstore/features/upload.py
+1
-1
cms/djangoapps/contentstore/features/video.py
+3
-5
common/djangoapps/terrain/steps.py
+4
-22
common/djangoapps/terrain/ui_helpers.py
+71
-40
No files found.
cms/djangoapps/contentstore/features/common.py
View file @
dbfc38df
...
...
@@ -2,7 +2,7 @@
# pylint: disable=W0621
from
lettuce
import
world
,
step
from
nose.tools
import
assert_true
,
assert_
equal
,
assert_
in
,
assert_false
# pylint: disable=E0611
from
nose.tools
import
assert_true
,
assert_in
,
assert_false
# pylint: disable=E0611
from
auth.authz
import
get_user_by_email
,
get_course_groupname_for_role
from
django.conf
import
settings
...
...
@@ -224,14 +224,50 @@ def i_enabled_the_advanced_module(step, module):
press_the_notification_button
(
step
,
'Save'
)
@step
(
'I have clicked the new unit button'
)
def
open_new_unit
(
step
):
step
.
given
(
'I have opened a new course section in Studio'
)
step
.
given
(
'I have added a new subsection'
)
step
.
given
(
'I expand the first section'
)
old_url
=
world
.
browser
.
url
world
.
css_click
(
'a.new-unit-item'
)
world
.
wait_for
(
lambda
x
:
world
.
browser
.
url
!=
old_url
)
@world.absorb
def
create_course_with_unit
():
"""
Prepare for tests by creating a course with a section, subsection, and unit.
Performs the following:
Clear out all courseware
Create a course with a section, subsection, and unit
Create a user and make that user a course author
Log the user into studio
Open the course from the dashboard
Expand the section and click on the New Unit link
The end result is the page where the user is editing the new unit
"""
world
.
clear_courses
()
course
=
world
.
CourseFactory
.
create
()
world
.
scenario_dict
[
'COURSE'
]
=
course
section
=
world
.
ItemFactory
.
create
(
parent_location
=
course
.
location
)
world
.
ItemFactory
.
create
(
parent_location
=
section
.
location
,
category
=
'sequential'
,
display_name
=
'Subsection One'
,
)
user
=
create_studio_user
(
is_staff
=
False
)
add_course_author
(
user
,
course
)
log_into_studio
()
world
.
css_click
(
'a.course-link'
)
css_selectors
=
[
'div.section-item a.expand-collapse-icon'
,
'a.new-unit-item'
]
for
selector
in
css_selectors
:
world
.
css_click
(
selector
)
world
.
wait_for_mathjax
()
world
.
wait_for_xmodule
()
assert
world
.
is_css_present
(
'ul.new-component-type'
)
@step
(
'I have clicked the new unit button$'
)
@step
(
u'I am in Studio editing a new unit$'
)
def
edit_new_unit
(
step
):
create_course_with_unit
()
@step
(
'the save notification button is disabled'
)
...
...
@@ -267,9 +303,9 @@ def confirm_the_prompt(step):
assert_false
(
world
.
css_find
(
btn_css
)
.
visible
)
@step
(
u'I am shown a
(.*)
$'
)
def
i_am_shown_a_notification
(
step
,
notification_type
):
assert
world
.
is_css_present
(
'.wrapper-
%
s'
%
notification_type
)
@step
(
u'I am shown a
prompt
$'
)
def
i_am_shown_a_notification
(
step
):
assert
world
.
is_css_present
(
'.wrapper-
prompt'
)
def
type_in_codemirror
(
index
,
text
):
...
...
cms/djangoapps/contentstore/features/component.feature
View file @
dbfc38df
...
...
@@ -80,9 +80,3 @@ Feature: CMS.Component Adding
And
I add a
"Blank Advanced Problem"
"Advanced Problem"
component
And
I delete all components
Then
I see no components
Scenario
:
I
see a notification on save
Given
I am in Studio editing a new unit
And
I add a
"Discussion"
"single step"
component
And
I edit and save a component
Then
I am shown a notification
cms/djangoapps/contentstore/features/component.py
View file @
dbfc38df
...
...
@@ -2,52 +2,19 @@
#pylint: disable=W0621
from
lettuce
import
world
,
step
from
nose.tools
import
assert_true
,
assert_in
,
assert_equal
# pylint: disable=E0611
from
common
import
create_studio_user
,
add_course_author
,
log_into_studio
@step
(
u'I am in Studio editing a new unit$'
)
def
add_unit
(
step
):
world
.
clear_courses
()
course
=
world
.
CourseFactory
.
create
()
section
=
world
.
ItemFactory
.
create
(
parent_location
=
course
.
location
)
world
.
ItemFactory
.
create
(
parent_location
=
section
.
location
,
category
=
'sequential'
,
display_name
=
'Subsection One'
,)
user
=
create_studio_user
(
is_staff
=
False
)
add_course_author
(
user
,
course
)
log_into_studio
()
world
.
wait_for_requirejs
([
"jquery"
,
"gettext"
,
"js/models/course"
,
"coffee/src/models/module"
,
"coffee/src/views/unit"
,
"jquery.ui"
,
])
world
.
wait_for_mathjax
()
css_selectors
=
[
'a.course-link'
,
'div.section-item a.expand-collapse-icon'
,
'a.new-unit-item'
,
]
for
selector
in
css_selectors
:
world
.
css_click
(
selector
)
from
nose.tools
import
assert_true
,
assert_in
# pylint: disable=E0611
@step
(
u'I add this type of single step component:$'
)
def
add_a_single_step_component
(
step
):
world
.
wait_for_xmodule
()
for
step_hash
in
step
.
hashes
:
component
=
step_hash
[
'Component'
]
assert_in
(
component
,
[
'Discussion'
,
'Video'
])
css_selector
=
'a[data-type="{}"]'
.
format
(
component
.
lower
())
world
.
css_click
(
css_selector
)
# In the current implementation, all the "new component"
# buttons are handled by one BackBone.js view.
# If we click two buttons at super-human speed,
# the view will miss the second click while it's
# processing the first.
# To account for this, we wait for each component
# to be created before clicking the next component.
world
.
wait_for_visible
(
'section.xmodule_{}Module'
.
format
(
component
))
world
.
create_component_instance
(
step
=
step
,
category
=
'{}'
.
format
(
component
.
lower
()),
)
@step
(
u'I see this type of single step component:$'
)
...
...
@@ -62,45 +29,13 @@ def see_a_single_step_component(step):
@step
(
u'I add this type of( Advanced)? (HTML|Problem) component:$'
)
def
add_a_multi_step_component
(
step
,
is_advanced
,
category
):
def
click_advanced
():
css
=
'ul.problem-type-tabs a[href="#tab2"]'
world
.
css_click
(
css
)
my_css
=
'ul.problem-type-tabs li.ui-state-active a[href="#tab2"]'
assert
(
world
.
css_find
(
my_css
))
def
find_matching_link
():
"""
Find the link with the specified text. There should be one and only one.
"""
# The tab shows links for the given category
links
=
world
.
css_find
(
'div.new-component-{} a'
.
format
(
category
))
# Find the link whose text matches what you're looking for
matched_links
=
[
link
for
link
in
links
if
link
.
text
==
step_hash
[
'Component'
]]
# There should be one and only one
assert_equal
(
len
(
matched_links
),
1
)
return
matched_links
[
0
]
def
click_link
():
link
.
click
()
world
.
wait_for_xmodule
()
category
=
category
.
lower
()
for
step_hash
in
step
.
hashes
:
css_selector
=
'a[data-type="{}"]'
.
format
(
category
)
world
.
css_click
(
css_selector
)
world
.
wait_for_invisible
(
css_selector
)
if
is_advanced
:
# Sometimes this click does not work if you go too fast.
world
.
retry_on_exception
(
click_advanced
,
max_attempts
=
5
,
ignored_exceptions
=
AssertionError
)
# Retry this in case the list is empty because you tried too fast.
link
=
world
.
retry_on_exception
(
func
=
find_matching_link
,
ignored_exceptions
=
AssertionError
)
# Wait for the link to be clickable. If you go too fast it is not.
world
.
retry_on_exception
(
click_link
)
world
.
create_component_instance
(
step
=
step
,
category
=
'{}'
.
format
(
category
.
lower
()),
component_type
=
step_hash
[
'Component'
],
is_advanced
=
bool
(
is_advanced
),
)
@step
(
u'I see (HTML|Problem) components in this order:'
)
...
...
cms/djangoapps/contentstore/features/component_settings_editor_helpers.py
View file @
dbfc38df
...
...
@@ -2,30 +2,35 @@
#pylint: disable=C0111
from
lettuce
import
world
from
nose.tools
import
assert_equal
,
assert_true
# pylint: disable=E0611
from
nose.tools
import
assert_equal
,
assert_true
,
assert_in
# pylint: disable=E0611
from
terrain.steps
import
reload_the_page
@world.absorb
def
create_component_instance
(
step
,
component_button_css
,
category
,
expected_css
,
boilerplate
=
None
,
has_multiple_templates
=
True
):
click_new_component_button
(
step
,
component_button_css
)
if
category
in
(
'problem'
,
'html'
):
def
create_component_instance
(
step
,
category
,
component_type
=
None
,
is_advanced
=
False
):
"""
Create a new component in a Unit.
def
animation_done
(
_driver
):
script
=
"$('div.new-component').css('display')"
return
world
.
browser
.
evaluate_script
(
script
)
==
'none'
Parameters
----------
category: component type (discussion, html, problem, video)
component_type: for components with multiple templates, the link text in the menu
is_advanced: for html and problem, is the desired component under the
advanced menu
"""
assert_in
(
category
,
[
'problem'
,
'html'
,
'video'
,
'discussion'
])
world
.
wait_for
(
animation_done
)
component_button_css
=
'.large-{}-icon'
.
format
(
category
.
lower
())
world
.
css_click
(
component_button_css
)
if
has_multiple_templates
:
click_component_from_menu
(
category
,
boilerplate
,
expected_css
)
if
category
in
(
'problem'
,
'html'
):
world
.
wait_for_invisible
(
component_button_css
)
click_component_from_menu
(
category
,
component_type
,
is_advanced
)
if
category
in
(
'video'
,):
world
.
wait_for_xmodule
()
if
category
==
'problem'
:
expected_css
=
'section.xmodule_CapaModule'
else
:
expected_css
=
'section.xmodule_{}Module'
.
format
(
category
.
title
())
assert_true
(
world
.
is_css_present
(
expected_css
))
...
...
@@ -33,29 +38,53 @@ def create_component_instance(step, component_button_css, category,
@world.absorb
def
click_new_component_button
(
step
,
component_button_css
):
step
.
given
(
'I have clicked the new unit button'
)
world
.
wait_for_requirejs
(
[
"jquery"
,
"js/models/course"
,
"coffee/src/models/module"
,
"coffee/src/views/unit"
,
"jquery.ui"
,
"domReady!"
]
)
world
.
css_click
(
component_button_css
)
@world.absorb
def
click_component_from_menu
(
category
,
boilerplate
,
expected_css
):
def
_click_advanced
():
css
=
'ul.problem-type-tabs a[href="#tab2"]'
world
.
css_click
(
css
)
my_css
=
'ul.problem-type-tabs li.ui-state-active a[href="#tab2"]'
assert
(
world
.
css_find
(
my_css
))
def
_find_matching_link
(
category
,
component_type
):
"""
Creates a component from `instance_id`. For components with more
than one template, clicks on `elem_css` to create the new
component. Components with only one template are created as soon
as the user clicks the appropriate button, so we assert that the
expected component is present.
Find the link with the specified text. There should be one and only one.
"""
if
boilerplate
:
elem_css
=
"a[data-category='{}'][data-boilerplate='{}']"
.
format
(
category
,
boilerplate
)
else
:
elem_css
=
"a[data-category='{}']:not([data-boilerplate])"
.
format
(
category
)
elements
=
world
.
css_find
(
elem_css
)
assert_equal
(
len
(
elements
),
1
)
world
.
css_click
(
elem_css
)
# The tab shows links for the given category
links
=
world
.
css_find
(
'div.new-component-{} a'
.
format
(
category
))
# Find the link whose text matches what you're looking for
matched_links
=
[
link
for
link
in
links
if
link
.
text
==
component_type
]
# There should be one and only one
assert_equal
(
len
(
matched_links
),
1
)
return
matched_links
[
0
]
def
click_component_from_menu
(
category
,
component_type
,
is_advanced
):
"""
Creates a component for a category with more
than one template, i.e. HTML and Problem.
For some problem types, it is necessary to click to
the Advanced tab.
The component_type is the link text, e.g. "Blank Common Problem"
"""
if
is_advanced
:
# Sometimes this click does not work if you go too fast.
world
.
retry_on_exception
(
_click_advanced
,
ignored_exceptions
=
AssertionError
)
# Retry this in case the list is empty because you tried too fast.
link
=
world
.
retry_on_exception
(
lambda
:
_find_matching_link
(
category
,
component_type
),
ignored_exceptions
=
AssertionError
)
# Wait for the link to be clickable. If you go too fast it is not.
world
.
retry_on_exception
(
lambda
:
link
.
click
())
@world.absorb
...
...
cms/djangoapps/contentstore/features/course-overview.feature
View file @
dbfc38df
...
...
@@ -58,20 +58,3 @@ Feature: CMS.Course Overview
And
I click the
"Expand All Sections"
link
Then
I see the
"Collapse All Sections"
link
And
all sections are expanded
Scenario
:
Notification is shown on grading status changes
Given
I have a course with 1 section
When
I navigate to the course overview page
And
I change an assignment's grading status
Then
I am shown a notification
# Notification is not shown on reorder for IE
# Safari does not have moveMouseTo implemented
@skip_internetexplorer
@skip_safari
Scenario
:
Notification is shown on subsection reorder
Given
I have opened a new course section in Studio
And
I have added a new subsection
And
I have added a new subsection
When
I reorder subsections
Then
I am shown a notification
cms/djangoapps/contentstore/features/course-team.py
View file @
dbfc38df
...
...
@@ -50,8 +50,8 @@ def other_delete_self(_step):
@step
(
u'I make "([^"]*)" a course team admin'
)
def
make_course_team_admin
(
_step
,
name
):
admin_btn_css
=
'.user-item[data-email="{
email}
"] .user-actions .add-admin-role'
.
format
(
email
=
name
+
'@edx.org'
)
admin_btn_css
=
'.user-item[data-email="{
name}@edx.org
"] .user-actions .add-admin-role'
.
format
(
name
=
name
)
world
.
css_click
(
admin_btn_css
)
...
...
@@ -80,8 +80,8 @@ def see_course(_step, do_not_see, gender='self'):
@step
(
u'"([^"]*)" should( not)? be marked as an admin'
)
def
marked_as_admin
(
_step
,
name
,
not_marked_admin
):
flag_css
=
'.user-item[data-email="{
email}
"] .flag-role.flag-role-admin'
.
format
(
email
=
name
+
'@edx.org'
)
flag_css
=
'.user-item[data-email="{
name}@edx.org
"] .flag-role.flag-role-admin'
.
format
(
name
=
name
)
if
not_marked_admin
:
assert
world
.
is_css_not_present
(
flag_css
)
else
:
...
...
cms/djangoapps/contentstore/features/course_import.py
View file @
dbfc38df
...
...
@@ -2,6 +2,7 @@ import os
from
lettuce
import
world
from
django.conf
import
settings
def
import_file
(
filename
):
world
.
browser
.
execute_script
(
"$('input.file-input').css('display', 'block')"
)
path
=
os
.
path
.
join
(
settings
.
COMMON_TEST_DATA_ROOT
,
"imports"
,
filename
)
...
...
cms/djangoapps/contentstore/features/discussion-editor.feature
View file @
dbfc38df
...
...
@@ -2,7 +2,7 @@
Feature
:
CMS.Discussion Component Editor
As a course author, I want to be able to create discussion components.
Scenario
:
User can view metadata
Scenario
:
User can view
discussion component
metadata
Given
I have created a Discussion Tag
And
I edit and select Settings
Then
I see three alphabetized settings and their expected values
...
...
@@ -14,7 +14,3 @@ Feature: CMS.Discussion Component Editor
And
I edit and select Settings
Then
I can modify the display name
And
my display name change is persisted on save
Scenario
:
Creating a discussion takes a single click
Given
I have clicked the new unit button
Then
creating a discussion takes a single click
cms/djangoapps/contentstore/features/discussion-editor.py
View file @
dbfc38df
...
...
@@ -6,11 +6,10 @@ from lettuce import world, step
@step
(
'I have created a Discussion Tag$'
)
def
i_created_discussion_tag
(
step
):
world
.
create_course_with_unit
()
world
.
create_component_instance
(
step
,
'.large-discussion-icon'
,
'discussion'
,
'.xmodule_DiscussionModule'
,
has_multiple_templates
=
False
step
=
step
,
category
=
'discussion'
,
)
...
...
@@ -22,12 +21,3 @@ def i_see_only_the_settings_and_values(step):
[
'Display Name'
,
"Discussion"
,
False
],
[
'Subcategory'
,
"Topic-Level Student-Visible Label"
,
False
]
])
@step
(
'creating a discussion takes a single click'
)
def
discussion_takes_a_single_click
(
step
):
component_css
=
'.xmodule_DiscussionModule'
assert
world
.
is_css_not_present
(
component_css
)
world
.
css_click
(
"a[data-category='discussion']"
)
assert
world
.
is_css_present
(
component_css
)
cms/djangoapps/contentstore/features/grading.py
View file @
dbfc38df
...
...
@@ -180,7 +180,7 @@ def cannot_edit_fail(_step):
def
i_change_grace_period
(
_step
,
grace_period
):
grace_period_css
=
'#course-grading-graceperiod'
ele
=
world
.
css_find
(
grace_period_css
)
.
first
# Sometimes it takes a moment for the JavaScript
# to populate the field. If we don't wait for
# this to happen, then we can end up with
...
...
cms/djangoapps/contentstore/features/html-editor.py
View file @
dbfc38df
...
...
@@ -6,9 +6,11 @@ from lettuce import world, step
@step
(
'I have created a Blank HTML Page$'
)
def
i_created_blank_html_page
(
step
):
world
.
create_course_with_unit
()
world
.
create_component_instance
(
step
,
'.large-html-icon'
,
'html'
,
'.xmodule_HtmlModule'
step
=
step
,
category
=
'html'
,
component_type
=
'Text'
)
...
...
@@ -18,11 +20,10 @@ def i_see_only_the_html_display_name(step):
@step
(
'I have created an E-text Written in LaTeX$'
)
def
i_created_blank_html_page
(
step
):
def
i_created_etext_in_latex
(
step
):
world
.
create_course_with_unit
()
world
.
create_component_instance
(
step
,
'.large-html-icon'
,
'html'
,
'.xmodule_HtmlModule'
,
'latex_html.yaml'
step
=
step
,
category
=
'html'
,
component_type
=
'E-text Written in LaTeX'
)
cms/djangoapps/contentstore/features/problem-editor.py
View file @
dbfc38df
# disable missing docstring
#pylint: disable=C0111
import
os
import
json
from
lettuce
import
world
,
step
from
nose.tools
import
assert_equal
,
assert_true
# pylint: disable=E0611
...
...
@@ -18,12 +17,11 @@ SHOW_ANSWER = "Show Answer"
@step
(
'I have created a Blank Common Problem$'
)
def
i_created_blank_common_problem
(
step
):
world
.
create_course_with_unit
()
world
.
create_component_instance
(
step
,
'.large-problem-icon'
,
'problem'
,
'.xmodule_CapaModule'
,
'blank_common.yaml'
step
=
step
,
category
=
'problem'
,
component_type
=
'Blank Common Problem'
)
...
...
@@ -168,14 +166,13 @@ def cancel_does_not_save_changes(step):
@step
(
'I have created a LaTeX Problem'
)
def
create_latex_problem
(
step
):
world
.
click_new_component_button
(
step
,
'.large-problem-icon'
)
def
animation_done
(
_driver
):
return
world
.
browser
.
evaluate_script
(
"$('div.new-component').css('display')"
)
==
'none'
world
.
wait_for
(
animation_done
)
# Go to advanced tab.
world
.
css_click
(
'#ui-id-2'
)
world
.
click_component_from_menu
(
"problem"
,
"latex_problem.yaml"
,
'.xmodule_CapaModule'
)
world
.
create_course_with_unit
()
world
.
create_component_instance
(
step
=
step
,
category
=
'problem'
,
component_type
=
'Problem Written in LaTeX'
,
is_advanced
=
True
)
@step
(
'I edit and compile the High Level Source'
)
...
...
cms/djangoapps/contentstore/features/section.py
View file @
dbfc38df
...
...
@@ -5,8 +5,6 @@ from lettuce import world, step
from
common
import
*
from
nose.tools
import
assert_equal
# pylint: disable=E0611
############### ACTIONS ####################
@step
(
'I click the New Section link$'
)
def
i_click_new_section_link
(
_step
):
...
...
@@ -53,9 +51,6 @@ def i_see_a_mini_notification(_step, _type):
assert
world
.
is_css_present
(
saving_css
)
############ ASSERTIONS ###################
@step
(
'I see my section on the Courseware page$'
)
def
i_see_my_section_on_the_courseware_page
(
_step
):
see_my_section_on_the_courseware_page
(
'My Section'
)
...
...
@@ -125,8 +120,6 @@ def the_section_release_date_is_updated(_step):
assert_equal
(
status_text
,
'Will Release: 12/25/2013 at 00:00 UTC'
)
############ HELPER METHODS ###################
def
save_section_name
(
name
):
name_css
=
'.new-section-name'
save_css
=
'.new-section-name-save'
...
...
cms/djangoapps/contentstore/features/textbooks.py
View file @
dbfc38df
...
...
@@ -47,7 +47,7 @@ def name_textbook(_step, name):
@step
(
u'I name the (first|second|third) chapter "([^"]*)"'
)
def
name_chapter
(
_step
,
ordinal
,
name
):
index
=
[
"first"
,
"second"
,
"third"
]
.
index
(
ordinal
)
input_css
=
".textbook .chapter{i} input.chapter-name"
.
format
(
i
=
index
+
1
)
input_css
=
".textbook .chapter{i} input.chapter-name"
.
format
(
i
=
index
+
1
)
world
.
css_fill
(
input_css
,
name
)
if
world
.
is_firefox
():
world
.
trigger_event
(
input_css
)
...
...
@@ -56,7 +56,7 @@ def name_chapter(_step, ordinal, name):
@step
(
u'I type in "([^"]*)" for the (first|second|third) chapter asset'
)
def
asset_chapter
(
_step
,
name
,
ordinal
):
index
=
[
"first"
,
"second"
,
"third"
]
.
index
(
ordinal
)
input_css
=
".textbook .chapter{i} input.chapter-asset-path"
.
format
(
i
=
index
+
1
)
input_css
=
".textbook .chapter{i} input.chapter-asset-path"
.
format
(
i
=
index
+
1
)
world
.
css_fill
(
input_css
,
name
)
if
world
.
is_firefox
():
world
.
trigger_event
(
input_css
)
...
...
@@ -65,7 +65,7 @@ def asset_chapter(_step, name, ordinal):
@step
(
u'I click the Upload Asset link for the (first|second|third) chapter'
)
def
click_upload_asset
(
_step
,
ordinal
):
index
=
[
"first"
,
"second"
,
"third"
]
.
index
(
ordinal
)
button_css
=
".textbook .chapter{i} .action-upload"
.
format
(
i
=
index
+
1
)
button_css
=
".textbook .chapter{i} .action-upload"
.
format
(
i
=
index
+
1
)
world
.
css_click
(
button_css
)
...
...
cms/djangoapps/contentstore/features/upload.py
View file @
dbfc38df
...
...
@@ -191,7 +191,7 @@ def view_asset(_step, status):
# Note that world.visit would trigger a 403 error instead of displaying "Unauthorized"
# Instead, we can drop back into the selenium driver get command.
world
.
browser
.
driver
.
get
(
url
)
assert_equal
(
world
.
css_text
(
'body'
),
expected_text
)
assert_equal
(
world
.
css_text
(
'body'
),
expected_text
)
@step
(
'I see a confirmation that the file was deleted$'
)
...
...
cms/djangoapps/contentstore/features/video.py
View file @
dbfc38df
...
...
@@ -12,11 +12,10 @@ BUTTONS = {
@step
(
'I have created a Video component$'
)
def
i_created_a_video_component
(
step
):
world
.
create_course_with_unit
()
world
.
create_component_instance
(
step
,
'.large-video-icon'
,
'video'
,
'.xmodule_VideoModule'
,
has_multiple_templates
=
False
step
=
step
,
category
=
'video'
,
)
...
...
@@ -155,4 +154,3 @@ def check_captions_visibility_state(_step, visibility_state, timeout):
assert
world
.
css_visible
(
'.subtitles'
)
else
:
assert
not
world
.
css_visible
(
'.subtitles'
)
common/djangoapps/terrain/steps.py
View file @
dbfc38df
...
...
@@ -11,7 +11,6 @@
# Disable the "unused argument" warning because lettuce uses "step"
#pylint: disable=W0613
import
re
from
lettuce
import
world
,
step
from
.course_helpers
import
*
from
.ui_helpers
import
*
...
...
@@ -23,32 +22,15 @@ logger = getLogger(__name__)
@step
(
r'I wait (?:for )?"(\d+\.?\d*)" seconds?$'
)
def
wait
(
step
,
seconds
):
def
wait
_for_seconds
(
step
,
seconds
):
world
.
wait
(
seconds
)
REQUIREJS_WAIT
=
{
re
.
compile
(
'settings-details'
):
[
"jquery"
,
"js/models/course"
,
"js/models/settings/course_details"
,
"js/views/settings/main"
],
re
.
compile
(
'settings-advanced'
):
[
"jquery"
,
"js/models/course"
,
"js/models/settings/advanced"
,
"js/views/settings/advanced"
,
"codemirror"
],
re
.
compile
(
'edit
\
/.+vertical'
):
[
"jquery"
,
"js/models/course"
,
"coffee/src/models/module"
,
"coffee/src/views/unit"
,
"jquery.ui"
],
}
@step
(
'I reload the page$'
)
def
reload_the_page
(
step
):
world
.
wait_for_ajax_complete
()
world
.
browser
.
reload
()
requirements
=
None
for
test
,
req
in
REQUIREJS_WAIT
.
items
():
if
test
.
search
(
world
.
browser
.
url
):
requirements
=
req
break
world
.
wait_for_requirejs
(
requirements
)
world
.
wait_for_js_to_load
()
@step
(
'I press the browser back button$'
)
...
...
@@ -163,9 +145,9 @@ def should_see_in_the_page(step, doesnt_appear, text):
else
:
multiplier
=
1
if
doesnt_appear
:
assert
world
.
browser
.
is_text_not_present
(
text
,
wait_time
=
5
*
multiplier
)
assert
world
.
browser
.
is_text_not_present
(
text
,
wait_time
=
5
*
multiplier
)
else
:
assert
world
.
browser
.
is_text_present
(
text
,
wait_time
=
5
*
multiplier
)
assert
world
.
browser
.
is_text_present
(
text
,
wait_time
=
5
*
multiplier
)
@step
(
'I am logged in$'
)
...
...
common/djangoapps/terrain/ui_helpers.py
View file @
dbfc38df
...
...
@@ -4,11 +4,13 @@
from
lettuce
import
world
import
time
import
json
import
re
import
platform
from
textwrap
import
dedent
from
urllib
import
quote_plus
from
selenium.common.exceptions
import
(
WebDriverException
,
TimeoutException
,
StaleElementReferenceException
)
WebDriverException
,
TimeoutException
,
StaleElementReferenceException
)
from
selenium.webdriver.support
import
expected_conditions
as
EC
from
selenium.webdriver.common.by
import
By
from
selenium.webdriver.support.ui
import
WebDriverWait
...
...
@@ -16,11 +18,50 @@ from lettuce.django import django_url
from
nose.tools
import
assert_true
# pylint: disable=E0611
REQUIREJS_WAIT
=
{
# Settings - Schedule & Details
re
.
compile
(
'^Schedule & Details Settings
\
|'
):
[
"jquery"
,
"js/models/course"
,
"js/models/settings/course_details"
,
"js/views/settings/main"
],
# Settings - Advanced Settings
re
.
compile
(
'^Advanced Settings
\
|'
):
[
"jquery"
,
"js/models/course"
,
"js/models/settings/advanced"
,
"js/views/settings/advanced"
,
"codemirror"
],
# Individual Unit (editing)
re
.
compile
(
'^Individual Unit
\
|'
):
[
"coffee/src/models/module"
,
"coffee/src/views/unit"
,
"coffee/src/views/module_edit"
],
# Content - Outline
# Note that calling your org, course number, or display name, 'course' will mess this up
re
.
compile
(
'^Course Outline
\
|'
):
[
"js/models/course"
,
"js/models/location"
,
"js/models/section"
,
"js/views/overview"
,
"js/views/section_edit"
],
# Dashboard
re
.
compile
(
'^My Courses
\
|'
):
[
"js/sock"
,
"gettext"
,
"js/base"
,
"jquery.ui"
,
"coffee/src/main"
,
"underscore"
],
}
@world.absorb
def
wait
(
seconds
):
time
.
sleep
(
float
(
seconds
))
@world.absorb
def
wait_for_js_to_load
():
requirements
=
None
for
test
,
req
in
REQUIREJS_WAIT
.
items
():
if
test
.
search
(
world
.
browser
.
title
):
requirements
=
req
break
world
.
wait_for_requirejs
(
requirements
)
# Selenium's `execute_async_script` function pauses Selenium's execution
# until the browser calls a specific Javascript callback; in effect,
# Selenium goes to sleep until the JS callback function wakes it back up again.
...
...
@@ -28,8 +69,6 @@ def wait(seconds):
# passed to this callback get returned from the `execute_async_script`
# function, which allows the JS to communicate information back to Python.
# Ref: https://selenium.googlecode.com/svn/trunk/docs/api/dotnet/html/M_OpenQA_Selenium_IJavaScriptExecutor_ExecuteAsyncScript.htm
@world.absorb
def
wait_for_js_variable_truthy
(
variable
):
"""
...
...
@@ -37,7 +76,7 @@ def wait_for_js_variable_truthy(variable):
environment until the given variable is defined and truthy. This process
guards against page reloads, and seamlessly retries on the next page.
"""
j
s
=
"""
j
avascript
=
"""
var callback = arguments[arguments.length - 1];
var unloadHandler = function() {{
callback("unload");
...
...
@@ -56,7 +95,13 @@ def wait_for_js_variable_truthy(variable):
}}, 10);
"""
.
format
(
variable
=
variable
)
for
_
in
range
(
5
):
# 5 attempts max
result
=
world
.
browser
.
driver
.
execute_async_script
(
dedent
(
js
))
try
:
result
=
world
.
browser
.
driver
.
execute_async_script
(
dedent
(
javascript
))
except
WebDriverException
as
wde
:
if
"document unloaded while waiting for result"
in
wde
.
msg
:
result
=
"unload"
else
:
raise
if
result
==
"unload"
:
# we ran this on the wrong page. Wait a bit, and try again, when the
# browser has loaded the next page.
...
...
@@ -105,7 +150,7 @@ def wait_for_requirejs(dependencies=None):
if
dependencies
[
0
]
!=
"jquery"
:
dependencies
.
insert
(
0
,
"jquery"
)
j
s
=
"""
j
avascript
=
"""
var callback = arguments[arguments.length - 1];
if(window.require) {{
requirejs.onError = callback;
...
...
@@ -126,7 +171,13 @@ def wait_for_requirejs(dependencies=None):
}}
"""
.
format
(
deps
=
json
.
dumps
(
dependencies
))
for
_
in
range
(
5
):
# 5 attempts max
result
=
world
.
browser
.
driver
.
execute_async_script
(
dedent
(
js
))
try
:
result
=
world
.
browser
.
driver
.
execute_async_script
(
dedent
(
javascript
))
except
WebDriverException
as
wde
:
if
"document unloaded while waiting for result"
in
wde
.
msg
:
result
=
"unload"
else
:
raise
if
result
==
"unload"
:
# we ran this on the wrong page. Wait a bit, and try again, when the
# browser has loaded the next page.
...
...
@@ -161,7 +212,7 @@ def wait_for_ajax_complete():
keeps track of this information, go here:
http://stackoverflow.com/questions/3148225/jquery-active-function#3148506
"""
j
s
=
"""
j
avascript
=
"""
var callback = arguments[arguments.length - 1];
if(!window.jQuery) {callback(false);}
var intervalID = setInterval(function() {
...
...
@@ -171,13 +222,13 @@ def wait_for_ajax_complete():
}
}, 100);
"""
world
.
browser
.
driver
.
execute_async_script
(
dedent
(
j
s
))
world
.
browser
.
driver
.
execute_async_script
(
dedent
(
j
avascript
))
@world.absorb
def
visit
(
url
):
world
.
browser
.
visit
(
django_url
(
url
))
wait_for_
requirejs
()
wait_for_
js_to_load
()
@world.absorb
...
...
@@ -246,11 +297,11 @@ def css_has_value(css_selector, value, index=0):
@world.absorb
def
wait_for
(
func
,
timeout
=
5
):
WebDriverWait
(
driver
=
world
.
browser
.
driver
,
timeout
=
timeout
,
ignored_exceptions
=
(
StaleElementReferenceException
)
)
.
until
(
func
)
WebDriverWait
(
driver
=
world
.
browser
.
driver
,
timeout
=
timeout
,
ignored_exceptions
=
(
StaleElementReferenceException
)
)
.
until
(
func
)
@world.absorb
...
...
@@ -349,14 +400,10 @@ def css_click(css_selector, index=0, wait_time=30):
msg
=
"Element {}[{}] is present but not visible"
.
format
(
css_selector
,
index
)
)
# Sometimes you can't click in the center of the element, as
# another element might be on top of it. In this case, try
# clicking in the upper left corner.
try
:
return
retry_on_exception
(
lambda
:
world
.
css_find
(
css_selector
)[
index
]
.
click
())
except
WebDriverException
:
return
css_click_at
(
css_selector
,
index
=
index
)
result
=
retry_on_exception
(
lambda
:
world
.
css_find
(
css_selector
)[
index
]
.
click
())
if
result
:
wait_for_js_to_load
()
return
result
@world.absorb
...
...
@@ -372,23 +419,6 @@ def css_check(css_selector, index=0, wait_time=30):
@world.absorb
def
css_click_at
(
css_selector
,
index
=
0
,
x_coord
=
10
,
y_coord
=
10
,
timeout
=
5
):
'''
A method to click at x,y coordinates of the element
rather than in the center of the element
'''
wait_for_clickable
(
css_selector
,
timeout
=
timeout
)
assert_true
(
world
.
css_visible
(
css_selector
,
index
=
index
),
msg
=
"Element {}[{}] is present but not visible"
.
format
(
css_selector
,
index
)
)
element
.
action_chains
.
move_to_element_with_offset
(
element
.
_element
,
x_coord
,
y_coord
)
element
.
action_chains
.
click
()
element
.
action_chains
.
perform
()
@world.absorb
def
select_option
(
name
,
value
,
index
=
0
,
wait_time
=
30
):
'''
A method to select an option
...
...
@@ -417,6 +447,7 @@ def css_fill(css_selector, text, index=0):
@world.absorb
def
click_link
(
partial_text
,
index
=
0
):
retry_on_exception
(
lambda
:
world
.
browser
.
find_link_by_partial_text
(
partial_text
)[
index
]
.
click
())
wait_for_js_to_load
()
@world.absorb
...
...
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