Commit 668bc81d by Jay Zoldak

New branch for clean lms acceptance tests

parent 8bf393fb
Feature: There are many different types of tabs
In order to validate tab types
As a staff member
I want to try out all the videos, buttons, and content
Scenario: I visit a tabbed quiz
Given I am registered for course "MITx/6.002x-EE98/2012_Fall_SJSU"
And I log in
Given I visit and check 502 for "http://www.edx.org/courses/MITx/6.002x-EE98/2012_Fall_SJSU/courseware/Week_0/Administrivia_and_Circuit_Elements/"
I process
from lettuce import * #before, world
from selenium import *
#import lettuce_webdriver.webdriver
import logging
import nose.tools
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
import re
## imported from lms/djangoapps/courseware/courses.py
from collections import defaultdict
from fs.errors import ResourceNotFoundError
from functools import wraps
import logging
from path import path
from django.conf import settings
from django.http import Http404
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from static_replace import replace_urls, try_staticfiles_lookup
from courseware.access import has_access
## end import
from django.core.urlresolvers import reverse
from courseware.courses import course_image_url, get_course_about_section, get_course_by_id
from courses import *
import os.path
import sys
path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'static'))
if not path in sys.path:
sys.path.insert(1, path)
del path
#from helpers import *
@step(u'I visit and check 502 for "(.*)"')
def i_visit_and_check_502_for_url(step, url):
world.browser.get(url)
check_for_502(url)
@step(u'I process')
def i_make_sure_everything_is_there(step):
e = world.browser.find_element_by_css_selector('section.course-content section')
process_section(e)
def process_section(element, num_tabs=0):
'''
Process section reads through whatever is in 'course-content' and classifies it according to sequence module type.
This function is recursive
There are 5 types, with 5 actions.
Sequence Module
-contains one child module
-to prevent from over-processing all its children (no easy way to specify only one level of depth), we only grab the first child
Vertical Module
-contains other modules
-process it and get its children, then process them
Capa Module
-problem type, contains only one problem
-for this, the most complex type, we created a separate method, process_problem
Video Module
-video type, contains only one video
-we only check to ensure that a section with class of video exists
Custom Tag Module
-a custom 'hack' module type
-there is a large variety of content that could go in a custom tag module, so we just pass if it is of this unusual type
'''
tab_type = element.get_attribute('class')
print 'processing a %s' % (tab_type)
if tab_type == "xmodule_display xmodule_SequenceModule":
child_modules = element.find_elements_by_css_selector("section[class^='xmodule']")
## ugly bit of code to get around not being able to specify only the first level of descendants
if child_modules[0].get_attribute('class') == "xmodule_display xmodule_VerticalModule":
process_section(child_modules[0])
else:
for mod in child_modules:
process_section(mod)
elif tab_type == "xmodule_display xmodule_VerticalModule":
vert_list = element.find_elements_by_css_selector("li section[class^='xmodule']")
print "I found %s items" % (str(len(vert_list)))
for item in vert_list:
print 'processing a child %s' % (item.get_attribute('class'))
process_section(item)
elif tab_type == "xmodule_display xmodule_CapaModule":
assert element.find_element_by_css_selector("section[id^='problem']") , "No problems found in %s" % (tab_type)
p = element.find_element_by_css_selector("section[id^='problem']")
p_id = p.get_attribute('id')
process_problem(p, p_id)
elif tab_type == "xmodule_display xmodule_VideoModule":
assert element.find_element_by_css_selector("section[class^='video']") , 'No video found in %s' % (tab_type)
elif tab_type == "xmodule_display xmodule_CustomTagModule":
pass
else:
assert False, "%s not recognized!!" % (tab_type)
def process_problem(element, problem_id):
'''
Process problem attempts to
1) scan all the input fields and reset them
2) click the 'check' button and look for an incorrect response (p.status text should be 'incorrect')
3) click the 'show answer' button IF it exists and IF the answer is not already displayed
4) enter the correct answer in each input box
5) click the 'check' button and verify that answers are correct
Because of all the ajax calls happening, sometimes the test fails because objects disconnect from the DOM.
The basic functionality does exist, though, and I'm hoping that someone can take it over and make it super effective.
'''
prob_xmod = element.find_element_by_css_selector("section.problem")
input_fields = prob_xmod.find_elements_by_css_selector("section[id^='textinput']")
## clear out all input to ensure an incorrect result
for field in input_fields:
box = field.find_element_by_css_selector("input")
box.clear()
print "\n I cleared out the box %s \n" % (box.get_attribute('id'))
## because of cookies or the application, only click the 'check' button if the status is not already 'incorrect'
if prob_xmod.find_element_by_css_selector("p.status").text.lower() != 'incorrect':
prob_xmod.find_element_by_css_selector("section.action input.check").click()
world.browser.implicitly_wait(4)
## all elements become disconnected after the click
element = world.browser.find_element_by_css_selector("section[id='"+problem_id+"']")
prob_xmod = element.find_element_by_css_selector("section.problem")
input_fields = prob_xmod.find_elements_by_css_selector("section[id^='textinput']")
for field in input_fields:
assert field.find_element_by_css_selector("div.incorrect") , "The 'check' button did not work for %s" % (problem_id)
print "\n So far so good! \n"
## wait for the ajax changes to render
world.browser.implicitly_wait(4)
## grab element and prob_xmod because the dom has changed (some classes/elements became hidden and changed the hierarchy)
element = world.browser.find_element_by_css_selector("section[id='"+problem_id+"']")
prob_xmod = element.find_element_by_css_selector("section.problem")
show_button = element.find_element_by_css_selector("section.action input.show")
## this logic is to ensure we do not accidentally hide the answers
if show_button.get_attribute('value').lower() == 'show answer':
show_button.click()
print "\n I clicked show for %s \n" % (problem_id)
else:
pass
## wait for the ajax changes to render
world.browser.implicitly_wait(4)
## grab element and prob_xmod because the dom has changed (some classes/elements became hidden and changed the hierarchy)
element = world.browser.find_element_by_css_selector("section[id='"+problem_id+"']")
prob_xmod = element.find_element_by_css_selector("section.problem")
## find all the input fields
input_fields = prob_xmod.find_elements_by_css_selector("section[id^='textinput']")
## in each field, find the answer, and send it to the field.
## Note that this does not work if the answer type is a strange format, e.g. "either a or b"
for field in input_fields:
field.find_element_by_css_selector("input").send_keys(field.find_element_by_css_selector("p[id^='answer']").text)
print "\n \n Entered %s into %s \n \n" % (field.find_element_by_css_selector("p[id^='answer']").text,field.find_element_by_css_selector("input").get_attribute('id') )
prob_xmod = element.find_element_by_css_selector("section.problem")
prob_xmod.find_element_by_css_selector("section.action input.check").click()
world.browser.implicitly_wait(4)
## assert that we entered the correct answers
## we have to redefine input-fields because apparently they become detached from the dom after clicking 'check'
input_fields = world.browser.find_elements_by_css_selector("section[id='"+problem_id+"'] section[id^='textinput']")
for field in input_fields:
## if you don't use 'starts with ^=' the test will fail because the actual class is 'correct ' (with a space)
assert world.browser.find_element_by_css_selector("div[class^='correct']"), "The check answer values were not correct for %s" % (problem_id)
inputs = world.browser.find_elements_by_css_selector("section[id^='textinput'] input")
for el in inputs:
el.clear()
print "\n checked answers for %s \n" % (problem_id)
Feature: View the Course Info tab
As a student in an edX course
In order to get background on the course
I want to view the info on the course info tab
Scenario: I can get to the course info tab when logged in
Given I am logged in
And I am registered for a course
And I visit the dashboard
When I click on View Courseware
Then I am on an info page
And the Course Info tab is active
And I do not see "! Info section missing !" anywhere on the page
# This test is currently failing
# see: https://www.pivotaltracker.com/projects/614553?classic=true#!/stories/38801223
Scenario: I cannot get to the course info tab when not logged in
Given I am not logged in
And I visit the homepage
When I visit the course info URL
Then the login dialog is visible
\ No newline at end of file
from lettuce import world, step
from lettuce.django import django_url
#from portal.common import *
@step('I am on an info page')
def i_am_on_an_info_page(step):
title = world.browser.title
url = world.browser.url
assert ('Course Info' in title)
assert (r'/info' in url)
@step('I visit the course info URL$')
def i_visit_the_course_info_url(step):
url = django_url('/courses/MITx/6.002x/2012_Fall/info')
world.browser.visit(url)
from lettuce import * #before, world
from selenium import *
#import lettuce_webdriver.webdriver
import logging
import nose.tools
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
## imported from lms/djangoapps/courseware/courses.py
from collections import defaultdict
from fs.errors import ResourceNotFoundError
from functools import wraps
import logging
from path import path
from django.conf import settings
from django.http import Http404
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from static_replace import replace_urls, try_staticfiles_lookup
from courseware.access import has_access
## end import
from django.core.urlresolvers import reverse
from courseware.courses import course_image_url, get_course_about_section, get_course_by_id
import xmodule
## support functions
def get_courses():
'''
Returns dict of lists of courses available, keyed by course.org (ie university).
Courses are sorted by course.number.
'''
courses = [c for c in modulestore().get_courses()
if isinstance(c, CourseDescriptor)]
courses = sorted(courses, key=lambda course: course.number)
return courses
def get_courseware(course_id):
"""
Given a course_id (string), return a courseware array of dictionaries for the
top two levels of navigation. Example:
[
{'chapter_name': 'Overview',
'sections': ['Welcome', 'System Usage Sequence', 'Lab0: Using the tools', 'Circuit Sandbox']
},
{'chapter_name': 'Week 1',
'sections': ['Administrivia and Circuit Elements', 'Basic Circuit Analysis', 'Resistor Divider', 'Week 1 Tutorials']
},
{'chapter_name': 'Midterm Exam',
'sections': ['Midterm Exam']
}
]
"""
course = get_course_by_id(course_id)
chapters = course.get_children()
courseware = [ {'chapter_name':c.display_name, 'sections':[s.display_name for s in c.get_children()]} for c in chapters]
return courseware
def get_courseware_with_tabs(course_id):
"""
Given a course_id (string), return a courseware array of dictionaries for the
top three levels of navigation. Same as get_courseware() except include
the tabs on the right hand main navigation page.
This hides the appropriate courseware as defined by the XML flag test:
chapter.metadata.get('hide_from_toc','false').lower() == 'true'
Example:
[{
'chapter_name': 'Overview',
'sections': [{
'clickable_tab_count': 0,
'section_name': 'Welcome',
'tab_classes': []
}, {
'clickable_tab_count': 1,
'section_name': 'System Usage Sequence',
'tab_classes': ['VerticalDescriptor']
}, {
'clickable_tab_count': 0,
'section_name': 'Lab0: Using the tools',
'tab_classes': ['HtmlDescriptor', 'HtmlDescriptor', 'CapaDescriptor']
}, {
'clickable_tab_count': 0,
'section_name': 'Circuit Sandbox',
'tab_classes': []
}]
}, {
'chapter_name': 'Week 1',
'sections': [{
'clickable_tab_count': 4,
'section_name': 'Administrivia and Circuit Elements',
'tab_classes': ['VerticalDescriptor', 'VerticalDescriptor', 'VerticalDescriptor', 'VerticalDescriptor']
}, {
'clickable_tab_count': 0,
'section_name': 'Basic Circuit Analysis',
'tab_classes': ['CapaDescriptor', 'CapaDescriptor', 'CapaDescriptor']
}, {
'clickable_tab_count': 0,
'section_name': 'Resistor Divider',
'tab_classes': []
}, {
'clickable_tab_count': 0,
'section_name': 'Week 1 Tutorials',
'tab_classes': []
}]
}, {
'chapter_name': 'Midterm Exam',
'sections': [{
'clickable_tab_count': 2,
'section_name': 'Midterm Exam',
'tab_classes': ['VerticalDescriptor', 'VerticalDescriptor']
}]
}]
"""
course = get_course_by_id(course_id)
chapters = [ chapter for chapter in course.get_children() if chapter.metadata.get('hide_from_toc','false').lower() != 'true' ]
courseware = [{'chapter_name':c.display_name, 'sections':[{'section_name':s.display_name, 'clickable_tab_count': len(s.get_children()) if (type(s)==xmodule.seq_module.SequenceDescriptor) else 0, 'tab_classes':[t.__class__.__name__ for t in s.get_children() ]} for s in c.get_children() if s.metadata.get('hide_from_toc', 'false').lower() != 'true']} for c in chapters ]
return courseware
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
# TODO: fix this one? Not sure whether you should get a 404.
# Scenario: I cannot get to the courseware tab when not logged in
# Given I am not logged in
# And I visit the homepage
# When I visit the courseware URL
# Then the login dialog is visible
\ No newline at end of file
from lettuce import world, step
from lettuce.django import django_url
@step('I click on View Courseware')
def i_click_on_view_courseware(step):
css = 'p.enter-course'
world.browser.find_by_css(css).first.click()
@step('I click on the "([^"]*)" tab$')
def i_click_on_the_tab(step, tab):
world.browser.find_link_by_text(tab).first.click()
@step('I visit the courseware URL$')
def i_visit_the_course_info_url(step):
url = django_url('/courses/MITx/6.002x/2012_Fall/courseware')
world.browser.visit(url)
@step(u'I do not see "([^"]*)" anywhere on the page')
def i_do_not_see_text_anywhere_on_the_page(step, text):
assert world.browser.is_text_not_present(text)
@step(u'I am on the dashboard page$')
def i_am_on_the_dashboard_page(step):
assert world.browser.is_element_present_by_css('section.courses')
assert world.browser.url == django_url('/dashboard')
@step('the "([^"]*)" tab is active$')
def the_tab_is_active(step, tab):
css = '.course-tabs a.active'
active_tab = world.browser.find_by_css(css)
assert (active_tab.text == tab)
@step('the login dialog is visible$')
def login_dialog_visible(step):
css = 'form#login_form.login_form'
assert world.browser.find_by_css(css).visible
Feature: All the high level tabs should work
In order to preview the courseware
As a student
I want to navigate through the high level tabs
# Note this didn't work as a scenario outline because
# before each scenario was not flushing the database
# TODO: break this apart so that if one fails the others
# will still run
Scenario: A student can see all tabs of the course
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 page title should be "6.002x Courseware"
When I click on the "Course Info" tab
Then the page title should be "6.002x Course Info"
When I click on the "Textbook" tab
Then the page title should be "6.002x Textbook"
When I click on the "Wiki" tab
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"
Feature: There are courses on the homepage
In order to compared rendered content to the database
As an acceptance test
I want to count all the chapters, sections, and tabs for each course
Scenario: Navigate through course MITx/6.002x/2012_Fall
Given I am registered for course "MITx/6.002x/2012_Fall"
And I log in
Then I verify all the content of each course
Scenario: Navigate through course edX/edx101/edX_Studio_Reference
Given I am registered for course "edX/edx101/edX_Studio_Reference"
And I log in
Then I verify all the content of each course
Scenario: Navigate through course BerkeleyX/CS169.1x/2012_Fall
Given I am registered for course "BerkeleyX/CS169.1x/2012_Fall"
And I log in
Then I verify all the content of each course
\ No newline at end of file
from lettuce import world, step
import re
from nose.tools import assert_equals
## imported from lms/djangoapps/courseware/courses.py
from collections import defaultdict
from fs.errors import ResourceNotFoundError
from functools import wraps
from path import path
from django.conf import settings
from django.http import Http404
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from static_replace import replace_urls, try_staticfiles_lookup
from courseware.access import has_access
## end import
from django.core.urlresolvers import reverse
from courseware.courses import course_image_url, get_course_about_section, get_course_by_id
from courses import *
import os.path
import sys
path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'static'))
if not path in sys.path:
sys.path.insert(1, path)
del path
#from helpers import *
from logging import getLogger
logger = getLogger(__name__)
def check_for_errors():
e = world.browser.find_by_css('.outside-app')
if len(e) > 0:
assert False, 'there was a server error at %s' % (world.browser.url)
else:
assert True
@step(u'I verify all the content of each course')
def i_verify_all_the_content_of_each_course(step):
all_possible_courses = get_courses()
ids = [c.id for c in all_possible_courses]
# Get a list of all the registered courses
registered_courses = world.browser.find_by_css('article.my-course')
if len(all_possible_courses) < len(registered_courses):
assert False, "user is registered for more courses than are uniquely posssible"
else:
pass
for test_course in registered_courses:
test_course.find_by_css('a').click()
check_for_errors()
# Get the course. E.g. 'MITx/6.002x/2012_Fall'
current_course = re.sub('/info','',re.sub('.*/courses/','',world.browser.url))
validate_course(current_course,ids)
world.browser.find_link_by_text('Courseware').click()
assert world.browser.is_element_present_by_id('accordion',wait_time=2)
check_for_errors()
browse_course(current_course)
# clicking the user link gets you back to the user's home page
world.browser.find_by_css('.user-link').click()
check_for_errors()
def browse_course(course_id):
## count chapters from xml and page and compare
chapters = get_courseware_with_tabs(course_id)
num_chapters = len(chapters)
rendered_chapters = world.browser.find_by_css('#accordion > nav > div')
num_rendered_chapters = len(rendered_chapters)
assert num_chapters == num_rendered_chapters, '%d chapters expected, %d chapters found on page for %s' % (num_chapters, num_rendered_chapters, course_id)
chapter_it = 0
## Iterate the chapters
while chapter_it < num_chapters:
## click into a chapter
world.browser.find_by_css('#accordion > nav > div')[chapter_it].find_by_tag('h3').click()
## look for the "there was a server error" div
check_for_errors()
## count sections from xml and page and compare
sections = chapters[chapter_it]['sections']
num_sections = len(sections)
rendered_sections = world.browser.find_by_css('#accordion > nav > div')[chapter_it].find_by_tag('li')
num_rendered_sections = len(rendered_sections)
assert num_sections == num_rendered_sections, '%d sections expected, %d sections found on page, iteration number %d on %s' % (num_sections, num_rendered_sections, chapter_it, course_id)
section_it = 0
## Iterate the sections
while section_it < num_sections:
## click on a section
world.browser.find_by_css('#accordion > nav > div')[chapter_it].find_by_tag('li')[section_it].find_by_tag('a').click()
## sometimes the course-content takes a long time to load
assert world.browser.is_element_present_by_css('.course-content',wait_time=5)
## look for server error div
check_for_errors()
## count tabs from xml and page and compare
## count the number of tabs. If number of tabs is 0, there won't be anything rendered
## so we explicitly set rendered_tabs because otherwise find_elements returns a None object with no length
num_tabs = sections[section_it]['clickable_tab_count']
if num_tabs != 0:
rendered_tabs = world.browser.find_by_css('ol#sequence-list > li')
num_rendered_tabs = len(rendered_tabs)
else:
rendered_tabs = 0
num_rendered_tabs = 0
assert num_tabs == num_rendered_tabs ,'%d tabs expected, %d tabs found, iteration number %d, on %s' % (num_tabs,num_rendered_tabs,section_it, course_id)
tab_it = 0
## Iterate the tabs
while tab_it < num_tabs:
rendered_tabs[tab_it].find_by_tag('a').click()
## do something with the tab sections[section_it]
check_for_errors()
tab_it += 1
section_it += 1
chapter_it += 1
def validate_course(current_course, ids):
try:
ids.index(current_course)
except:
assert False, "invalid course id"
## acceptance_testing
This fake django app is here to support acceptance testing using <a href="http://lettuce.it/">lettuce</a> +
<a href="https://github.com/wieden-kennedy/salad">salad</a>
(which uses <a href="http://splinter.cobrateam.info/">splinter</a> wrapping <a href="http://selenium.googlecode.com/svn/trunk/docs/api/py/index.html">selenium</a>).
Some documentation for our efforts are located in basecamp at <a href="https://basecamp.com/1892446/projects/841513-release/documents/1015202-staging-tests">this link</a>.
First you need to make sure that you've installed the requirements.
This includes lettuce, salad, selenium, splinter, etc.
Do this with:
```pip install -r test-requirements.txt```
First set up the database that you need:
WARNING!!! THIS WILL OVERWRITE THE DATA IN YOUR DEV DATABASE
IF YOU WANT TO KEEP THAT DATA, SAVE A COPY OF YOUR ../db/mitx.db ELSEWHERE FIRST!
<li>If necessary, delete it first from mit_all/db</li>
<li>```rake django-admin[syncdb,lms,acceptance]```</li>
<li>```rake django-admin[migrate,lms,acceptance]```</li>
To use, start up the server separately:
```rake lms[acceptance]```
In between scenarios, flush the database with this command.
You will not need to do this if it's set up in the terrain.py file
which is at the mitx root level.
```rake django-admin[flush,lms,acceptance,--noinput]```
Running the all the user acceptance scenarios:
```django-admin.py harvest --no-server --settings=lms.envs.acceptance --pythonpath=.```
Running a single user acceptance scenario:
```django-admin.py harvest --no-server --settings=lms.envs.acceptance --pythonpath=. lms/djangoapps/portal/features/signup.feature```
Or you can use the rake task named lettuce like this:
rake lettuce[lms/djangoapps/portal/features/homepage.feature]
Feature: Homepage for web users
In order to get an idea what edX is about
As a an anonymous web user
I want to check the information on the home page
Scenario: User can see the "Login" button
Given I visit the homepage
Then I should see a link called "Log In"
Scenario: User can see the "Sign up" button
Given I visit the homepage
Then I should see a link called "Sign Up"
Scenario Outline: User can see main parts of the page
Given I visit the homepage
Then I should see a link called "<Link>"
When I click the link with the text "<Link>"
Then I should see that the path is "<Path>"
Examples:
| Link | Path |
| Find Courses | /courses |
| About | /about |
| Jobs | /jobs |
| Contact | /contact |
Scenario: User can visit the blog
Given I visit the homepage
When I click the link with the text "Blog"
Then I should see that the url is "http://blog.edx.org/"
# TODO: test according to domain or policy
Scenario: User can see the partner institutions
Given I visit the homepage
Then I should see "<Partner>" in the Partners section
Examples:
| Partner |
| MITx |
| HarvardX |
| BerkeleyX |
| UTx |
# # TODO: Add scenario that tests the courses available
# # using a policy or a configuration file
from lettuce import world, step
@step('I should see "([^"]*)" in the Partners section$')
def i_should_see_partner(step, partner):
assert (partner in world.browser.find_by_css(".partners").text)
Feature: Login in as a registered user
As a registered user
In order to access my content
I want to be able to login in to edX
Scenario: Login to an unactivated account
Given I am an edX user
And I am an unactivated user
And I visit the homepage
When I click on the link with the text "Log In"
And I submit my credentials on the login form
Then I should see the login error message "This account has not been activated"
Scenario: Login to an activated account
Given I am an edX user
And I am an activated user
And I visit the homepage
When I click on the link with the text "Log In"
And I submit my credentials on the login form
Then I should be on the dashboard page
Scenario: Logout of a signed in account
Given I am logged in
When I click the dropdown arrow
And I click on the link with the text "Log Out"
Then I should see a link with the text "Log In"
And I should see that the path is "/"
\ No newline at end of file
from lettuce import step, world
from salad.steps.everything import *
from django.contrib.auth.models import User
@step('I am an unactivated user$')
def i_am_an_unactivated_user(step):
user_is_an_unactivated_user('robot')
@step('I am an activated user$')
def i_am_an_activated_user(step):
user_is_an_activated_user('robot')
@step('I submit my credentials on the login form')
def i_submit_my_credentials_on_the_login_form(step):
fill_in_the_login_form('email', 'robot@edx.org')
fill_in_the_login_form('password', 'test')
login_form = world.browser.find_by_css('form#login_form')
login_form.find_by_value('Access My Courses').click()
@step(u'I should see the login error message "([^"]*)"$')
def i_should_see_the_login_error_message(step, msg):
login_error_div = world.browser.find_by_css('form#login_form #login_error')
assert (msg in login_error_div.text)
@step(u'click the dropdown arrow$')
def click_the_dropdown(step):
css = ".dropdown"
e = world.browser.find_by_css(css)
e.click()
#### helper functions
def user_is_an_unactivated_user(uname):
u = User.objects.get(username=uname)
u.is_active = False
u.save()
def user_is_an_activated_user(uname):
u = User.objects.get(username=uname)
u.is_active = True
u.save()
def fill_in_the_login_form(field, value):
login_form = world.browser.find_by_css('form#login_form')
form_field = login_form.find_by_name(field)
form_field.fill(value)
Feature: Register for a course
As a registered user
In order to access my class content
I want to register for a class on the edX website
Scenario: I can register for a course
Given I am logged in
And I visit the courses page
When I register for the course numbered "6.002x"
Then I should see the course numbered "6.002x" in my dashboard
Scenario: I can unregister for a course
Given I am logged in
And I am registered for a course
And I visit the dashboard
When I click the link with the text "Unregister"
And I press the "Unregister" button in the Unenroll dialog
Then I should see "Looks like you haven't registered for any courses yet." somewhere in the page
\ No newline at end of file
from lettuce import world, step
@step('I register for the course numbered "([^"]*)"$')
def i_register_for_the_course(step, course):
courses_section = world.browser.find_by_css('section.courses')
course_link_css = 'article[id*="%s"] a' % course
course_link = courses_section.find_by_css(course_link_css).first
course_link.click()
intro_section = world.browser.find_by_css('section.intro')
register_link = intro_section.find_by_css('a.register')
register_link.click()
assert world.browser.is_element_present_by_css('section.container.dashboard')
@step(u'I should see the course numbered "([^"]*)" in my dashboard$')
def i_should_see_that_course_in_my_dashboard(step, course):
course_link_css = 'section.my-courses a[href*="%s"]' % course
assert world.browser.is_element_present_by_css(course_link_css)
@step(u'I press the "([^"]*)" button in the Unenroll dialog')
def i_press_the_button_in_the_unenroll_dialog(step, value):
button_css = 'section#unenroll-modal input[value="%s"]' % value
world.browser.find_by_css(button_css).click()
Feature: Sign in
In order to use the edX content
As a new user
I want to signup for a student account
Scenario: Sign up from the homepage
Given I visit the homepage
When I click the link with the text "Sign Up"
And I fill in "email" on the registration form with "robot2@edx.org"
And I fill in "password" on the registration form with "test"
And I fill in "username" on the registration form with "robot2"
And I fill in "name" on the registration form with "Robot Two"
And I check the checkbox named "terms_of_service"
And I check the checkbox named "honor_code"
And I press the "Create My Account" button on the registration form
Then I should see "THANKS FOR REGISTERING!" in the dashboard banner
\ No newline at end of file
from lettuce import world, step
@step('I fill in "([^"]*)" on the registration form with "([^"]*)"$')
def when_i_fill_in_field_on_the_registration_form_with_value(step, field, value):
register_form = world.browser.find_by_css('form#register_form')
form_field = register_form.find_by_name(field)
form_field.fill(value)
@step('I press the "([^"]*)" button on the registration form$')
def i_press_the_button_on_the_registration_form(step, button):
register_form = world.browser.find_by_css('form#register_form')
register_form.find_by_value(button).click()
@step('I check the checkbox named "([^"]*)"$')
def i_check_checkbox(step, checkbox):
world.browser.find_by_name(checkbox).check()
@step('I should see "([^"]*)" in the dashboard banner$')
def i_should_see_text_in_the_dashboard_banner_section(step, text):
css_selector = "section.dashboard-banner h2"
assert (text in world.browser.find_by_css(css_selector).text)
\ No newline at end of file
# Use this as your terrain file so that the common steps
# across all lms apps can be put in terrain/steps
# See https://groups.google.com/forum/?fromgroups=#!msg/lettuce-users/5VyU9B4HcX8/USgbGIJdS5QJ
from terrain.browser import *
from terrain.common import *
from terrain.factories import *
\ No newline at end of file
from lettuce import before, after, world
from splinter.browser import Browser
from splinter.driver.webdriver.firefox import FirefoxProfile
from logging import getLogger
import time
logger = getLogger(__name__)
logger.info("Loading the terrain file...")
try:
from django.core.management import call_command
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
from django.core import mail
try:
from south.management.commands import patch_for_test_db_setup
USE_SOUTH = getattr(settings, "SOUTH_TESTS_MIGRATE", False)
except:
USE_SOUTH = False
@before.runserver
def setup_database(actual_server):
logger.info("Setting up a test database...")
if USE_SOUTH:
patch_for_test_db_setup()
world.test_runner = DjangoTestSuiteRunner(interactive=False)
DjangoTestSuiteRunner.setup_test_environment(world.test_runner)
world.created_db = DjangoTestSuiteRunner.setup_databases(world.test_runner)
# call_command('syncdb', interactive=False, verbosity=0)
# call_command('migrate', interactive=False, verbosity=0)
# because the TestSuiteRunner setup_test_environment hard codes it to False
settings.DEBUG = True
@after.runserver
def teardown_database(actual_server):
if hasattr(world, "test_runner"):
logger.info("Destroying the test database ...")
DjangoTestSuiteRunner.teardown_databases(world.test_runner, world.created_db)
DjangoTestSuiteRunner.teardown_test_environment(world.test_runner)
@before.harvest
def initial_setup(server):
# call_command('syncdb', interactive=False, verbosity=2)
# call_command('migrate', interactive=False, verbosity=2)
world.browser = Browser('firefox')
# pass
# logger.info('Sleeping 7 seconds to give the server time to compile the js...')
# time.sleep(float(7))
# logger.info('...done sleeping.')
@before.each_scenario
def reset_data(scenario):
# Clean up django.
logger.info("Flushing the test database...")
call_command('flush', interactive=False)
#call_command('loaddata', 'all', verbosity=0)
@after.all
def teardown_browser(total):
# world.browser.driver.save_screenshot('/tmp/selenium_screenshot.png')
# world.browser.quit()
pass
except:
try:
# Only complain if it seems likely that using django was intended.
import django
logger.warn("Django terrains not imported.")
except:
pass
\ No newline at end of file
from lettuce import world, step
from factories import *
from django.core.management import call_command
from salad.steps.everything import *
from lettuce.django import django_url
from django.conf import settings
from django.contrib.auth.models import User
from student.models import CourseEnrollment
import time
from nose.tools import assert_equals
from logging import getLogger
logger = getLogger(__name__)
@step(u'I wait (?:for )?"(\d+)" seconds?$')
def wait(step, seconds):
time.sleep(float(seconds))
@step('I (?:visit|access|open) the homepage$')
def i_visit_the_homepage(step):
world.browser.visit(django_url('/'))
assert world.browser.is_element_present_by_css('header.global', 10)
@step(u'I (?:visit|access|open) the dashboard$')
def i_visit_the_dashboard(step):
world.browser.visit(django_url('/dashboard'))
assert world.browser.is_element_present_by_css('section.container.dashboard', 5)
@step('I should be on the dashboard page$')
def i_should_be_on_the_dashboard(step):
assert world.browser.is_element_present_by_css('section.container.dashboard', 5)
assert world.browser.title == 'Dashboard'
@step(u'I (?:visit|access|open) the courses page$')
def i_am_on_the_courses_page(step):
world.browser.visit(django_url('/courses'))
assert world.browser.is_element_present_by_css('section.courses')
@step('I should see that the path is "([^"]*)"$')
def i_should_see_that_the_path_is(step, path):
assert world.browser.url == django_url(path)
@step(u'the page title should be "([^"]*)"$')
def the_page_title_should_be(step, title):
assert_equals(world.browser.title, title)
@step('I am a logged in user$')
def i_am_logged_in_user(step):
create_user('robot')
log_in('robot@edx.org','test')
@step('I am not logged in$')
def i_am_not_logged_in(step):
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):
create_user('robot')
u = User.objects.get(username='robot')
CourseEnrollment.objects.get_or_create(user=u, course_id=course_id)
@step('I log in$')
def i_log_in(step):
log_in('robot@edx.org','test')
@step(u'I am an edX user$')
def i_am_an_edx_user(step):
create_user('robot')
#### helper functions
def create_user(uname):
# This user factory stuff should work after we kill askbot
portal_user = UserFactory.build(username=uname, email=uname + '@edx.org')
portal_user.set_password('test')
portal_user.save()
registration = RegistrationFactory(user=portal_user)
registration.register(portal_user)
registration.activate()
user_profile = UserProfileFactory(user=portal_user)
def log_in(email, password):
world.browser.cookies.delete()
world.browser.visit(django_url('/'))
world.browser.is_element_present_by_css('header.global', 10)
world.browser.click_link_by_href('#login-modal')
login_form = world.browser.find_by_css('form#login_form')
login_form.find_by_name('email').fill(email)
login_form.find_by_name('password').fill(password)
login_form.find_by_name('submit').click()
# wait for the page to redraw
assert world.browser.is_element_present_by_css('.content-wrapper', 10)
########### DEBUGGING ##############
@step(u'I save a screenshot to "(.*)"')
def save_screenshot_to(step, filename):
world.browser.driver.save_screenshot(filename)
import factory
from student.models import User, UserProfile, Registration
from datetime import datetime
import uuid
class UserProfileFactory(factory.Factory):
FACTORY_FOR = UserProfile
user = None
name = 'Robot Test'
level_of_education = None
gender = 'm'
mailing_address = None
goals = 'World domination'
class RegistrationFactory(factory.Factory):
FACTORY_FOR = Registration
user = None
activation_key = uuid.uuid4().hex
class UserFactory(factory.Factory):
FACTORY_FOR = User
username = 'robot'
email = 'robot+test@edx.org'
password = 'test'
first_name = 'Robot'
last_name = 'Test'
is_staff = False
is_active = True
is_superuser = False
last_login = datetime(2012, 1, 1)
date_joined = datetime(2011, 1, 1)
"""
This config file is a copy of dev environment without the Debug
Toolbar. I it suitable to run against acceptance tests.
"""
from .dev import *
# REMOVE DEBUG TOOLBAR
INSTALLED_APPS = tuple(e for e in INSTALLED_APPS if e != 'debug_toolbar')
MIDDLEWARE_CLASSES = tuple(e for e in MIDDLEWARE_CLASSES \
if e != 'debug_toolbar.middleware.DebugToolbarMiddleware')
########################### LETTUCE TESTING ##########################
MITX_FEATURES['DISPLAY_TOY_COURSES'] = True
INSTALLED_APPS += ('lettuce.django',)
LETTUCE_APPS = ('portal',) # dummy app covers the home page, login, registration, and course enrollment
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment