Commit 22fbbbc4 by Dean Dieker

wrote a function that takes individual quizzes (currently not tested for…

wrote a function that takes individual quizzes (currently not tested for multiple tabs/quizzes/answer types and also added a 502 errror detector to helpers (but did not implement in all other tests). For the section and quiz processing see course-section-content.py
parent c4da8fba
Staff accounts for servers
prod:
http://prod-edx-001.m.edx.org/
ddieker+admin@gmail.com
password
stage:
http://stage-edx-001.m.edx.org/
ddieker+admin@gmail.com
password
sandbox:
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: Login to an existing account
Given I visit "http://localhost:8000/"
When I click "LOG IN"
And I login with "ddieker++@gmail.com" in the "email" field
And I login with "password" in the "password" field
And I press "Access My Courses"
Then I should see an element with class of "user" within "3" seconds
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: Login to an existing account
Given I visit and check 502 for "http://www.edx.org/dashboard"
When I click "LOG IN"
And I login with "ddieker+admin@gmail.com" in the "email" field
And I login with "password" in the "password" field
And I press "Access My Courses"
Then I should see an element with class of "user" within "3" seconds
Scenario: I visit a tabbed quiz
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
\ No newline at end of file
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)
\ No newline at end of file
...@@ -4,7 +4,7 @@ Feature: All the high level tabs should work ...@@ -4,7 +4,7 @@ Feature: All the high level tabs should work
I want to click Courseware, Course Info, Discussion, Wiki, Progress, Instructor I want to click Courseware, Course Info, Discussion, Wiki, Progress, Instructor
Scenario: Login to an existing staff account Scenario: Login to an existing staff account
Given I visit "http://anant:agarwal@stage-edx-001.m.edx.org/dashboard" Given I visit "http://www.edx.org"
When I click "LOG IN" When I click "LOG IN"
And I login with "ddieker+admin@gmail.com" in the "email" field And I login with "ddieker+admin@gmail.com" in the "email" field
And I login with "password" in the "password" field And I login with "password" in the "password" field
...@@ -18,4 +18,4 @@ Feature: All the high level tabs should work ...@@ -18,4 +18,4 @@ Feature: All the high level tabs should work
I click on "Discussion" I click on "Discussion"
I click on "Wiki" I click on "Wiki"
I click on "Progress" I click on "Progress"
I click on "Instructor" I click on "Instructor"
\ No newline at end of file
...@@ -4,7 +4,7 @@ Feature: There are courses on the homepage ...@@ -4,7 +4,7 @@ Feature: There are courses on the homepage
I want to count all the chapters, sections, and tabs for each course I want to count all the chapters, sections, and tabs for each course
Scenario: Login to an existing account Scenario: Login to an existing account
Given I visit "http://anant:agarwal@stage-edx-001.m.edx.org/dashboard" Given I visit "http://www.edx.org"
When I click "LOG IN" When I click "LOG IN"
And I login with "ddieker+admin@gmail.com" in the "email" field And I login with "ddieker+admin@gmail.com" in the "email" field
And I login with "password" in the "password" field And I login with "password" in the "password" field
...@@ -12,4 +12,7 @@ Feature: There are courses on the homepage ...@@ -12,4 +12,7 @@ Feature: There are courses on the homepage
Then I should see an element with class of "user" within "3" seconds Then I should see an element with class of "user" within "3" seconds
Scenario: I visit my registered courses Scenario: I visit my registered courses
I verify all the content of each course I verify all the content of each course
\ No newline at end of file
...@@ -67,10 +67,12 @@ def i_verify_all_the_content_of_each_course(step): ...@@ -67,10 +67,12 @@ def i_verify_all_the_content_of_each_course(step):
def browse_course(course_id): def browse_course(course_id):
world.browser.find_element_by_xpath("//div[@id='accordion']//nav//div[1]").click() world.browser.find_element_by_xpath("//div[@id='accordion']//nav//div[1]").click()
## sometimes the browser looks for the accordion before it has rendered
wait_until_id_renders('accordion',2) wait_until_id_renders('accordion',2)
## look for the non-404 server error page
check_for_errors() check_for_errors()
## count chapters from xml and page and compare
chapters = get_courseware_with_tabs(course_id) chapters = get_courseware_with_tabs(course_id)
num_chapters = len(chapters) num_chapters = len(chapters)
rendered_chapters = len(world.browser.find_elements_by_class_name("chapter")) rendered_chapters = len(world.browser.find_elements_by_class_name("chapter"))
...@@ -81,9 +83,12 @@ def browse_course(course_id): ...@@ -81,9 +83,12 @@ def browse_course(course_id):
## Iterate the chapters ## Iterate the chapters
while chapter_it < num_chapters: while chapter_it < num_chapters:
## click into a chapter
world.browser.find_element_by_xpath("//*[@id='accordion']//nav//div["+str(chapter_it+1)+"]//h3").click() world.browser.find_element_by_xpath("//*[@id='accordion']//nav//div["+str(chapter_it+1)+"]//h3").click()
## look for the "there was a server error" div
check_for_errors() check_for_errors()
## count sections from xml and page and compare
sections = chapters[chapter_it]['sections'] sections = chapters[chapter_it]['sections']
num_sections = len(sections) num_sections = len(sections)
accordion_class = "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content-active" accordion_class = "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content-active"
...@@ -92,13 +97,21 @@ def browse_course(course_id): ...@@ -92,13 +97,21 @@ def browse_course(course_id):
assert num_sections == rendered_sections, '%d sections expected, %d sections found on page, iteration number %d on %s' % (num_sections, rendered_sections, chapter_it, course_id) assert num_sections == rendered_sections, '%d sections expected, %d sections found on page, iteration number %d on %s' % (num_sections, rendered_sections, chapter_it, course_id)
section_it = 0 section_it = 0
## Iterate the sections ## Iterate the sections
while section_it < num_sections: while section_it < num_sections:
## click on a section
world.browser.find_element_by_xpath("//*[@id='accordion']//nav//div["+str(chapter_it+1)+"]//ul[@class='"+accordion_class+"']//li["+str(section_it+1)+"]//a").click() world.browser.find_element_by_xpath("//*[@id='accordion']//nav//div["+str(chapter_it+1)+"]//ul[@class='"+accordion_class+"']//li["+str(section_it+1)+"]//a").click()
## sometimes the course-content takes a long time to load
wait_until_class_renders('course-content',3) wait_until_class_renders('course-content',3)
## look for server error div
check_for_errors() check_for_errors()
#tab = current_course.get_children()[0].get_children()[0] ## 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'] num_tabs = sections[section_it]['clickable_tab_count']
if num_tabs != 0: if num_tabs != 0:
rendered_tabs = len(world.browser.find_elements_by_xpath("//ol[@id='sequence-list']//li")) rendered_tabs = len(world.browser.find_elements_by_xpath("//ol[@id='sequence-list']//li"))
...@@ -107,11 +120,13 @@ def browse_course(course_id): ...@@ -107,11 +120,13 @@ def browse_course(course_id):
assert num_tabs == rendered_tabs ,'%d tabs expected, %d tabs found, iteration number %d, on %s' % (num_tabs,rendered_tabs,section_it, course_id) assert num_tabs == rendered_tabs ,'%d tabs expected, %d tabs found, iteration number %d, on %s' % (num_tabs,rendered_tabs,section_it, course_id)
## Iterate the tabs
tab_it = 0 tab_it = 0
## Iterate the tabs
while tab_it < num_tabs: while tab_it < num_tabs:
tab = world.browser.find_element_by_xpath("//ol[@id='sequence-list']//li["+str(tab_it+1)+"]//a[@data-element='"+str(tab_it+1)+"']") tab = world.browser.find_element_by_xpath("//ol[@id='sequence-list']//li["+str(tab_it+1)+"]//a[@data-element='"+str(tab_it+1)+"']")
tab.click() tab.click()
## do something with the tab sections[section_it]
check_for_errors() check_for_errors()
tab_it += 1 tab_it += 1
......
...@@ -14,7 +14,7 @@ url = 'http://anant:agarwal@stage-edx-001.m.edx.org/' ...@@ -14,7 +14,7 @@ url = 'http://anant:agarwal@stage-edx-001.m.edx.org/'
## Utility method for finding elements in a selection ## Utility method for finding elements in a selection
def find_element_by_name_in_selection(selection_id, field): def find_element_by_name_in_selection(selection_id, field):
e = world.browser.find_element_by_xpath("//*[@id='"+selection_id+"']").find_element_by_name(field) e = world.browser.find_element_by_css_selector("#"+selection_id+" input[name="+field+"]")
return e return e
## Utility methods for waiting for elements to render ## Utility methods for waiting for elements to render
...@@ -43,7 +43,8 @@ def has_courses(): ...@@ -43,7 +43,8 @@ def has_courses():
def register_for_course(coursename): def register_for_course(coursename):
world.browser.get(url+'courses') world.browser.get(url+'courses')
world.browser.find_element_by_xpath("//*[@id='"+coursename+"']//a[1]").click() world.browser.find_element_by_xpath("//*[@id='"+coursename+"']//a[1]").click()
wait_until_class_renders('register',3).click() wait_until_class_renders('register',3)
world.browser.find_element_by_class_name('register').click()
def check_if_registered(coursename): def check_if_registered(coursename):
world.browser.get(url+'dashboard') world.browser.get(url+'dashboard')
...@@ -59,4 +60,18 @@ def check_for_errors(): ...@@ -59,4 +60,18 @@ def check_for_errors():
if len(e) > 0: if len(e) > 0:
assert False, 'there was a server error at %s' % (world.browser.current_url) assert False, 'there was a server error at %s' % (world.browser.current_url)
else: else:
assert True
def check_for_502(url):
'''
Look for those pesky bad gateway errors
'''
try:
e = world.browser.find_element_by_css_selector("body+h1")
if len(e) > 0:
assert False, 'There was a 502 error at %s' % (url)
else:
assert True
except:
assert True assert True
\ No newline at end of file
...@@ -4,10 +4,10 @@ Feature: Login ...@@ -4,10 +4,10 @@ Feature: Login
We'll see if I can log in to my account and perform user actions We'll see if I can log in to my account and perform user actions
Scenario: Login to an existing account and logout Scenario: Login to an existing account and logout
Given I visit "http://anant:agarwal@stage-edx-001.m.edx.org/" Given I visit "http://www.edx.org/"
#"http://anant:agarwal@stage-edx-001.m.edx.org/" #"http://anant:agarwal@stage-edx-001.m.edx.org/"
When I click "LOG IN" When I click "LOG IN"
And I login with "ddieker@gmail.com" in the "email" field And I login with "ddieker+admin@gmail.com" in the "email" field
And I login with "password" in the "password" field And I login with "password" in the "password" field
And I press "Access My Courses" And I press "Access My Courses"
Then I should see an element with class of "user" within "3" seconds Then I should see an element with class of "user" within "3" seconds
......
...@@ -7,7 +7,7 @@ from helpers import * ...@@ -7,7 +7,7 @@ from helpers import *
## Login ## Login
url = 'http://anant:agarwal@sandbox-test-001.m.edx.org/' url = 'http://www.edx.org/'
#url = 'http://anant:agarwal@stage-edx-001.m.edx.org/' #url = 'http://anant:agarwal@stage-edx-001.m.edx.org/'
@step(u'I login with "(.*)" in the "(.*)" field') @step(u'I login with "(.*)" in the "(.*)" field')
......
...@@ -7,6 +7,6 @@ def setup_browser(): ...@@ -7,6 +7,6 @@ def setup_browser():
world.browser = webdriver.Firefox() world.browser = webdriver.Firefox()
@after.all #@after.all
def teardown_browser(total): #def teardown_browser(total):
world.browser.quit() # world.browser.quit()
...@@ -7,6 +7,6 @@ from os import getenv ...@@ -7,6 +7,6 @@ from os import getenv
def setup_browser(): def setup_browser():
world.browser = webdriver.Firefox() world.browser = webdriver.Firefox()
@after.all #@after.all
def teardown_browser(total): #def teardown_browser(total):
world.browser.quit() # world.browser.quit()
\ No newline at end of file \ 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