Skip to content

Commit

Permalink
Merge branch 'fix-test-browser'
Browse files Browse the repository at this point in the history
  • Loading branch information
koterpillar committed Nov 1, 2016
2 parents e408fbd + 3b22baa commit 58e9274
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 113 deletions.
25 changes: 6 additions & 19 deletions .gitignore
@@ -1,23 +1,10 @@
*.egg
*.egg-info
*.pyc
*$py.class
*.pt.py
*.txt.py
*~
*.egg-info
geckodriver.log
ghostdriver.log
.coverage
.tox/
nosetests.xml
pyramid/coverage.xml
tutorial.db
env26/
env26-debug/
bookenv/
env24/
env27/
jyenv/
pypyenv/
build/
dist/
/build/
/dist/
/docs/_build
/htmlcov
/aloe_webdriver/tests/features/tmp*.feature
15 changes: 10 additions & 5 deletions .travis.yml
@@ -1,11 +1,16 @@
sudo: required
dist: trusty
language: python
python:
- '2.7'
- '3.4'
- '3.5'
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
env:
- BROWSER=chrome
- BROWSER=firefox
- BROWSER=phantomjs
services:
- docker
install:
- pip install -r requirements.txt
- pip install -r test_requirements.txt
Expand All @@ -14,7 +19,7 @@ script:
- pep8 .
- pylint aloe_webdriver setup.py
- coverage erase
- coverage run -m unittest -v -b
- ./tools/with_docker_browser $BROWSER coverage run -m unittest discover -v -b
- coverage report
- "./tools/check_installs"
- make -C docs coverage
Expand All @@ -24,7 +29,7 @@ deploy:
provider: pypi
user: koterpillar
password:
secure: b/awXsEYkij1/UBekBw39e3+wcImSgh6DmIqz55pP3DzND8ydKQAwExYPrvt2OHOaMYfUH1iokl8HoMnll1ho71Vr3EV4ivpze2xeG2d7Cf+fMWGsQQhN7ZdNuJgZg1jceys4PuHpVipwTVIc3LdJloYkBeb/odI4J8bkfEa+yOfwElQmJ0VDYu3IXJQIccHXmy1W1G0OWYRprhtL9DC3WESPZe4Fv4uhE227WKy1FCLycveRgv5JEtV7qiyysLMbrwg4wSdI2Y9t3wFH7AkRJJY3A1yW2Hgcw/PBOt+NJ3naQm6PJi3zc+E+gU5xrqKSJnnY/v177aOPPLzRXzb3XJjH7UNzPLPQC+sfgIQfg71SUxoexP7q6yMarlS3oaEzkk/Csw/2cCQzcKc4uY3yiP8WY/CwlezZMwD4DYi5RVXc29Yv97bp+xx8ilvraF+ZOoDeRyxI/OD11eKqOlOfn6GA0fBRWbpM7Jg4Rut6X6xQkYOHp8WaCmXZZ6OF9wEgNPvQttFbJf2nF2pDxX0g8vRMOIybXZx1zr6ZNzOkAjdVcxATT9BbWEoBsZ6/FGdLGFKeNg7GzoNZiON7Hu3n0F6m5dF555uTZG1ht6+Q3N2LLQjlrXNQGaSSkn+/oAO11Q06a/5dYwUdZ1C/+win8RNr/RC+SZnUfbi3CknZas=
secure: b/awXsEYkij1/UBekBw39e3+wcImSgh6DmIqz55pP3DzNDFix testing8ydKQAwExYPrvt2OHOaMYfUH1iokl8HoMnll1ho71Vr3EV4ivpze2xeG2d7Cf+fMWGsQQhN7ZdNuJgZg1jceys4PuHpVipwTVIc3LdJloYkBeb/odI4J8bkfEa+yOfwElQmJ0VDYu3IXJQIccHXmy1W1G0OWYRprhtL9DC3WESPZe4Fv4uhE227WKy1FCLycveRgv5JEtV7qiyysLMbrwg4wSdI2Y9t3wFH7AkRJJY3A1yW2Hgcw/PBOt+NJ3naQm6PJi3zc+E+gU5xrqKSJnnY/v177aOPPLzRXzb3XJjH7UNzPLPQC+sfgIQfg71SUxoexP7q6yMarlS3oaEzkk/Csw/2cCQzcKc4uY3yiP8WY/CwlezZMwD4DYi5RVXc29Yv97bp+xx8ilvraF+ZOoDeRyxI/OD11eKqOlOfn6GA0fBRWbpM7Jg4Rut6X6xQkYOHp8WaCmXZZ6OF9wEgNPvQttFbJf2nF2pDxX0g8vRMOIybXZx1zr6ZNzOkAjdVcxATT9BbWEoBsZ6/FGdLGFKeNg7GzoNZiON7Hu3n0F6m5dF555uTZG1ht6+Q3N2LLQjlrXNQGaSSkn+/oAO11Q06a/5dYwUdZ1C/+win8RNr/RC+SZnUfbi3CknZas=
server: https://pypi.python.org/pypi
on:
tags: true
Expand Down
36 changes: 30 additions & 6 deletions aloe_webdriver/css.py
Expand Up @@ -42,12 +42,25 @@ def load_script(browser, url):
document.getElementsByTagName("head")[0].appendChild(script_tag);
""", url)

sleep(1)


JQUERY = '//ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js'


def is_jquery_not_defined_error(msg):
"""
Check whether the JavaScript error message is due to jQuery not
being available.
"""

# Firefox: '$ is not defined'
# Chrome: 'unknown error: $ is not defined'
# PhantomJS: JSON with 'Can't find variable: $'
return any(txt in msg for txt in (
"$ is not defined",
"Can't find variable: $",
))


def load_jquery(func):
"""
A decorator to ensure a function is run with jQuery available.
Expand All @@ -65,12 +78,23 @@ def wrapped(browser, *args, **kwargs):
try:
return func(browser, *args, **kwargs)
except WebDriverException as ex:
if ex.msg.startswith('$ is not defined'):
load_script(browser, JQUERY)
return func(browser, *args, **kwargs)
else:
if not is_jquery_not_defined_error(ex.msg):
raise

