[Checkins] SVN: zope.testbrowser/trunk/ - Added a zope.testbrowser.testing.Browser.post method that allows

Benji York benji at zope.com
Sun Mar 30 19:49:20 EDT 2008


Log message for revision 85021:
  - Added a zope.testbrowser.testing.Browser.post method that allows
    tests to supply a body and a content type.  This is handy for
    testing Ajax requests with non-form input (e.g. JSON).
  
  - Move tests for fixed bugs to a separate file.
  
  - Removed non-functional and undocumented code intended to help test servers
    using virtual hosting.
  

Changed:
  U   zope.testbrowser/trunk/CHANGES.txt
  U   zope.testbrowser/trunk/buildout.cfg
  U   zope.testbrowser/trunk/setup.py
  U   zope.testbrowser/trunk/src/zope/testbrowser/README.txt
  U   zope.testbrowser/trunk/src/zope/testbrowser/browser.py
  U   zope.testbrowser/trunk/src/zope/testbrowser/fixed-bugs.txt
  U   zope.testbrowser/trunk/src/zope/testbrowser/ftests/__init__.py
  U   zope.testbrowser/trunk/src/zope/testbrowser/ftests/ftesting.zcml
  U   zope.testbrowser/trunk/src/zope/testbrowser/testing.py

-=-
Modified: zope.testbrowser/trunk/CHANGES.txt
===================================================================
--- zope.testbrowser/trunk/CHANGES.txt	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/CHANGES.txt	2008-03-30 23:49:20 UTC (rev 85021)
@@ -2,9 +2,14 @@
 CHANGES
 =======
 
+
 3.5.0 (unreleased)
 ------------------
 
+- Added a zope.testbrowser.testing.Browser.post method that allows
+  tests to supply a body and a content type.  This is handy for
+  testing Ajax requests with non-form input (e.g. JSON).
+
 - Remove vendor import of mechanize.
 
 - Fix bug that caused HTTP exception tracebacks to differ between version 3.4.0
@@ -23,7 +28,12 @@
 - Fix browser.getLink documentation that was not updated since the last API
   modification.
 
+- Move tests for fixed bugs to a separate file.
 
+- Removed non-functional and undocumented code intended to help test servers
+  using virtual hosting.
+
+
 3.4.2 (2007-10-31)
 ------------------
 

Modified: zope.testbrowser/trunk/buildout.cfg
===================================================================
--- zope.testbrowser/trunk/buildout.cfg	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/buildout.cfg	2008-03-30 23:49:20 UTC (rev 85021)
@@ -1,8 +1,11 @@
 [buildout]
 develop = .
 parts = test interpreter
+index = http://download.zope.org/simple
+extends = http://download.zope.org/zope3.4/versions.cfg
 versions = versions
-extends = http://download.zope.org/zope3.4/versions.cfg
+allow-picked-versions = false                                                                                                                                                                                                           
+use-dependency-links = false
 
 [versions]
 zope.testbrowser =
@@ -13,10 +16,9 @@
 recipe = zc.recipe.testrunner
 defaults = ['--tests-pattern', '^f?tests$']
 eggs = zope.testbrowser [test]
-    mechanize
 
 [interpreter]
 recipe = zc.recipe.egg
 eggs = zope.testbrowser
+       mechanize
 interpreter = py
-    mechanize

Modified: zope.testbrowser/trunk/setup.py
===================================================================
--- zope.testbrowser/trunk/setup.py	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/setup.py	2008-03-30 23:49:20 UTC (rev 85021)
@@ -50,9 +50,9 @@
     namespace_packages = ['zope',],
     tests_require = ['zope.testing'],
     install_requires = [
+        'ClientForm',
+        'mechanize',
         'setuptools',
-        'mechanize',
-        'ClientForm',
         'zope.interface',
         'zope.schema',
         ],
@@ -60,9 +60,9 @@
         test = [
             'zope.app.component',
             'zope.app.folder',
+            'zope.app.securitypolicy',
             'zope.app.testing',
             'zope.app.zcmlfiles',
-            'zope.app.securitypolicy',
             ],
         ),
     include_package_data = True,

