[Checkins] SVN: zope.testbrowser/tags/3.10.3/ Added `wsgi_intercept` support (came from ``zope.app.wsgi.testlayer``).

Michael Howitz mh at gocept.com
Mon Jan 24 09:03:18 EST 2011


Log message for revision 119872:
  Added `wsgi_intercept` support (came from ``zope.app.wsgi.testlayer``).
  
  

Changed:
  U   zope.testbrowser/tags/3.10.3/CHANGES.txt
  U   zope.testbrowser/tags/3.10.3/setup.py
  U   zope.testbrowser/tags/3.10.3/src/zope/testbrowser/README.txt
  A   zope.testbrowser/tags/3.10.3/src/zope/testbrowser/tests/test_wsgi.py
  A   zope.testbrowser/tags/3.10.3/src/zope/testbrowser/wsgi.py

-=-
Modified: zope.testbrowser/tags/3.10.3/CHANGES.txt
===================================================================
--- zope.testbrowser/tags/3.10.3/CHANGES.txt	2011-01-24 13:35:14 UTC (rev 119871)
+++ zope.testbrowser/tags/3.10.3/CHANGES.txt	2011-01-24 14:03:18 UTC (rev 119872)
@@ -2,16 +2,22 @@
 CHANGES
 =======
 
+3.11.0 (unreleased)
+-------------------
+
+- Added `wsgi_intercept` support (came from ``zope.app.wsgi.testlayer``).
+
+
 3.10.3 (2010-10-15)
 -------------------
 
-- Fixed backwards compatibility with zope.app.wsgi.testlayer.
+- Fixed backwards compatibility with ``zope.app.wsgi.testlayer``.
 
 
 3.10.2 (2010-10-15)
 -------------------
 
-- Fixed Python2.7 compatibility in Browser.handleErrors.
+- Fixed Python 2.7 compatibility in Browser.handleErrors.
 
 
 3.10.1 (2010-09-21)

Modified: zope.testbrowser/tags/3.10.3/setup.py
===================================================================
--- zope.testbrowser/tags/3.10.3/setup.py	2011-01-24 13:35:14 UTC (rev 119871)
+++ zope.testbrowser/tags/3.10.3/setup.py	2011-01-24 14:03:18 UTC (rev 119872)
@@ -74,6 +74,9 @@
         'zope-functional-testing': [
             'zope.app.testing',
             ],
+        'wsgi': [
+            'wsgi_intercept',
+            ]
         },
     include_package_data = True,
     zip_safe = False,

Modified: zope.testbrowser/tags/3.10.3/src/zope/testbrowser/README.txt
===================================================================
--- zope.testbrowser/tags/3.10.3/src/zope/testbrowser/README.txt	2011-01-24 13:35:14 UTC (rev 119871)
+++ zope.testbrowser/tags/3.10.3/src/zope/testbrowser/README.txt	2011-01-24 14:03:18 UTC (rev 119872)
@@ -2,6 +2,12 @@
 Detailed Documentation
 ======================
 
+Different Browsers
+------------------
+
+HTTP Browser
+~~~~~~~~~~~~
+
 The ``zope.testbrowser.browser`` module exposes a ``Browser`` class that
 simulates a web browser similar to Mozilla Firefox or IE.
 
@@ -11,6 +17,38 @@
 This version of the browser object can be used to access any web site just as
 you would do using a normal web browser.
 
+WSGI Test Browser
+~~~~~~~~~~~~~~~~~
+
+There is also a special version of the ``Browser`` class which uses
+`wsgi_intercept`_ and can be used to do functional testing of WSGI
+applications, it can be imported from ``zope.testbrowser.wsgi``:
+
+    >>> from zope.testbrowser.wsgi import Browser
+    >>> browser = Browser()
+
+.. _`wsgi_intercept`: http://pypi.python.org/pypi/wsgi_intercept
+
+To use this browser you have to:
+
+  * use the `wsgi` extra of the ``zope.testbrowser`` egg,
+
+  * write a subclass of ``zope.testbrowser.wsgi.Layer`` and override the
+    ``make_wsgi_app`` method,
+
+  * use an instance of the class as the test layer of your test.
+
+Example:
+
+    >>> class SimpleLayer(zope.testbrowser.wsgi.Layer):
+    ...     def make_wsgi_app(self):
+    ...         return simple_app
+
+Where ``simple_app`` is the callable of your WSGI application.
+
+Zope 3 Test Browser
+~~~~~~~~~~~~~~~~~~~
+
 There is also a special version of the ``Browser`` class used to do functional
 testing of Zope 3 applications, it can be imported from
 ``zope.testbrowser.testing``:
@@ -18,8 +56,12 @@
     >>> from zope.testbrowser.testing import Browser
     >>> browser = Browser()
 
-An initial page to load can be passed to the ``Browser`` constructor:
+Bowser Usage
+------------
 
+All browsers are used the same way.  An initial page to load can be passed
+to the ``Browser`` constructor:
+
     >>> browser = Browser('http://localhost/@@/testbrowser/simple.html')
     >>> browser.url
     'http://localhost/@@/testbrowser/simple.html'

