Update selenium, use chrome for tests, and use sauce-labs for CI integration tests

This commit is contained in:
David Taylor
2017-05-15 21:41:33 +01:00
parent 55d24e96cb
commit fdce2fa53d
4 changed files with 97 additions and 57 deletions

View File

@@ -1,20 +1,27 @@
sudo: false
dist: trusty
language: python
python:
"2.7"
cache: pip
before_install:
- "export DISPLAY=:99.0"
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
addons:
sauce_connect:
username: davidtaylorhq
access_key:
secure: "ibpjQ19LfjwfQruiJmX0j6NzyNwsS3PvRFdfXUYcfCEa9Eh20QQ/S8pOdFhRh70KIEuwN5oGuPqDkJPPTjkdY3/NCjuA7/NMTp14jAIX4XjpeNcsPFupp31vEy7KBuX4iAGpenrHJssFCwurpvrlWfwSOrk7bVZKaGUowVOXmyth1FSNQvr5c3YnlxmGvNzNBMMBDcJ3ixSlS9pBRLnHIJ1w3/f9Lx2uONkVMeGM6rVyuHholWvanIyNVYtO9JkXkoie6n1R3gNbXCyJdxSRn2OLppdryUaA0wUPJSu3hqEM3R5EsRDiFJszkJLTwSBG8x4k/dbqim7stjsu1qpUhCIG5mT6e+UI9auPi/5nlwlVmPhSq58qBP53vH3hs++02wjDlgvTGB1p4PqFblHhVaslaQ166bo9skGMZb0fXLlM1aCmmwFTpC5ofiPTSRTdJcljHG/d3JabKX03ME+nX2LFPIMnSLXgrjrfh2ppI6LFESiX3Z8jYUdsgTFeN3nQZ8U0kyb5X9Ay9YFnAaYD9OuxaqweTmqAJQj093GK38+79WMN2jnvEUzM1ZjI8Y4L/f3rHvhNIwYvZjQ+gJRhUqJh2Qruk7ke7uQ1oecxIqRHj8hIFEkuBcM3e86MkRiYQXXI9jOX3JrhI/jivAjFuw0flU2tjLNgM7tUYzjMyqk="
install:
- pip install -r requirements.txt
- pip install coveralls codeclimate-test-reporter
before_script:
- export PATH=$PATH:/usr/lib/chromium-browser/
- python manage.py collectstatic --noinput
script:
- coverage run manage.py test RIGS
- coverage run manage.py test --verbosity=2
after_success:
- coveralls

View File