Modified: zope.testbrowser/trunk/src/zope/testbrowser/README.txt
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/README.txt	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/src/zope/testbrowser/README.txt	2008-03-30 23:49:20 UTC (rev 85021)
@@ -1107,6 +1107,74 @@
     ValueError: if no other arguments are given, index is required.
 
 
+Submitting a posts body directly
+--------------------------------
+
+In addition to the open method, zope.testbrowser.testing.Browser has a ``post``
+method that allows a request body to be supplied.  This method is particularly
+helpful when testing Ajax methods.
+
+Let's visit a page that echos it's request:
+
+    >>> browser.open('http://localhost/@@echo.html')
+    >>> print browser.contents,
+    HTTP_USER_AGENT: Python-urllib/2.4
+    HTTP_CONNECTION: close
+    HTTP_COOKIE:
+    HTTP_REFERER: localhost
+    HTTP_ACCEPT_LANGUAGE: en-US
+    REQUEST_METHOD: GET
+    HTTP_HOST: localhost
+    PATH_INFO: /@@echo.html
+    SERVER_PROTOCOL: HTTP/1.1
+    QUERY_STRING:
+    Body: ''
+
+Now, we'll try a post.  The post method takes a URL, a data string,
+and an optional content type.  If we just pass a string, then
+a URL-encoded query string is assumed:
+
+    >>> browser.post('http://localhost/@@echo.html', 'x=1&y=2')
+    >>> print browser.contents,
+    CONTENT_LENGTH: 7
+    HTTP_USER_AGENT: Python-urllib/2.4
+    HTTP_CONNECTION: close
+    HTTP_COOKIE:
+    HTTP_REFERER: localhost
+    HTTP_ACCEPT_LANGUAGE: en-US
+    y: 2
+    REQUEST_METHOD: POST
+    HTTP_HOST: localhost
+    PATH_INFO: /@@echo.html
+    CONTENT_TYPE: application/x-www-form-urlencoded
+    SERVER_PROTOCOL: HTTP/1.1
+    QUERY_STRING:
+    x: 1
+    Body: ''
+
+
+The body is empty because it is consumed to get form data.
+
+We can pass a content-type explicitly:
+
+    >>> browser.post('http://localhost/@@echo.html',
+    ...              '{"x":1,"y":2}', 'application/x-javascipt')
+    >>> print browser.contents,
+    CONTENT_LENGTH: 13
+    HTTP_USER_AGENT: Python-urllib/2.4
+    HTTP_CONNECTION: close
+    HTTP_COOKIE:
+    HTTP_REFERER: localhost
+    HTTP_ACCEPT_LANGUAGE: en-US
+    REQUEST_METHOD: POST
+    HTTP_HOST: localhost
+    PATH_INFO: /@@echo.html
+    CONTENT_TYPE: application/x-javascipt
+    SERVER_PROTOCOL: HTTP/1.1
+    Body: '{"x":1,"y":2}'
+
+Here, the body is left in place because it isn't form data.
+
 Performance Testing
 -------------------
 
@@ -1215,33 +1283,3 @@
     Traceback (most recent call last):
     ...
     AttributeError: 'Link' object has no attribute 'nonexistant'
-
-
-Fixed Bugs
-----------
-
-This section includes tests for bugs that were found and then fixed that don't
-fit into the more documentation-centric sections above.
-
-Spaces in URL
-~~~~~~~~~~~~~
-
-When URLs have spaces in them, they're handled correctly (before the bug was
-fixed, you'd get "ValueError: too many values to unpack"):
-
-    >>> browser.open('http://localhost/@@/testbrowser/navigate.html')
-    >>> browser.getLink('Spaces in the URL').click()
-
-.goBack() Truncation
-~~~~~~~~~~~~~~~~~~~~
-
-The .goBack() method used to truncate the .contents.
-
-    >>> browser.open('http://localhost/@@/testbrowser/navigate.html')
-    >>> actual_length = len(browser.contents)
-
-    >>> browser.open('http://localhost/@@/testbrowser/navigate.html')
-    >>> browser.open('http://localhost/@@/testbrowser/simple.html')
-    >>> browser.goBack()
-    >>> len(browser.contents) == actual_length
-    True

