Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
Merge pull request #4391 from gratipay/unclog-ttw-tests
Browse files Browse the repository at this point in the history
Unclog ttw tests
  • Loading branch information
Paul Kuruvilla authored Mar 30, 2017
2 parents c968a56 + 3c1e47e commit f9eb6df
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 27 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ docs/gratipay.rst
_vimrc_local.vim
.transifexrc
npm-debug.log

# ttw tests
phantomjsdriver.log
ghostdriver.log
geckodriver.log
13 changes: 12 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
dist: trusty
language: python
git:
depth: 5
addons:
postgresql: 9.3
firefox: latest
before_install:
- git branch -vv | grep '^*'
- pwd
Expand All @@ -12,11 +14,20 @@ before_install:
- git clone -b "2.1.0" --depth 1 https://github.com/lloyd/yajl.git && cd yajl && ./configure && sudo make install && cd ..

- npm install marky-markdown

# For ttw tests
- wget https://github.com/mozilla/geckodriver/releases/download/v0.15.0/geckodriver-v0.15.0-linux64.tar.gz
- mkdir -p geckodriver
- tar -xzf geckodriver-v0.15.0-linux64.tar.gz -C geckodriver
- export PATH="geckodriver:$PATH"
- geckodriver --version
- firefox --version
cache:
directories:
- env/bin
- env/lib/python2.7/site-packages
- node_modules
- geckodriver
install:
- if [ "${TRAVIS_BRANCH}" = "master" -a "${TRAVIS_PULL_REQUEST}" = "false" ]; then rm -rf env; fi
- touch requirements.txt package.json
Expand All @@ -27,7 +38,7 @@ before_script:
- echo "REQUIRE_YAJL=true" | tee -a tests/local.env local.env
- psql -U postgres -c 'CREATE DATABASE "gratipay";'
- if [ "${TRAVIS_BRANCH}" = "master" -a "${TRAVIS_PULL_REQUEST}" = "false" ]; then rm -rfv tests/py/fixtures; fi
script: LD_LIBRARY_PATH=/usr/local/lib make bgrun test doc -j2
script: LD_LIBRARY_PATH=/usr/local/lib xvfb-run make bgrun test doc -j2
notifications:
email: false
irc: false
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,11 @@ Testing [![Build Status](http://img.shields.io/travis/gratipay/gratipay.com/mast
=======

Our test suite is divided into through-the-web (TTW) tests and Python tests.
You need to install [PhantomJS](http://phantomjs.org/) separately in order to
run the TTW tests. For both suites we use the [pytest](http://pytest.org/) test
runner; it's installed automatically as part of `make env`.
You need to install [Firefox](https://www.mozilla.org/en-US/firefox/new/) and
[geckodriver](https://github.com/mozilla/geckodriver/releases/) separately in
order to run the TTW tests. For both suites we use the
[pytest](http://pytest.org/) test runner; it's installed automatically as part
of `make env`.
The easiest way to run the whole test suite is:
Expand All @@ -421,8 +423,8 @@ make pytest
```
To invoke `py.test` directly you should use the `honcho` utility that comes
with the install. First `make tests/env`, the activate the virtualenv by running
`source env/bin/activate`, and then:
with the install. First `make tests/env`, then activate the virtualenv by
running `source env/bin/activate`, and then:
[gratipay] $ cd tests/
[gratipay] $ honcho run -e defaults.env,local.env py.test
Expand Down
62 changes: 56 additions & 6 deletions gratipay/testing/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import atexit
import os
import time

from splinter.browser import _DRIVERS

Expand All @@ -13,6 +14,14 @@
from .harness import Harness


# starting a browser is expensive, so we do so lazily, and once
_browser = None


class NeverLeft(Exception): pass
class NeverShowedUp(Exception): pass


class BrowserHarness(Harness):
"""This is a harness for through-the-web (TTW) testing. It passes
everything through to an underlying `Splinter`_ browser, with the following
Expand All @@ -28,13 +37,13 @@ class BrowserHarness(Harness):

@classmethod
def setUpClass(cls):
global _browser
super(BrowserHarness, cls).setUpClass()

# starting a browser is expensive, so we do so lazily, and once
if cls._browser is None:
if _browser is None:
DriverClass = _DRIVERS[os.environ['WEBDRIVER_BROWSER']]
cls._browser = DriverClass()
atexit.register(cls._browser.quit)
_browser = DriverClass()
atexit.register(_browser.quit)
cls._browser = _browser

def setUp(self):
Harness.setUp(self)
Expand Down Expand Up @@ -82,10 +91,51 @@ def has_element(self, selector, timeout=None):
"""
return self.is_element_present_by_css(selector, timeout)

def wait_to_disappear(self, selector, timeout=2):
"""Wait up to ``timeout`` seconds for element specified by ``selector``
to disappear, returning ``None``.
"""
end_time = time.time() + timeout
while time.time() < end_time:
if not self.has_element(selector):
return
raise NeverLeft(selector)

def wait_for(self, selector, timeout=2):
"""Wait up to ``timeout`` seconds for element specified by ``selector``
to appear, returning the element.
"""
end_time = time.time() + timeout
while time.time() < end_time:
if self.has_element(selector):
return self.find_by_css(selector)
raise NeverShowedUp(selector)

def wait_for_notification(self, type='notice'):
"""Wait for a certain ``type`` of notification. Dismiss the
notification and return the message.
"""
n_selector = '.notifications-fixed .notification-{}'.format(type)
m_selector = 'span.btn-close'
notification = self.wait_for(n_selector).first
message = notification.find_by_css('div').html
notification.find_by_css(m_selector).first.click()
self.wait_to_disappear(n_selector + ' ' + m_selector)
return message

def wait_for_success(self):
"""Wait for a success notification. Dismiss it and return the message.
"""
return self.wait_for_notification('success')

def wait_for_error(self):
"""Wait for an error notification. Dismiss it and return the message.
"""
return self.wait_for_notification('error')

def __getattr__(self, name):
try:
out = self.__getattribute__(name)
except AttributeError:
out = getattr(self._browser, name)
return out

2 changes: 1 addition & 1 deletion js/gratipay/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Gratipay.notification = function(text, type, timeout, closeCallback) {
$('#notification-area .notifications-fixed').prepend($dialog.get(1));

function close() {
$dialog.fadeOut(null, $dialog.remove);
$dialog.fadeOut(null, function() { $dialog.remove(); delete $dialog; });
if (closeCallback) closeCallback();
}

Expand Down
2 changes: 1 addition & 1 deletion requirements.dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
./vendor/wrapt-1.10.2.tar.gz
./vendor/vcrpy-1.1.3.tar.gz

./vendor/selenium-2.53.6.tar.gz
./vendor/selenium-3.3.1.tar.gz
./vendor/splinter-0.7.3.tar.gz

./vendor/imagesize-0.7.1.tar.gz
Expand Down
14 changes: 14 additions & 0 deletions tests/py/test_testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

import traceback

from gratipay.testing.browser import NeverLeft


def test_fail_repr():
try:
raise NeverLeft('.some.selector')
except:
formatted = traceback.format_exc()
assert 'NeverLeft: .some.selector' in formatted
2 changes: 1 addition & 1 deletion tests/test.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DATABASE_URL="dbname=gratipay-test"
BASE_URL=
WEBDRIVER_BROWSER="phantomjs"
WEBDRIVER_BROWSER="firefox"
WEBDRIVER_BASE_URL="http://localhost:8537"
UPDATE_HOMEPAGE_EVERY=0
CHECK_DB_EVERY=0
Expand Down
26 changes: 14 additions & 12 deletions tests/ttw/test_team_distributing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import absolute_import, division, print_function, unicode_literals

import time
from gratipay.testing import BrowserHarness, D,P
from selenium.webdriver.common.keys import Keys


class Tests(BrowserHarness):
Expand All @@ -20,38 +20,40 @@ def test_owner_can_add_a_member(self):
self.sign_in('picard')
self.visit('/TheEnterprise/distributing/')
self.css('.lookup-container .query').first.fill('alice')
time.sleep(0.3)
self.css('.lookup-container button').first.click()
time.sleep(0.3)
self.wait_for('table.team a')
assert [a.text for a in self.css('table.team a')] == ['alice']


def set_take(self, amount):
el = self.css('table.team form.edit input').first
el.fill(amount)
el.type(Keys.ENTER)


def test_member_can_set_their_take(self):
self.enterprise.add_member(self.alice, P('picard'))
self.sign_in('alice')
self.visit('/TheEnterprise/distributing/')
self.css('table.team form.edit input').first.fill('5.37\n')
time.sleep(0.1)
self.set_take('5.37')
assert self.wait_for_success() == 'Your take is now $5.37.'
assert self.enterprise.get_take_for(self.alice) == D('5.37')


def test_member_can_set_their_take_again(self):
self.test_member_can_set_their_take()
self.css('table.team form.edit input').first.fill('100.00\n')
time.sleep(0.1)
self.set_take('100')
assert self.wait_for_success() == 'Your take is now $100.00.'
assert self.enterprise.get_take_for(self.alice) == D('100.00')


def test_owner_can_remove_a_member(self):
self.enterprise.add_member(self.alice, P('picard'))
self.sign_in('picard')
self.visit('/TheEnterprise/distributing/')

self.css('table.team span.remove').first.click()
time.sleep(0.1)
self.css('.modal .yes').first.click()
time.sleep(0.1)

self.wait_for('.modal .yes').first.click()
assert self.wait_for_success() == 'alice has been removed from the team.'
assert self.enterprise.get_memberships() == []


Expand Down
Binary file removed vendor/selenium-2.53.6.tar.gz
Binary file not shown.
Binary file added vendor/selenium-3.3.1.tar.gz
Binary file not shown.

0 comments on commit f9eb6df

Please sign in to comment.