New branch for clean lms acceptance tests
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
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
|
||||
197
lms/djangoapps/courseware/features/course-section-content.py
Normal file
197
lms/djangoapps/courseware/features/course-section-content.py
Normal file
@@ -0,0 +1,197 @@
|
||||
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)
|
||||
21
lms/djangoapps/courseware/features/course_info.feature
Normal file
21
lms/djangoapps/courseware/features/course_info.feature
Normal file
@@ -0,0 +1,21 @@
|
||||
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
|
||||
15
lms/djangoapps/courseware/features/course_info.py
Normal file
15
lms/djangoapps/courseware/features/course_info.py
Normal file
@@ -0,0 +1,15 @@
|
||||
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)
|
||||
129
lms/djangoapps/courseware/features/courses.py
Normal file
129
lms/djangoapps/courseware/features/courses.py
Normal file
@@ -0,0 +1,129 @@
|
||||
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
|
||||
|
||||
18
lms/djangoapps/courseware/features/courseware.feature
Normal file
18
lms/djangoapps/courseware/features/courseware.feature
Normal file
@@ -0,0 +1,18 @@
|
||||
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
|
||||
36
lms/djangoapps/courseware/features/courseware_common.py
Normal file
36
lms/djangoapps/courseware/features/courseware_common.py
Normal file
@@ -0,0 +1,36 @@
|
||||
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
|
||||
23
lms/djangoapps/courseware/features/high-level-tabs.feature
Normal file
23
lms/djangoapps/courseware/features/high-level-tabs.feature
Normal file
@@ -0,0 +1,23 @@
|
||||
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"
|
||||
19
lms/djangoapps/courseware/features/smart-accordion.feature
Normal file
19
lms/djangoapps/courseware/features/smart-accordion.feature
Normal file
@@ -0,0 +1,19 @@
|
||||
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
|
||||
149
lms/djangoapps/courseware/features/smart-accordion.py
Normal file
149
lms/djangoapps/courseware/features/smart-accordion.py
Normal file
@@ -0,0 +1,149 @@
|
||||
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"
|
||||
37
lms/djangoapps/portal/README.md
Normal file
37
lms/djangoapps/portal/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
## 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]
|
||||
0
lms/djangoapps/portal/__init__.py
Normal file
0
lms/djangoapps/portal/__init__.py
Normal file
45
lms/djangoapps/portal/features/homepage.feature
Normal file
45
lms/djangoapps/portal/features/homepage.feature
Normal file
@@ -0,0 +1,45 @@
|
||||
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
|
||||
5
lms/djangoapps/portal/features/homepage.py
Normal file
5
lms/djangoapps/portal/features/homepage.py
Normal file
@@ -0,0 +1,5 @@
|
||||
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)
|
||||
27
lms/djangoapps/portal/features/login.feature
Normal file
27
lms/djangoapps/portal/features/login.feature
Normal file
@@ -0,0 +1,27 @@
|
||||
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 "/"
|
||||
46
lms/djangoapps/portal/features/login.py
Normal file
46
lms/djangoapps/portal/features/login.py
Normal file
@@ -0,0 +1,46 @@
|
||||
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)
|
||||
18
lms/djangoapps/portal/features/registration.feature
Normal file
18
lms/djangoapps/portal/features/registration.feature
Normal file
@@ -0,0 +1,18 @@
|
||||
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
|
||||
24
lms/djangoapps/portal/features/registration.py
Normal file
24
lms/djangoapps/portal/features/registration.py
Normal file
@@ -0,0 +1,24 @@
|
||||
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()
|
||||
16
lms/djangoapps/portal/features/signup.feature
Normal file
16
lms/djangoapps/portal/features/signup.feature
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
22
lms/djangoapps/portal/features/signup.py
Normal file
22
lms/djangoapps/portal/features/signup.py
Normal file
@@ -0,0 +1,22 @@
|
||||
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)
|
||||
|
||||
6
lms/djangoapps/terrain/__init__.py
Normal file
6
lms/djangoapps/terrain/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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 *
|
||||
78
lms/djangoapps/terrain/browser.py
Normal file
78
lms/djangoapps/terrain/browser.py
Normal file
@@ -0,0 +1,78 @@
|
||||
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
|
||||
105
lms/djangoapps/terrain/common.py
Normal file
105
lms/djangoapps/terrain/common.py
Normal file
@@ -0,0 +1,105 @@
|
||||
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)
|
||||
34
lms/djangoapps/terrain/factories.py
Normal file
34
lms/djangoapps/terrain/factories.py
Normal file
@@ -0,0 +1,34 @@
|
||||
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)
|
||||
20
lms/envs/acceptance.py
Normal file
20
lms/envs/acceptance.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
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
|
||||
@@ -3,3 +3,7 @@ coverage
|
||||
nosexcover
|
||||
pylint
|
||||
pep8
|
||||
lettuce
|
||||
salad
|
||||
selenium
|
||||
factory_boy
|
||||
|
||||
Reference in New Issue
Block a user