Modified: zope.testbrowser/trunk/src/zope/testbrowser/browser.py
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/browser.py	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/src/zope/testbrowser/browser.py	2008-03-30 23:49:20 UTC (rev 85021)
@@ -249,6 +249,11 @@
             if self.raiseHttpErrors and code >= 400:
                 raise urllib2.HTTPError(url, code, msg, self.headers, fp=None)
 
+    def post(self, url, data, content_type=None):
+        if content_type is not None:
+            data = {'body': data, 'content-type': content_type}
+        return self.open(url, data)
+
     def _start_timer(self):
         self.timer.start()
 

Modified: zope.testbrowser/trunk/src/zope/testbrowser/fixed-bugs.txt
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/fixed-bugs.txt	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/src/zope/testbrowser/fixed-bugs.txt	2008-03-30 23:49:20 UTC (rev 85021)
@@ -1,3 +1,11 @@
+==========
+Fixed Bugs
+==========
+
+This file includes tests for bugs that were found and then fixed that don't fit
+into the more documentation-centric sections above.
+
+
 Unicode URLs
 ============
 
@@ -25,3 +33,28 @@
     >>> browser = Browser()
     >>> browser.addHeader(u'Cookie', 'test')
     >>> browser.open('http://localhost/@@/testbrowser/simple.html')
+
+
+Spaces in URL
+=============
+
+When URLs have spaces in them, they're handled correctly (before the bug was
+fixed, you'd get "ValueError: too many values to unpack"):
+
+    >>> browser.open('http://localhost/@@/testbrowser/navigate.html')
+    >>> browser.getLink('Spaces in the URL').click()
+
+
+.goBack() Truncation
+====================
+
+The .goBack() method used to truncate the .contents.
+
+    >>> browser.open('http://localhost/@@/testbrowser/navigate.html')
+    >>> actual_length = len(browser.contents)
+
+    >>> browser.open('http://localhost/@@/testbrowser/navigate.html')
+    >>> browser.open('http://localhost/@@/testbrowser/simple.html')
+    >>> browser.goBack()
+    >>> len(browser.contents) == actual_length
+    True

Modified: zope.testbrowser/trunk/src/zope/testbrowser/ftests/__init__.py
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/ftests/__init__.py	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/src/zope/testbrowser/ftests/__init__.py	2008-03-30 23:49:20 UTC (rev 85021)
@@ -1 +1,23 @@
-# Make a package.
+##############################################################################
+#
+# Copyright (c) Zope Corporation 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.
+#
+##############################################################################
+
+class Echo:
+    """Simply echo the contents of the request"""
+
+    def __init__(self, context, request):
+        self.request = request
+
+    def __call__(self):
+        return ('\n'.join('%s: %s' % x for x in self.request.items()) +
+            '\nBody: %r' % self.request.bodyStream.read())

Modified: zope.testbrowser/trunk/src/zope/testbrowser/ftests/ftesting.zcml
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/ftests/ftesting.zcml	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/src/zope/testbrowser/ftests/ftesting.zcml	2008-03-30 23:49:20 UTC (rev 85021)
@@ -30,6 +30,12 @@
   <grant permission="zope.View"
                   role="zope.Anonymous" />
 
+  <browser:page
+     name="echo.html"
+     for="*"
+     class=".ftests.Echo"
+     permission="zope.Public"
+     />
 
   <browser:resourceDirectory
       name="testbrowser"

Modified: zope.testbrowser/trunk/src/zope/testbrowser/testing.py
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/testing.py	2008-03-30 21:24:49 UTC (rev 85020)
+++ zope.testbrowser/trunk/src/zope/testbrowser/testing.py	2008-03-30 23:49:20 UTC (rev 85021)
@@ -123,7 +123,15 @@
 class PublisherHTTPHandler(urllib2.HTTPHandler):
     """Special HTTP handler to use the Zope Publisher."""
 
-    http_request = urllib2.AbstractHTTPHandler.do_request_
+    def http_request(self, req):
+        # look at data and set content type
+        if req.has_data():
+            data = req.get_data()
+            if isinstance(data, dict):
+                req.add_data(data['body'])
+                req.add_unredirected_header('Content-type',
+                                            data['content-type'])
+        return urllib2.AbstractHTTPHandler.do_request_(self, req)
 
     def http_open(self, req):
         """Open an HTTP connection having a ``urllib2`` request."""
