Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

fix matching string in _lxml finder of single ids

setup dev version link
add screenshot plugin to package
add tests for screenshot
refactor cookie checking, fix tests, document policy
fixes to ensure setup.py tests work correctly remove dead import
  • Loading branch information...
commit 4812e6479205d4048316bfccc52ae3478b49964c 1 parent 9bc7dc2
Dan Colish dcolish authored
2  alfajor/browsers/_lxml.py
View
@@ -199,7 +199,7 @@ def __getitem__(self, key):
if not isinstance(key, basestring):
return super(DOMElement, self).__getitem__(key)
# '#foo'? (and not '#foo li')
- if _single_id_selector.match('#'):
+ if _single_id_selector.match(key):
try:
return self.get_element_by_id(key[1:])
except KeyError:
13 alfajor/browsers/network.py
View
@@ -81,12 +81,12 @@ def cookies(self):
return {}
request = urllib2.Request(self.location)
policy = self._cookie_jar._policy
- return dict(
- ((cookie.name,
- cookie.value[1:-1] if cookie.value.startswith('"') else cookie.value)
- for cookie in self._cookie_jar
- if policy.return_ok(cookie, request))
- )
+
+ # return ok will only return a cookie if the following attrs are set
+ # correctly => # "version", "verifiability", "secure", "expires",
+ # "port", "domain"
+ return dict((c.name, c.value.strip('"'))
+ for c in self._cookie_jar if policy.return_ok(c, request))
def set_cookie(self, name, value, domain=None, path=None,
session=True, expires=None, port=None):
@@ -167,4 +167,3 @@ def _open(self, url, method='GET', data=None, refer=True,
open_ended - open_started - request_time)
after_browser_activity.send(self)
-
38 alfajor/browsers/selenium.py
View
@@ -16,6 +16,7 @@
import time
from urllib2 import urlopen, Request
from urlparse import urljoin
+from warnings import warn
from blinker import signal
from werkzeug import UserAgent, url_encode
@@ -42,6 +43,9 @@
after_browser_activity = signal('after_browser_activity')
before_browser_activity = signal('before_browser_activity')
_enterable_chars_re = re.compile(r'(\\[a-z]|\\\d+|.)')
+csv.register_dialect('cookies', delimiter=';',
+ skipinitialspace=True,
+ quoting=csv.QUOTE_NONE)
class Selenium(DOMMixin):
@@ -139,22 +143,15 @@ def wait_for(self, condition, timeout=None):
@property
def cookies(self):
"""A dictionary of cookie names and values."""
- # TODO:jek: pass this off to the driver? let it use a customized csv
- # reader to split & unpack?
- cookie_strings = self.selenium('getCookie').split('; ')
- cookies = dict()
- for cookie_string in cookie_strings:
- if not cookie_string:
- continue
- key, val = cookie_string.split('=', 1)
- cookies[key] = val.strip('"')
- return cookies
-
- def set_cookie(self, name, value, domain=None, path=None):
+ return self.selenium('getCookie', dict=True)
+
+ def set_cookie(self, name, value, domain=None, path=None, max_age=None,
+ session=None, expires=None, port=None):
+ if domain or session or expires or port:
+ message = "Selenium Cookies support only path and max_age"
+ warn(message, UserWarning)
cookie_string = '%s=%s' % (name, value)
- options_string = ''
- if path:
- options_string += 'path=%s' % path
+ options_string = '' if not path else 'path=%s' % path
self.selenium('createCookie', cookie_string, options_string)
def delete_cookie(self, name, domain=None, path=None):
@@ -197,6 +194,7 @@ def test_complete(self):
def __call__(self, command, *args, **kw):
transform = _transformers[kw.pop('transform', 'unicode')]
return_list = kw.pop('list', False)
+ return_dict = kw.pop('dict', False)
assert not kw, 'Unknown keyword argument.'
payload = {'cmd': command, 'sessionId': self._session_id}
@@ -218,6 +216,15 @@ def __call__(self, command, *args, **kw):
if return_list:
rows = list(csv.reader(StringIO(data)))
return [transform(col) for col in rows[0]]
+
+ elif return_dict:
+ rows = list(csv.reader(StringIO(data), 'cookies'))
+
+ if rows:
+ return dict(
+ map(lambda x: x.strip('"'), x.split('=')) for x in rows[0])
+ else:
+ return {}
else:
return transform(data)
@@ -312,6 +319,7 @@ def event_sender(name):
def handler(self, wait_for=None, timeout=None):
before_browser_activity.send(self.browser)
self.browser.selenium(selenium_name, self._locator)
+ # XXX:dc: when would a None wait_for be a good thing?
if wait_for:
self.browser.wait_for(wait_for, timeout)
time.sleep(0.2)
30 alfajor/browsers/wsgi.py
View
@@ -115,21 +115,25 @@ def cookies(self):
request = urllib2.Request(self.location)
policy = self._cookie_jar._policy
policy._now = int(time())
- return dict(
- ((cookie.name,
- cookie.value[1:-1] if cookie.value.startswith('"') else cookie.value)
- for cookie in self._cookie_jar
- if policy.return_ok(cookie, request))
- )
- def set_cookie(self, name, value, domain=None, path=None,
- session=True, expires=None, port=None):
-# Cookie(version, name, value, port, port_specified,
-# domain, domain_specified, domain_initial_dot,
-# path, path_specified, secure, expires,
-# discard, comment, comment_url, rest,
-# rfc2109=False):
+ # return ok will only return a cookie if the following attrs are set
+ # correctly => # "version", "verifiability", "secure", "expires",
+ # "port", "domain"
+ return dict((c.name, c.value.strip('"'))
+ for c in self._cookie_jar if policy.return_ok(c, request))
+ def set_cookie(self, name, value, domain=None, path=None,
+ session=True, expires=None, port=None, request=None):
+ """
+ :param expires: Seconds from epoch
+ :param port: must match request port
+ :param domain: the fqn of your server hostname
+ """
+ # Cookie(version, name, value, port, port_specified,
+ # domain, domain_specified, domain_initial_dot,
+ # path, path_specified, secure, expires,
+ # discard, comment, comment_url, rest,
+ # rfc2109=False):
cookie = Cookie(0, name, value, port, bool(port),
domain or '', bool(domain),
(domain and domain.startswith('.')),
67 alfajor/runners/nose.py
View
@@ -7,8 +7,10 @@
"""Integration with the 'nose' test runner."""
from __future__ import absolute_import
+from base64 import b64decode
from logging import getLogger
from optparse import OptionGroup
+from os import path
from nose.plugins.base import Plugin
@@ -22,10 +24,11 @@ class Alfajor(Plugin):
name = 'alfajor'
enabled = True # FIXME
+ alfajor_enabled_screenshot = False
def __init__(self):
Plugin.__init__(self)
- self._contexts = {}
+ self._contexts = []
def options(self, parser, env):
group = OptionGroup(parser, "Alfajor options")
@@ -70,6 +73,19 @@ def options(self, parser, env):
'[ALFAJOR_SERVER_URL]')
parser.add_option_group(group)
+ group = OptionGroup(parser, "Alfajor Screenshot Options")
+ group.add_option(
+ "--screenshot", action="store_true",
+ dest="alfajor_enabled_screenshot",
+ default=env.get('ALFAJOR_SCREENSHOT', False),
+ help="Take screenshots of failed pages")
+ group.add_option(
+ "--screenshot-dir",
+ dest="alfajor_screenshot_dir",
+ default=env.get('ALFAJOR_SCREENSHOT_DIR', ''),
+ help="Dir to store screenshots")
+ parser.add_option_group(group)
+
def configure(self, options, config):
Plugin.configure(self, options, config)
alfajor_options = {}
@@ -105,13 +121,46 @@ def startContext(self, context):
managers.add((manager, declaration))
declaration.proxy._factory = manager.create
if managers:
- self._contexts[context] = managers
+ self._contexts.append((context, managers))
def stopContext(self, context):
- if context not in self._contexts:
- return
- managers = self._contexts.pop(context)
- for manager, declaration in managers:
- manager.destroy()
- declaration.proxy._instance = None
- declaration.proxy._factory = None
+ # self._contexts is a list of tuples, [0] is the context key
+ if self._contexts and context == self._contexts[-1][0]:
+ key, managers = self._contexts.pop(-1)
+ for manager, declaration in managers:
+ manager.destroy()
+ declaration.proxy._instance = None
+ declaration.proxy._factory = None
+
+ def addError(self, test, err):
+ instance = None
+ if self._contexts:
+ context, managers = self._contexts[-1]
+ if not managers:
+ return
+ manager, declaration = managers.pop()
+ instance = declaration.proxy._instance
+ if (self.options['enabled_screenshot'] and
+ hasattr(instance, 'selenium')):
+ self.screenshot(instance, test)
+
+ def addFailure(self, test, err):
+ instance = None
+ if self._contexts:
+ contexts, managers = self._contexts[-1]
+ if not managers:
+ return
+ manager, declaration = managers.pop()
+ instance = declaration.proxy._instance
+ if (self.options['enabled_screenshot'] and
+ hasattr(instance, 'selenium')):
+ self.screenshot(instance, test)
+
+ def screenshot(self, instance, test):
+ test_name = test.id().split('.')[-1]
+ directory = self.options['screenshot_dir']
+ output_file = open('/'.join(
+ [path.abspath(directory), test_name + '.png']), "w")
+ img = instance.selenium.capture_entire_page_screenshot_to_string()
+ output_file.write(b64decode(img))
+ output_file.close()
6 setup.cfg
View
@@ -0,0 +1,6 @@
+[aliases]
+test = nosetests
+
+[nosetests]
+detailed-errors=1
+
8 setup.py
View
@@ -34,6 +34,12 @@
and web api implementations at a fine-grained level.
- A friendly BSD license.
+
+An `in-development`_ version is also available
+
+.. _`in-development`: http://github.com/idealist/alfajor/zipball/master#egg=alfajor-dev
+
+
"""
from setuptools import setup, find_packages
@@ -86,6 +92,6 @@
'blinker',
],
tests_require=[
- 'nose == 0.11.3',
+ 'nose',
],
)
35 tests/browser/__init__.py
View
@@ -4,8 +4,43 @@
# This file is part of 'alfajor' and is distributed under the BSD license.
# See LICENSE for more details.
+import os
+
from alfajor import WebBrowser
+from nose.tools import with_setup
browser = WebBrowser()
browser.configure_in_scope('self-tests')
+
+
+def setup_fn():
+ pass
+
+
+def teardown_fn():
+ browser.reset()
+
+
+def browser_test():
+ def dec(fn):
+ return with_setup(setup_fn, teardown_fn)(fn)
+ return dec
+
+browser_test.__test__ = False
+
+
+def screenshot_fails(file):
+ def dec(fn):
+ def test(*args, **kw):
+ try:
+ fn(*args, **kw)
+ except:
+ if os.path.exists(file):
+ os.remove(file)
+ return True
+ else:
+ return False
+ test.__name__ = fn.__name__
+ return test
+ return dec
2  tests/browser/alfajor.ini
View
@@ -9,7 +9,7 @@ zero=zero
[self-tests+browser.wsgi]
server-entry-point = tests.browser.webapp:webapp()
-base_url = http://localhost
+base_url = http://localhost:8008
[self-tests+browser.network]
cmd = alfajor-invoke tests.browser.webapp:run
64 tests/browser/test_browser.py
View
@@ -3,22 +3,14 @@
#
# This file is part of 'alfajor' and is distributed under the BSD license.
# See LICENSE for more details.
-
-from datetime import datetime
import time
-from nose.tools import raises, with_setup
-
-from . import browser
+from nose.tools import raises
+from . import browser, browser_test, screenshot_fails
-def setup_fn():
- browser.reset()
-
-def teardown_fn():
- pass
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_simple():
browser.open('/')
@@ -37,23 +29,22 @@ def test_simple():
assert browser.document.cssselect('p')[0].text == 'hi there'
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_reset():
# TODO: flesh this out when cookie querying is working and has
# test coverage. until then, just verify that the method doesn't
# explode.
browser.open('/')
- browser.reset()
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_user_agent():
browser.open('/')
ua = browser.user_agent
assert ua['browser'] != 'unknown'
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_traversal():
browser.open('/seq/a')
a_id = browser.document['#request_id'].text
@@ -77,7 +68,7 @@ def test_traversal():
assert '/seq/b' in browser.cssselect('p.referrer')[0].text
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def _test_single_cookie(bounce):
browser.open('/')
assert not browser.cookies
@@ -97,13 +88,13 @@ def _test_single_cookie(bounce):
assert not browser.cookies
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_single_cookie():
yield _test_single_cookie, False
yield _test_single_cookie, True
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def _test_multiple_cookies(bounce):
browser.open('/')
assert not browser.cookies
@@ -124,19 +115,19 @@ def _test_multiple_cookies(bounce):
assert not browser.cookies
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_multiple_cookies():
yield _test_multiple_cookies, False
yield _test_multiple_cookies, True
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_wait_for():
# bare minimum no side-effects call browser.wait_for
browser.wait_for('duration', 1)
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_wait_for_duration():
if 'selenium' in browser.capabilities:
start = time.time()
@@ -145,7 +136,7 @@ def test_wait_for_duration():
assert duration >= 1
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_wait_for_element():
if 'selenium' in browser.capabilities:
browser.open('/waitfor')
@@ -154,8 +145,8 @@ def test_wait_for_element():
assert browser.cssselect('#expected_p')
+@browser_test()
@raises(AssertionError)
-@with_setup(setup_fn, teardown_fn)
def test_wait_for_element_not_found():
if 'selenium' in browser.capabilities:
browser.open('/waitfor')
@@ -164,7 +155,7 @@ def test_wait_for_element_not_found():
raise AssertionError('Ignore if not selenium')
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_wait_for_element_not_present():
if 'selenium' in browser.capabilities:
browser.open('/waitfor')
@@ -174,7 +165,7 @@ def test_wait_for_element_not_present():
assert not browser.cssselect('#removeme')
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_wait_for_ajax():
if 'selenium' in browser.capabilities:
browser.open('/waitfor')
@@ -183,7 +174,7 @@ def test_wait_for_ajax():
assert len(browser.cssselect('.ajaxAdded')) == 3
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_wait_for_js():
if 'selenium' in browser.capabilities:
browser.open('/waitfor')
@@ -191,17 +182,24 @@ def test_wait_for_js():
wait_for='js:window.exampleCount==100;', timeout=3000)
-@with_setup(setup_fn, teardown_fn)
+@browser_test()
def test_set_cookie():
if 'cookies' in browser.capabilities:
browser.open('/')
+
browser.set_cookie('foo', 'bar')
- browser.set_cookie('py', 'py', 'localhost', port='8080')
+ browser.set_cookie('py', 'py', 'localhost.local', port='8008')
browser.set_cookie('green', 'frog',
- session=False, expires=3600)
-
+ session=False, expires=time.time() + 3600)
assert 'foo' in browser.cookies
- # TODO:sw Busted
- #assert 'py' in browser.cookies
- #assert 'green' in browser.cookies
+ assert 'py' in browser.cookies
+ assert 'green' in browser.cookies
+
+@browser_test()
+@screenshot_fails('test_screenshot.png')
+def test_screenshot():
+ if 'javascript' not in browser.capabilities:
+ return
+ browser.open('http://www.google.com')
+ assert False
1  tests/browser/test_forms.py
View
@@ -7,7 +7,6 @@
from alfajor._compat import json_loads as loads
from nose.tools import eq_, raises
-import os.path
from . import browser
Please sign in to comment.
Something went wrong with that request. Please try again.