load_script(browser, JQUERY)

@wait_for
def jquery_available():
"""Assert that jQuery has loaded."""
try:
return browser.execute_script('return $')
except WebDriverException:
raise AssertionError("jQuery is not loaded")

jquery_available()

return func(browser, *args, **kwargs)

return wrapped


Expand Down
138 changes: 137 additions & 1 deletion aloe_webdriver/tests/base.py
Expand Up @@ -3,7 +3,15 @@
"""

import os
import socketserver
import threading
import unittest
from contextlib import contextmanager
from functools import wraps
from http.server import SimpleHTTPRequestHandler
from time import sleep

from selenium import webdriver

from aloe.testing import in_directory

Expand Down Expand Up @@ -33,11 +41,20 @@ def outer(func):
def inner(self):
"""Run the scenario from docstring."""

scenario = func.__doc__

# Make it possible to reference SERVER_HOST in URLs inside
# scenarios
scenario = scenario.replace(
'SERVER_HOST',
os.environ.get('SERVER_HOST', '0.0.0.0')
)

feature_string = """
Feature: {name}
Scenario: {name}
{scenario_string}
""".format(name=func.__name__, scenario_string=func.__doc__)
""".format(name=func.__name__, scenario_string=scenario)

result = self.run_feature_string(feature_string)

Expand All @@ -49,3 +66,122 @@ def inner(self):
return inner

return outer


class TestRequestHandler(SimpleHTTPRequestHandler):
"""A handler serving the test pages."""

def translate_path(self, path):
"""Serve the pages directory instead of the current directory."""

pages_dir = os.path.relpath(
os.path.join(os.path.dirname(__file__), 'html_pages'))

return SimpleHTTPRequestHandler.translate_path(
self, '/' + pages_dir + path)

def do_GET(self):
"""
Artificially slow down the response to make sure there are no race
conditions.
"""

sleep(0.5)

return SimpleHTTPRequestHandler.do_GET(self)

def log_message(self, *args, **kwargs):
"""Turn off logging."""
pass


class TestServer(socketserver.TCPServer):
"""Server for the test pages."""

allow_reuse_address = True

def get_request(self):
"""Set a timeout on the request socket."""

request, addr = socketserver.TCPServer.get_request(self)
request.settimeout(2) # pylint:disable=no-member
return request, addr


@contextmanager
def test_server():
"""A context manager starting a server for the test pages."""

port = 7755

server = TestServer(('', port), TestRequestHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.start()

# When running the browser in Docker, pass the host address
# to allow the container to access the server on the host
if 'SERVER_HOST' in os.environ:
address = (os.environ['SERVER_HOST'], port)
else:
address = server.server_address

yield server, address

server.shutdown()
server_thread.join()
server.server_close()


def browser_type():
"""Browser type selected for the tests."""

return os.environ.get('BROWSER_TYPE', 'firefox')


def skip_if_browser(browsers, message):
"""Decorator to skip a test with a particular browser type."""

if not isinstance(browsers, (list, tuple)):
browsers = [browsers]

if browser_type() in browsers:
return unittest.skip(message)
return lambda func: func


def create_browser():
"""Create a Selenium browser for tests."""

if 'SELENIUM_ADDRESS' in os.environ:
address = 'http://{}/wd/hub'.format(os.environ['SELENIUM_ADDRESS'])

capabilities = {
'chrome': webdriver.DesiredCapabilities.CHROME,
'firefox': webdriver.DesiredCapabilities.FIREFOX,
'phantomjs': webdriver.DesiredCapabilities.PHANTOMJS,
}
try:
browser = capabilities[browser_type()]
except KeyError:
raise ValueError("Invalid BROWSER_TYPE.")

return webdriver.Remote(
address,
desired_capabilities=browser,
)

browsers = {
'chrome': webdriver.Chrome,
'firefox': webdriver.Firefox,
'phantomjs': webdriver.PhantomJS,
}
driver = browsers[browser_type()]

# Explicitly specify the browser locale for the date input tests to work
# regardless of the user's settings
old_lc_all = os.environ.get('LC_ALL', '')
try:
os.environ['LC_ALL'] = 'en_US'
return driver()
finally:
os.environ['LC_ALL'] = old_lc_all

0 comments on commit 58e9274

Please sign in to comment.