@@ -159,94 +167,3 @@
     def __init__(self, url=None):
         mech_browser = PublisherMechanizeBrowser()
         super(Browser, self).__init__(url=url, mech_browser=mech_browser)
-
-#### virtual host test suites ####
-
-example_path_re = re.compile('http://example.com/virtual_path/')
-
-class VirtualHostingPublisherConnection(PublisherConnection):
-    def request(self, method, url, body=None, headers=None):
-        if self.host == 'example.com':
-            assert url.startswith('/virtual_path')
-            url = url[13:]
-        if not url:
-            url = '/'
-        url = '/vh_test_folder/++vh++http:example.com:80/virtual_path/++' + url
-        super(VirtualHostingPublisherConnection, self).request(
-            method, url, body, headers)
-
-class VirtualHostingPublisherHTTPHandler(urllib2.HTTPHandler):
-    """Special HTTP handler to use the Zope Publisher."""
-
-    http_request = urllib2.AbstractHTTPHandler.do_request_
-
-    def http_open(self, req):
-        """Open an HTTP connection having a ``urllib2`` request."""
-        # Here we connect to the publisher.
-        return self.do_open(VirtualHostingPublisherConnection, req)
-
-class VirtualHostingPublisherMechanizeBrowser(PublisherMechanizeBrowser):
-    handler_classes = PublisherMechanizeBrowser.handler_classes.copy()
-    handler_classes['http'] = VirtualHostingPublisherHTTPHandler
-
-class VirtualHostingBrowser(browser.Browser):
-    """A Zope ``testbrowser` Browser that inserts ."""
-
-    def __init__(self, url=None):
-        mech_browser = VirtualHostingPublisherMechanizeBrowser()
-        super(VirtualHostingBrowser, self).__init__(
-            url=url, mech_browser=mech_browser)
-
-def virtualHostingSetUp(test):
-    # need to create a folder named vh_test_folder in root
-    root = test.globs['getRootFolder']()
-    f = Folder()
-    root['vh_test_folder'] = f
-    f.setSiteManager(LocalSiteManager(f))
-    transaction.commit()
-
-def VirtualHostTestBrowserSuite(*paths, **kw):
-#    layer=None,
-#    globs=None, setUp=None, normalizers=None, **kw):
-
-    if 'checker' in kw:
-        raise ValueError(
-            'Must not supply custom checker.  To provide values for '
-            'zope.testing.renormalizing.RENormalizing checkers, include a '
-            '"normalizers" argument that is a list of (compiled regex, '
-            'replacement) pairs.')
-    kw['package'] = doctest._normalize_module(kw.get('package'))
-    layer = kw.pop('layer', None)
-    normalizers = kw.pop('normalizers', None)
-    vh_kw = kw.copy()
-    if 'globs' in kw:
-        globs = kw['globs'] = kw['globs'].copy() # don't mutate the original
-    else:
-        globs = kw['globs'] = {}
-    if 'Browser' in globs:
-        raise ValueError('"Browser" must not be defined in globs')
-    vh_kw['globs'] = globs.copy()
-    globs['Browser'] = Browser
-    vh_kw['globs']['Browser'] = VirtualHostingBrowser
-    def vh_setUp(test):
-        virtualHostingSetUp(test)
-        if 'setUp' in kw:
-            kw['setUp'](test)
-    vh_kw['setUp'] = vh_setUp
-    if normalizers is not None:
-        kw['checker'] = renormalizing.RENormalizing(normalizers)
-        vh_normalizers = normalizers[:]
-    else:
-        vh_normalizers = []
-    vh_normalizers.append((example_path_re, 'http://localhost/'))
-    vh_kw['checker'] = renormalizing.RENormalizing(vh_normalizers)
-    suite = unittest.TestSuite()
-    test = functional.FunctionalDocFileSuite(*paths, **kw)
-    vh_test = functional.FunctionalDocFileSuite(*paths, **vh_kw)
-    vh_test.level = 2
-    if layer is not None:
-        test.layer = layer
-        vh_test.layer = layer
-    suite.addTest(test)
-    suite.addTest(vh_test)
-    return suite



More information about the Checkins mailing list