Added: zope.testbrowser/tags/3.10.3/src/zope/testbrowser/tests/test_wsgi.py
===================================================================
--- zope.testbrowser/tags/3.10.3/src/zope/testbrowser/tests/test_wsgi.py	                        (rev 0)
+++ zope.testbrowser/tags/3.10.3/src/zope/testbrowser/tests/test_wsgi.py	2011-01-24 14:03:18 UTC (rev 119872)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2011 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import unittest
+import zope.testbrowser.wsgi
+
+
+# Copied from PEP #333
+def simple_app(environ, start_response):
+    """Simplest possible application object"""
+    status = '200 OK'
+    response_headers = [('Content-type', 'text/plain')]
+    start_response(status, response_headers)
+    return ['Hello world!\n']
+
+
+class SimpleLayer(zope.testbrowser.wsgi.Layer):
+
+    def make_wsgi_app(self):
+        return simple_app
+
+SIMPLE_LAYER = SimpleLayer()
+
+
+class TestWSGI(unittest.TestCase):
+
+    layer = SIMPLE_LAYER
+
+    def test_(self):
+        browser = zope.testbrowser.wsgi.Browser()
+        browser.open('http://localhost')
+        self.assertEqual('Hello world!\n', browser.contents)
+        # XXX test for authorization header munging is missing


Property changes on: zope.testbrowser/tags/3.10.3/src/zope/testbrowser/tests/test_wsgi.py
___________________________________________________________________
Added: svn:keywords
   + Id Rev Date
Added: svn:eol-style
   + native

Copied: zope.testbrowser/tags/3.10.3/src/zope/testbrowser/wsgi.py (from rev 119868, zope.app.wsgi/trunk/src/zope/app/wsgi/testlayer.py)
===================================================================
--- zope.testbrowser/tags/3.10.3/src/zope/testbrowser/wsgi.py	                        (rev 0)
+++ zope.testbrowser/tags/3.10.3/src/zope/testbrowser/wsgi.py	2011-01-24 14:03:18 UTC (rev 119872)
@@ -0,0 +1,129 @@
+##############################################################################
+#
+# Copyright (c) 2010-2011 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+import base64
+import re
+import wsgi_intercept
+import wsgi_intercept.mechanize_intercept
+import zope.testbrowser.browser
+
+
+# List of hostname where the test browser/http function replies to
+TEST_HOSTS = ['localhost', '127.0.0.1']
+
+
+class InterceptBrowser(wsgi_intercept.mechanize_intercept.Browser):
+
+    default_schemes = ['http']
+    default_others = ['_http_error',
+                      '_http_default_error']
+    default_features = ['_redirect', '_cookies', '_referer', '_refresh',
+                        '_equiv', '_basicauth', '_digestauth']
+
+
+class Browser(zope.testbrowser.browser.Browser):
+    """Override the zope.testbrowser.browser.Browser interface so that it
+    uses InterceptBrowser.
+    """
+
+    def __init__(self, *args, **kw):
+        kw['mech_browser'] = InterceptBrowser()
+        super(Browser, self).__init__(*args, **kw)
+
+
+# Compatibility helpers to behave like zope.app.testing
+
+basicre = re.compile('Basic (.+)?:(.+)?$')
+
+
+def auth_header(header):
+    """This function takes an authorization HTTP header and encode the
+    couple user, password into base 64 like the HTTP protocol wants
+    it.
+    """
+    match = basicre.match(header)
+    if match:
+        u, p = match.group(1, 2)
+        if u is None:
+            u = ''
+        if p is None:
+            p = ''
+        auth = base64.encodestring('%s:%s' % (u, p))
+        return 'Basic %s' % auth[:-1]
+    return header
+
+
+def is_wanted_header(header):
+    """Return True if the given HTTP header key is wanted.
+    """
+    key, value = header
+    return key.lower() not in ('x-content-type-warning', 'x-powered-by')
+
+
+class AuthorizationMiddleware(object):
+    """This middleware makes the WSGI application compatible with the
+    HTTPCaller behavior defined in zope.app.testing.functional:
+    - It modifies the HTTP Authorization header to encode user and
+      password into base64 if it is Basic authentication.
+    """
+
+    def __init__(self, wsgi_stack):
+        self.wsgi_stack = wsgi_stack
+
+    def __call__(self, environ, start_response):
+        # Handle authorization
+        auth_key = 'HTTP_AUTHORIZATION'
+        if auth_key in environ:
+            environ[auth_key] = auth_header(environ[auth_key])
+
+        # Remove unwanted headers
+        def application_start_response(status, headers, exc_info=None):
+            headers = filter(is_wanted_header, headers)
+            start_response(status, headers)
+
+        for entry in self.wsgi_stack(environ, application_start_response):
+            yield entry
+
+
+class Layer(object):
+    """Test layer which sets up WSGI application for use with
+    wsgi_intercept/testbrowser.
+
+    """
+
+    __bases__ = ()
+    __name__ = 'Layer'
+
+    def make_wsgi_app(self):
+        # Override this method in subclasses of this layer in order to set up
+        # the WSGI application.
+        raise NotImplementedError
+
+    def cooperative_super(self, method_name):
+        # Calling `super` for multiple inheritance:
+        method = getattr(super(Layer, self), method_name, None)
+        if method is not None:
+            method()
+
+    def setUp(self):
+        self.cooperative_super('setUp')
+        self.app = self.make_wsgi_app()
+        factory = lambda: AuthorizationMiddleware(self.app)
+
+        for host in TEST_HOSTS:
+            wsgi_intercept.add_wsgi_intercept(host, 80, factory)
+
+    def tearDown(self):
+        for host in TEST_HOSTS:
+            wsgi_intercept.remove_wsgi_intercept(host, 80)
+        self.cooperative_super('tearDown')



More information about the checkins mailing list