Commit d5111b9e by Jay Zoldak

Get lettuce tests running against test database

parent ccc8b599
## acceptance_testing ## acceptance_testing
This fake django app is here to support acceptance testing using <a href="http://lettuce.it/">lettuce</a> + 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> <a href="http://splinter.cobrateam.info/">splinter</a> (which wraps <a href="http://selenium.googlecode.com/svn/trunk/docs/api/py/index.html">selenium</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>. 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. First you need to make sure that you've installed the requirements.
This includes lettuce, salad, selenium, splinter, etc. This includes lettuce, selenium, splinter, etc.
Do this with: Do this with:
```pip install -r test-requirements.txt``` ```pip install -r test-requirements.txt```
......
from lettuce import world, step#, before, after from lettuce import world, step#, before, after
from factories import * from factories import *
from django.core.management import call_command from django.core.management import call_command
from salad.steps.everything import * from nose.tools import assert_equals, assert_in
from lettuce.django import django_url from lettuce.django import django_url
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -25,6 +25,10 @@ def i_visit_the_dashboard(step): ...@@ -25,6 +25,10 @@ def i_visit_the_dashboard(step):
world.browser.visit(django_url('/dashboard')) world.browser.visit(django_url('/dashboard'))
assert world.browser.is_element_present_by_css('section.container.dashboard', 5) assert world.browser.is_element_present_by_css('section.container.dashboard', 5)
@step(r'click (?:the|a) link (?:called|with the text) "([^"]*)"$')
def click_the_link_called(step, text):
world.browser.find_link_by_text(text).click()
@step('I should be on the dashboard page$') @step('I should be on the dashboard page$')
def i_should_be_on_the_dashboard(step): def i_should_be_on_the_dashboard(step):
assert world.browser.is_element_present_by_css('section.container.dashboard', 5) assert world.browser.is_element_present_by_css('section.container.dashboard', 5)
...@@ -43,6 +47,18 @@ def i_should_see_that_the_path_is(step, path): ...@@ -43,6 +47,18 @@ def i_should_see_that_the_path_is(step, path):
def the_page_title_should_be(step, title): def the_page_title_should_be(step, title):
assert world.browser.title == title assert world.browser.title == title
@step(r'should see that the url is "([^"]*)"$')
def should_have_the_url(step, url):
assert_equals(world.browser.url, url)
@step(r'should see (?:the|a) link (?:called|with the text) "([^"]*)"$')
def should_see_a_link_called(step, text):
assert len(world.browser.find_link_by_text(text)) > 0
@step(r'should see "(.*)" (?:somewhere|anywhere) in (?:the|this) page')
def should_see_in_the_page(step, text):
assert_in(text, world.browser.html)
@step('I am logged in$') @step('I am logged in$')
def i_am_logged_in(step): def i_am_logged_in(step):
world.create_user('robot') world.create_user('robot')
......
...@@ -7,7 +7,7 @@ Feature: Login in as a registered user ...@@ -7,7 +7,7 @@ Feature: Login in as a registered user
Given I am an edX user Given I am an edX user
And I am an unactivated user And I am an unactivated user
And I visit the homepage And I visit the homepage
When I click on the link with the text "Log In" When I click the link with the text "Log In"
And I submit my credentials on the login form And I submit my credentials on the login form
Then I should see the login error message "This account has not been activated" Then I should see the login error message "This account has not been activated"
...@@ -15,13 +15,13 @@ Feature: Login in as a registered user ...@@ -15,13 +15,13 @@ Feature: Login in as a registered user
Given I am an edX user Given I am an edX user
And I am an activated user And I am an activated user
And I visit the homepage And I visit the homepage
When I click on the link with the text "Log In" When I click the link with the text "Log In"
And I submit my credentials on the login form And I submit my credentials on the login form
Then I should be on the dashboard page Then I should be on the dashboard page
Scenario: Logout of a signed in account Scenario: Logout of a signed in account
Given I am logged in Given I am logged in
When I click the dropdown arrow When I click the dropdown arrow
And I click on the link with the text "Log Out" And I click the link with the text "Log Out"
Then I should see a link with the text "Log In" Then I should see a link with the text "Log In"
And I should see that the path is "/" And I should see that the path is "/"
from lettuce import step, world from lettuce import step, world
from salad.steps.everything import *
from django.contrib.auth.models import User from django.contrib.auth.models import User
@step('I am an unactivated user$') @step('I am an unactivated user$')
......
from lettuce import before, after, world from lettuce import before, after, world
from splinter.browser import Browser from splinter.browser import Browser
from splinter.driver.webdriver.firefox import FirefoxProfile
from logging import getLogger from logging import getLogger
import time import time
logger = getLogger(__name__) logger = getLogger(__name__)
logger.info("Loading the terrain file...") logger.info("Loading the terrain file...")
try: from django.core.management import call_command
from django.core.management import call_command
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
from django.core import mail
try: @before.harvest
from south.management.commands import patch_for_test_db_setup def initial_setup(server):
USE_SOUTH = getattr(settings, "SOUTH_TESTS_MIGRATE", False)
except:
USE_SOUTH = False
@before.runserver # Sync the test database defined in the settings.py file
def setup_database(actual_server): # then apply the SOUTH migrations
logger.info("Setting up a test database...") call_command('syncdb', interactive=False)
call_command('migrate', interactive=False)
if USE_SOUTH: # Launch firefox
patch_for_test_db_setup() world.browser = Browser('firefox')
world.test_runner = DjangoTestSuiteRunner(interactive=False) @before.each_scenario
DjangoTestSuiteRunner.setup_test_environment(world.test_runner) def reset_data(scenario):
world.created_db = DjangoTestSuiteRunner.setup_databases(world.test_runner) # Clean up the django database
logger.info("Flushing the test database...")
call_command('flush', interactive=False)
# call_command('syncdb', interactive=False, verbosity=0) @after.all
# call_command('migrate', interactive=False, verbosity=0) def teardown_browser(total):
# Quit firefox
# because the TestSuiteRunner setup_test_environment hard codes it to False world.browser.quit()
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 lettuce import world, step
from factories import * from factories import *
from django.core.management import call_command from django.core.management import call_command
from salad.steps.everything import *
from lettuce.django import django_url from lettuce.django import django_url
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -87,7 +86,6 @@ def i_am_an_edx_user(step): ...@@ -87,7 +86,6 @@ def i_am_an_edx_user(step):
#### helper functions #### helper functions
@world.absorb @world.absorb
def create_user(uname): 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 = UserFactory.build(username=uname, email=uname + '@edx.org')
portal_user.set_password('test') portal_user.set_password('test')
portal_user.save() portal_user.save()
...@@ -136,7 +134,7 @@ def save_the_course_content(path='/tmp'): ...@@ -136,7 +134,7 @@ def save_the_course_content(path='/tmp'):
soup = BeautifulSoup(html) soup = BeautifulSoup(html)
# get rid of the header, we only want to compare the body # get rid of the header, we only want to compare the body
# soup.head.decompose() soup.head.decompose()
# for now, remove the data-id attributes, because they are # for now, remove the data-id attributes, because they are
# causing mismatches between cms-master and master # causing mismatches between cms-master and master
...@@ -166,9 +164,8 @@ def save_the_course_content(path='/tmp'): ...@@ -166,9 +164,8 @@ def save_the_course_content(path='/tmp'):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
filename = '%s.html' % (quote_plus(section_url)) filename = '%s.html' % (quote_plus(section_url))
f = open('%s/%s' % (path, filename), 'w') f = open('%s/%s' % (path, filename), 'w')
f.write(output) f.write(output)
f.close f.close
""" Command line interface to difflib.py to compare html files
"""
import sys, os, time, difflib, optparse, os.path, re
from urllib import unquote_plus
def main():
# Configure the option parser
usage = "usage: %prog fromdir todir"
parser = optparse.OptionParser(usage)
(options, args) = parser.parse_args()
if len(args) == 0:
parser.print_help()
sys.exit(1)
if len(args) != 2:
parser.error("need to specify both a fromdir and todir")
fromdir, todir = args # as specified in the usage string
if not os.path.isdir(fromdir):
print "'%s' is not a directory" % fromdir
if not os.path.isdir(todir):
print "'%s' is not a directory" % todir
from_files = os.listdir(fromdir)
to_files = os.listdir(todir)
for filename in from_files:
if filename in to_files:
fromfile = os.path.join(fromdir, filename)
tofile = os.path.join(todir, filename)
# we're passing these as arguments to the diff function
# fromdate = time.ctime(os.stat(fromfile).st_mtime)
# todate = time.ctime(os.stat(tofile).st_mtime)
fromlines = cleanup(open(fromfile, 'U').readlines())
tolines = cleanup(open(tofile, 'U').readlines())
diff = difflib.unified_diff(fromlines, tolines, fromdir, todir, n=0)
# fromdate, todate, n=0)
print 'FILE: %s' % unquote_plus(filename)
# we're using writelines because diff is a generator
sys.stdout.writelines(diff)
print ''
def cleanup(lines):
lines = [s.replace('/c4x/MITx/6.002x/asset', '/static/content-mit-6002x') for s in lines]
lines = [s.replace('handouts_', 'handouts/') for s in lines]
return lines
if __name__ == '__main__':
main()
\ No newline at end of file
""" """
This config file is a copy of dev environment without the Debug This config file extends the test environment configuration
Toolbar. I it suitable to run against acceptance tests. so that we can run the lettuce acceptance tests.
""" """
from .dev import * from .test import *
# REMOVE DEBUG TOOLBAR # You need to start the server in debug mode,
# otherwise the browser will not render the pages correctly
INSTALLED_APPS = tuple(e for e in INSTALLED_APPS if e != 'debug_toolbar') DEBUG = True
MIDDLEWARE_CLASSES = tuple(e for e in MIDDLEWARE_CLASSES \
if e != 'debug_toolbar.middleware.DebugToolbarMiddleware') # We need to apply the SOUTH migrations to set up the
# auth tables correctly. Otherwise you'll get an error like this:
########################### OPEN GRADING TESTING ########################## # DatabaseError: no such table: auth_registration
XQUEUE_INTERFACE = { SOUTH_TESTS_MIGRATE = True
"url": 'http://127.0.0.1:3032',
"django_auth": { # Set this up so that rake lms[acceptance] and running the
"username": "lms", # harvest command both use the same (test) database
"password": "abcd" # which they can flush without messing up your dev db
}, DATABASES = {
"basic_auth": ('anant', 'agarwal'), 'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "test_mitx.db",
'TEST_NAME': ENV_ROOT / "db" / "test_mitx.db",
}
} }
########################### LETTUCE TESTING ##########################
MITX_FEATURES['DISPLAY_TOY_COURSES'] = True MITX_FEATURES['DISPLAY_TOY_COURSES'] = True
INSTALLED_APPS += ('lettuce.django',) INSTALLED_APPS += ('lettuce.django',)
LETTUCE_APPS = ('portal',) # dummy app covers the home page, login, registration, and course enrollment LETTUCE_APPS = ('portal',) # dummy app covers the home page, login, registration, and course enrollment
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