@@ -95,5 +95,18 @@ python manage.py generateSampleData
|keyholder|keyholder|
|basic |basic |
### Testing ###
Tests are contained in 3 files. `RIGS/test_models.py` contains tests for logic within the data models. `RIGS/test_unit.py` contains "Live server" tests, using raw web requests. `RIGS/test_integration.py` contains user interface tests which take control of a web browser. For automated Travis tests, we use [Sauce Labs](https://saucelabs.com). When debugging locally, ensure that you have the latest version of Google Chrome installed, then install [chromedriver](https://sites.google.com/a/chromium.org/chromedriver/) and ensure it is on the `PATH`.
You can run the entire test suite, or you can run specific sections individually. For example, in order of specificity:
```
python manage.py test
python manage.py test RIGS.test_models
python manage.py test RIGS.test_models.EventTestCase
python manage.py test RIGS.test_models.EventTestCase.test_current_events
```
### Committing, pushing and testing ###
Feel free to commit as you wish, on your own branch. On my branch (master for development) do not commit code that you either know doesn't work or don't know works. If you must commit this code, please make sure you say in the commit message that it isn't working, and if you can why it isn't working. If and only if you absolutely must push, then please don't leave it as the HEAD for too long, it's not much to ask but when you are done just make sure you haven't broken the HEAD for the next person.

View File

@@ -3,7 +3,6 @@ import os
import re
from datetime import date, timedelta
import reversion
from django.core import mail
from django.db import transaction
from django.test import LiveServerTestCase
@@ -15,20 +14,52 @@ from selenium.webdriver.support.ui import WebDriverWait
from RIGS import models
import re
import os
from datetime import date, timedelta
from django.db import transaction
from reversion import revisions as reversion
import json
import time
import sys
browsers = [{"platform": "macOS 10.12",
"browserName": "chrome",
"version": "latest"},
]
def on_platforms(platforms):
if os.environ.get("TRAVIS"):
def decorator(base_class):
module = sys.modules[base_class.__module__].__dict__
for i, platform in enumerate(platforms):
d = dict(base_class.__dict__)
d['desired_capabilities'] = platform
name = "%s_%s" % (base_class.__name__, i + 1)
module[name] = type(name, (base_class,), d)
return decorator
def create_browser(test_name, desired_capabilities):
# return webdriver.Chrome()
if os.environ.get("TRAVIS"):
username = os.environ["SAUCE_USERNAME"]
access_key = os.environ["SAUCE_ACCESS_KEY"]
caps = {'browserName': desired_capabilities['browserName']}
caps['platform'] = desired_capabilities['platform']
caps['version'] = desired_capabilities['version']
caps["tunnel-identifier"] = os.environ["TRAVIS_JOB_NUMBER"]
caps["name"] = '#' + os.environ["TRAVIS_JOB_NUMBER"] + ": " + test_name
hub_url = "%s:%s@localhost:4445" % (username, access_key)
driver = webdriver.Remote(desired_capabilities=caps, command_executor="http://%s/wd/hub" % hub_url)
return driver
else:
return webdriver.Chrome()
@on_platforms(browsers)
class UserRegistrationTest(LiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser = create_browser(self.id(), self.desired_capabilities)
self.browser.implicitly_wait(3) # Set implicit wait session wide
os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self):
@@ -155,7 +186,7 @@ class UserRegistrationTest(LiveServerTestCase):
# All is well
@on_platforms(browsers)
class EventTest(LiveServerTestCase):
def setUp(self):
@@ -166,9 +197,9 @@ class EventTest(LiveServerTestCase):
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser.maximize_window()
self.browser = create_browser(self.id(), self.desired_capabilities)
self.browser.implicitly_wait(10) # Set implicit wait session wide
# self.browser.maximize_window()
os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self):
@@ -211,7 +242,7 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 3) #setup WebDriverWait to use later (to wait for animations)
wait.until(animation_is_finished())
@@ -366,11 +397,11 @@ class EventTest(LiveServerTestCase):
self.assertEqual(obj.pk, int(option.get_attribute("value")))
# Set start date/time
form.find_element_by_id('id_start_date').send_keys('3015-05-25')
form.find_element_by_id('id_start_date').send_keys('25/05/3015')
form.find_element_by_id('id_start_time').send_keys('06:59')
# Set end date/time
form.find_element_by_id('id_end_date').send_keys('4000-06-27')
form.find_element_by_id('id_end_date').send_keys('27/06/4000')
form.find_element_by_id('id_end_time').send_keys('07:00')
# Add item
@@ -467,7 +498,7 @@ class EventTest(LiveServerTestCase):
self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk) + '/duplicate/')
self.authenticate('/event/' + str(testEvent.pk) + '/duplicate/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 3) #setup WebDriverWait to use later (to wait for animations)
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
@@ -540,7 +571,7 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 3) #setup WebDriverWait to use later (to wait for animations)
wait.until(animation_is_finished())
@@ -555,14 +586,13 @@ class EventTest(LiveServerTestCase):
e.send_keys('Test Event Name')
# Both dates, no times, end before start
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
form.find_element_by_id('id_end_date').clear()
form.find_element_by_id('id_end_date').send_keys('3015-04-23')
self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-23'")
# Attempt to save - should fail
save.click()
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
@@ -571,16 +601,14 @@ class EventTest(LiveServerTestCase):
# Same date, end time before start time
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
form.find_element_by_id('id_end_date').clear()
form.find_element_by_id('id_end_date').send_keys('3015-04-23')
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-23'")
form.find_element_by_id('id_start_time').clear()
form.find_element_by_id('id_start_time').send_keys(Keys.DELETE)
form.find_element_by_id('id_start_time').send_keys('06:59')
form.find_element_by_id('id_end_time').clear()
form.find_element_by_id('id_end_time').send_keys(Keys.DELETE)
form.find_element_by_id('id_end_time').send_keys('06:00')
# Attempt to save - should fail
@@ -593,31 +621,28 @@ class EventTest(LiveServerTestCase):
# Same date, end time before start time
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
form.find_element_by_id('id_end_date').clear()
form.find_element_by_id('id_end_date').send_keys('3015-04-23')
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-24'")
form.find_element_by_id('id_start_time').clear()
form.find_element_by_id('id_start_time').send_keys(Keys.DELETE)
form.find_element_by_id('id_start_time').send_keys('06:59')
form.find_element_by_id('id_end_time').clear()
form.find_element_by_id('id_end_time').send_keys(Keys.DELETE)
form.find_element_by_id('id_end_time').send_keys('06:00')
# No end date, end time before start time
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
self.browser.execute_script("document.getElementById('id_end_date').value=''")
form.find_element_by_id('id_end_date').clear()
form.find_element_by_id('id_start_time').clear()
form.find_element_by_id('id_start_time').send_keys(Keys.DELETE)
form.find_element_by_id('id_start_time').send_keys('06:59')
form.find_element_by_id('id_end_time').clear()
form.find_element_by_id('id_end_time').send_keys(Keys.DELETE)
form.find_element_by_id('id_end_time').send_keys('06:00')
# Attempt to save - should fail
@@ -630,15 +655,11 @@ class EventTest(LiveServerTestCase):
# 2 dates, end after start
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
self.browser.execute_script("document.getElementById('id_end_date').value='3015-04-26'")
form.find_element_by_id('id_end_date').clear()
form.find_element_by_id('id_end_date').send_keys('3015-04-26')
form.find_element_by_id('id_start_time').clear()
form.find_element_by_id('id_end_time').clear()
self.browser.execute_script("document.getElementById('id_start_time').value=''")
self.browser.execute_script("document.getElementById('id_end_time').value=''")
# Attempt to save - should succeed
save.click()
@@ -653,7 +674,7 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 3) #setup WebDriverWait to use later (to wait for animations)
self.browser.implicitly_wait(3) #Set session-long wait (only works for non-existant DOM objects)
wait.until(animation_is_finished())
@@ -672,8 +693,7 @@ class EventTest(LiveServerTestCase):
e.send_keys('Test Event Name')
# Set an arbitrary date
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
# Save the rig
save.click()
@@ -728,6 +748,7 @@ class EventTest(LiveServerTestCase):
organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
@on_platforms(browsers)
class IcalTest(LiveServerTestCase):
def setUp(self):
@@ -767,7 +788,7 @@ class IcalTest(LiveServerTestCase):
models.Event.objects.create(name="TE E17", start_date=date.today()-timedelta(days=1), is_rig=False, description="non rig yesterday")
models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, description="non rig today cancelled")
self.browser = webdriver.Firefox()
self.browser = create_browser(self.id(), self.desired_capabilities)
self.browser.implicitly_wait(3) # Set implicit wait session wide
os.environ['RECAPTCHA_TESTING'] = 'True'
@@ -926,6 +947,5 @@ class animation_is_finished(object):
numberAnimating = driver.execute_script('return $(":animated").length')
finished = numberAnimating == 0
if finished:
import time
time.sleep(0.1)
return finished

View File

@@ -23,7 +23,7 @@ python-dateutil==2.6.0
pytz==2017.2
raven==6.0.0
reportlab==3.4.0
selenium==2.53.1
selenium==3.4.1
simplejson==3.10.0
six==1.10.0
sqlparse==0.2.3