[Checkins] SVN: zope.testbrowser/tags/3.5.1/ - Tag 3.5.1

Sidnei da Silva sidnei at enfoldsystems.com
Fri Oct 10 12:57:17 EDT 2008


Log message for revision 92000:
   - Tag 3.5.1

Changed:
  A   zope.testbrowser/tags/3.5.1/
  D   zope.testbrowser/tags/3.5.1/CHANGES.txt
  A   zope.testbrowser/tags/3.5.1/CHANGES.txt
  U   zope.testbrowser/tags/3.5.1/bootstrap.py
  D   zope.testbrowser/tags/3.5.1/buildout.cfg
  A   zope.testbrowser/tags/3.5.1/buildout.cfg
  D   zope.testbrowser/tags/3.5.1/setup.py
  A   zope.testbrowser/tags/3.5.1/setup.py
  D   zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py
  A   zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py
  D   zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py
  A   zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py

-=-
Copied: zope.testbrowser/tags/3.5.1 (from rev 91902, zope.testbrowser/trunk)


Property changes on: zope.testbrowser/tags/3.5.1
___________________________________________________________________
Name: svn:ignore
   + bin
build
dist
lib
setup.cfg
develop-eggs
eggs
parts
.installed.cfg

Name: svn:mergeinfo
   + 

Deleted: zope.testbrowser/tags/3.5.1/CHANGES.txt
===================================================================
--- zope.testbrowser/trunk/CHANGES.txt	2008-10-08 09:30:09 UTC (rev 91902)
+++ zope.testbrowser/tags/3.5.1/CHANGES.txt	2008-10-10 16:57:17 UTC (rev 92000)
@@ -1,69 +0,0 @@
-=======
-CHANGES
-=======
-
-
-3.5.0 (unreleased)
-------------------
-
-- Provide a work around for a mechanize/urllib2 bug in creating request
-  objects that won't handle fragment URLs correctly.
-
-- 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
-  and 3.4.1.
-
-- Workaround for bug in Python Cookie.SimpleCookie when handling unicode
-  strings.
-
-- Fix bug introduced in 3.4.1 that created incompatible tracebacks in doctests.
-  This necessitated adding a patched mechanize to the source tree; patches have
-  been sent to the mechanize project.
-
-- Fix https://bugs.launchpad.net/bugs/149517 by adding zope.interface and
-  zope.schema as real dependencies
-
-- 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)
-------------------
-
-- Resolve ``ZopeSecurityPolicy`` deprecation warning.
-
-
-3.4.1 (2007-09-01)
-------------------
-
-* Updated to mechanize 0.1.7b and ClientForm 0.2.7.  These are now
-  pulled in via egg dependencies.
-
-* ``zope.testbrowser`` now works on Python 2.5.
-
-
-3.4.0 (2007-06-04)
-------------------
-
-* Added the ability to suppress raising exceptions on HTTP errors
-  (``raiseHttpErrors`` attribute).
-
-* Made the tests more resilient to HTTP header formatting changes with
-  the REnormalizer.
-
-
-3.4.0a1 (2007-04-22)
---------------------
-
-Initial release as a separate project, corresponds to zope.testbrowser
-from Zope 3.4.0a1

Copied: zope.testbrowser/tags/3.5.1/CHANGES.txt (from rev 91999, zope.testbrowser/trunk/CHANGES.txt)
===================================================================
--- zope.testbrowser/tags/3.5.1/CHANGES.txt	                        (rev 0)
+++ zope.testbrowser/tags/3.5.1/CHANGES.txt	2008-10-10 16:57:17 UTC (rev 92000)
@@ -0,0 +1,74 @@
+=======
+CHANGES
+=======
+
+3.5.1 (2008-10-10)
+------------------
+
+- Provide a work around for a mechanize/urllib2 bug on Python 2.6
+  missing 'timeout' attribute on 'Request' base class.
+
+- Provide a work around for a mechanize/urllib2 bug in creating request
+  objects that won't handle fragment URLs correctly.
+
+3.5.0 (2008-03-30)
+------------------
+
+- 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
+  and 3.4.1.
+
+- Workaround for bug in Python Cookie.SimpleCookie when handling unicode
+  strings.
+
+- Fix bug introduced in 3.4.1 that created incompatible tracebacks in doctests.
+  This necessitated adding a patched mechanize to the source tree; patches have
+  been sent to the mechanize project.
+
+- Fix https://bugs.launchpad.net/bugs/149517 by adding zope.interface and
+  zope.schema as real dependencies
+
+- 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)
+------------------
+
+- Resolve ``ZopeSecurityPolicy`` deprecation warning.
+
+
+3.4.1 (2007-09-01)
+------------------
+
+* Updated to mechanize 0.1.7b and ClientForm 0.2.7.  These are now
+  pulled in via egg dependencies.
+
+* ``zope.testbrowser`` now works on Python 2.5.
+
+
+3.4.0 (2007-06-04)
+------------------
+
+* Added the ability to suppress raising exceptions on HTTP errors
+  (``raiseHttpErrors`` attribute).
+
+* Made the tests more resilient to HTTP header formatting changes with
+  the REnormalizer.
+
+
+3.4.0a1 (2007-04-22)
+--------------------
+
+Initial release as a separate project, corresponds to zope.testbrowser
+from Zope 3.4.0a1

Modified: zope.testbrowser/tags/3.5.1/bootstrap.py
===================================================================
--- zope.testbrowser/trunk/bootstrap.py	2008-10-08 09:30:09 UTC (rev 91902)
+++ zope.testbrowser/tags/3.5.1/bootstrap.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -17,7 +17,7 @@
 The script accepts buildout command-line options, so you can
 use the -c option to specify an alternate configuration file.
 
-$Id: bootstrap.py 72703 2007-02-20 11:49:26Z jim $
+$Id$
 """
 
 import os, shutil, sys, tempfile, urllib2

Deleted: zope.testbrowser/tags/3.5.1/buildout.cfg
===================================================================
--- zope.testbrowser/trunk/buildout.cfg	2008-10-08 09:30:09 UTC (rev 91902)
+++ zope.testbrowser/tags/3.5.1/buildout.cfg	2008-10-10 16:57:17 UTC (rev 92000)
@@ -1,24 +0,0 @@
-[buildout]
-develop = .
-parts = test interpreter
-index = http://download.zope.org/simple
-extends = http://download.zope.org/zope3.4/versions.cfg
-versions = versions
-allow-picked-versions = false
-use-dependency-links = false
-
-[versions]
-zope.testbrowser =
-zope.publisher = 3.5.1
-zope.app.publication = 3.4.2
-
-[test]
-recipe = zc.recipe.testrunner
-defaults = ['--tests-pattern', '^f?tests$']
-eggs = zope.testbrowser [test]
-
-[interpreter]
-recipe = zc.recipe.egg
-eggs = zope.testbrowser
-       mechanize
-interpreter = py

Copied: zope.testbrowser/tags/3.5.1/buildout.cfg (from rev 91989, zope.testbrowser/trunk/buildout.cfg)
===================================================================
--- zope.testbrowser/tags/3.5.1/buildout.cfg	                        (rev 0)
+++ zope.testbrowser/tags/3.5.1/buildout.cfg	2008-10-10 16:57:17 UTC (rev 92000)
@@ -0,0 +1,26 @@
+[buildout]
+develop = .
+parts = test interpreter
+index = http://download.zope.org/simple
+extends = http://download.zope.org/zope3.4/versions.cfg
+versions = versions
+allow-picked-versions = false
+use-dependency-links = false
+
+[versions]
+mechanize = 0.1.9
+setuptools = 0.6c9
+zope.testbrowser =
+zope.publisher = 3.5.1
+zope.app.publication = 3.4.2
+
+[test]
+recipe = zc.recipe.testrunner
+defaults = ['--tests-pattern', '^f?tests$']
+eggs = zope.testbrowser [test]
+
+[interpreter]
+recipe = zc.recipe.egg
+eggs = zope.testbrowser
+       mechanize
+interpreter = py

Deleted: zope.testbrowser/tags/3.5.1/setup.py
===================================================================
--- zope.testbrowser/trunk/setup.py	2008-10-08 09:30:09 UTC (rev 91902)
+++ zope.testbrowser/tags/3.5.1/setup.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -1,70 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 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.
-#
-##############################################################################
-"""Setup for zope.testbrowser package
-
-$Id$
-"""
-import os
-from setuptools import setup, find_packages
-
-long_description = (
-    '.. contents::\n\n'
-    + open('README.txt').read()
-    + '\n\n'
-    + open(os.path.join('src', 'zope', 'testbrowser', 'README.txt')).read()
-    + '\n\n'
-    + open('CHANGES.txt').read()
-    )
-
-setup(
-    name = 'zope.testbrowser',
-    version = '3.5.0dev',
-    url = 'http://pypi.python.org/pypi/zope.testbrowser',
-    license = 'ZPL 2.1',
-    description = 'Programmable browser for functional black-box tests',
-    author = 'Zope Corporation and Contributors',
-    author_email = 'zope3-dev at zope.org',
-    long_description = long_description,
-    classifiers=[
-        'Environment :: Web Environment',
-        'Intended Audience :: Developers',
-        'License :: OSI Approved :: Zope Public License',
-        'Programming Language :: Python',
-        'Topic :: Software Development :: Testing',
-        'Topic :: Internet :: WWW/HTTP',
-        ],
-
-    packages = find_packages('src'),
-    package_dir = {'': 'src'},
-    namespace_packages = ['zope',],
-    tests_require = ['zope.testing'],
-    install_requires = [
-        'ClientForm >= 0.2.8',
-        'mechanize',
-        'setuptools',
-        'zope.interface',
-        'zope.schema',
-        ],
-    extras_require = dict(
-        test = [
-            'zope.app.component',
-            'zope.app.folder',
-            'zope.app.securitypolicy',
-            'zope.app.testing',
-            'zope.app.zcmlfiles',
-            ],
-        ),
-    include_package_data = True,
-    zip_safe = False,
-    )

Copied: zope.testbrowser/tags/3.5.1/setup.py (from rev 91989, zope.testbrowser/trunk/setup.py)
===================================================================
--- zope.testbrowser/tags/3.5.1/setup.py	                        (rev 0)
+++ zope.testbrowser/tags/3.5.1/setup.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -0,0 +1,70 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Setup for zope.testbrowser package
+
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+long_description = (
+    '.. contents::\n\n'
+    + open('README.txt').read()
+    + '\n\n'
+    + open(os.path.join('src', 'zope', 'testbrowser', 'README.txt')).read()
+    + '\n\n'
+    + open('CHANGES.txt').read()
+    )
+
+setup(
+    name = 'zope.testbrowser',
+    version = '3.5.1dev',
+    url = 'http://pypi.python.org/pypi/zope.testbrowser',
+    license = 'ZPL 2.1',
+    description = 'Programmable browser for functional black-box tests',
+    author = 'Zope Corporation and Contributors',
+    author_email = 'zope3-dev at zope.org',
+    long_description = long_description,
+    classifiers=[
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Testing',
+        'Topic :: Internet :: WWW/HTTP',
+        ],
+
+    packages = find_packages('src'),
+    package_dir = {'': 'src'},
+    namespace_packages = ['zope',],
+    tests_require = ['zope.testing'],
+    install_requires = [
+        'ClientForm >= 0.2.8',
+        'mechanize',
+        'setuptools',
+        'zope.interface',
+        'zope.schema',
+        ],
+    extras_require = dict(
+        test = [
+            'zope.app.component',
+            'zope.app.folder',
+            'zope.app.securitypolicy',
+            'zope.app.testing',
+            'zope.app.zcmlfiles',
+            ],
+        ),
+    include_package_data = True,
+    zip_safe = False,
+    )

Deleted: zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/testing.py	2008-10-08 09:30:09 UTC (rev 91902)
+++ zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -1,172 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2005 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.
-#
-##############################################################################
-"""Zope 3-specific testing code
-
-$Id$
-"""
-import re
-import sys
-import unittest
-import httplib
-import urllib2
-from cStringIO import StringIO
-
-import mechanize
-
-import transaction
-from zope.testbrowser import browser
-from zope.testing import renormalizing, doctest
-
-from zope.app.testing import functional
-from zope.app.folder.folder import Folder
-from zope.app.component.site import LocalSiteManager
-
-class PublisherConnection(object):
-    """A ``urllib2`` compatible connection obejct."""
-
-    def __init__(self, host):
-        self.caller = functional.HTTPCaller()
-        self.host = host
-
-    def set_debuglevel(self, level):
-        pass
-
-    def _quote(self, url):
-        # the publisher expects to be able to split on whitespace, so we have
-        # to make sure there is none in the URL
-        return url.replace(' ', '%20')
-
-    def request(self, method, url, body=None, headers=None):
-        """Send a request to the publisher.
-
-        The response will be stored in ``self.response``.
-        """
-        if body is None:
-            body = ''
-
-        if url == '':
-            url = '/'
-
-        url = self._quote(url)
-        # Extract the handle_error option header
-        if sys.version_info >= (2,5):
-            handle_errors_key = 'X-Zope-Handle-Errors'
-        else:
-            handle_errors_key = 'X-zope-handle-errors'
-        handle_errors = headers.get(handle_errors_key, True)
-        if handle_errors_key in headers:
-            del headers[handle_errors_key]
-
-        # Construct the headers.
-        header_chunks = []
-        if headers is not None:
-            for header in headers.items():
-                header_chunks.append('%s: %s' % header)
-            headers = '\n'.join(header_chunks) + '\n'
-        else:
-            headers = ''
-
-        # Construct the full HTTP request string, since that is what the
-        # ``HTTPCaller`` wants.
-        request_string = (method + ' ' + url + ' HTTP/1.1\n'
-                          + headers + '\n' + body)
-        self.response = self.caller(request_string, handle_errors)
-
-    def getresponse(self):
-        """Return a ``urllib2`` compatible response.
-
-        The goal of ths method is to convert the Zope Publisher's reseponse to
-        a ``urllib2`` compatible response, which is also understood by
-        mechanize.
-        """
-        real_response = self.response._response
-        status = real_response.getStatus()
-        reason = real_response._reason # XXX add a getReason method
-
-        headers = real_response.getHeaders()
-        headers.sort()
-        headers.insert(0, ('Status', real_response.getStatusString()))
-        headers = '\r\n'.join('%s: %s' % h for h in headers)
-        content = real_response.consumeBody()
-        return PublisherResponse(content, headers, status, reason)
-
-
-class PublisherResponse(object):
-    """``urllib2`` compatible response object."""
-
-    def __init__(self, content, headers, status, reason):
-        self.content = content
-        self.status = status
-        self.reason = reason
-        self.msg = httplib.HTTPMessage(StringIO(headers), 0)
-        self.content_as_file = StringIO(self.content)
-
-    def read(self, amt=None):
-        return self.content_as_file.read(amt)
-
-    def close(self):
-        """To overcome changes in urllib2 and socket in python2.5"""
-        pass
-
-
-class PublisherHTTPHandler(urllib2.HTTPHandler):
-    """Special HTTP handler to use the Zope Publisher."""
-
-    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."""
-        # Here we connect to the publisher.
-        return self.do_open(PublisherConnection, req)
-
-
-class PublisherMechanizeBrowser(mechanize.Browser):
-    """Special ``mechanize`` browser using the Zope Publisher HTTP handler."""
-
-    default_schemes = ['http']
-    default_others = ['_http_error', '_http_request_upgrade',
-                      '_http_default_error']
-    default_features = ['_redirect', '_cookies', '_referer', '_refresh',
-                        '_equiv', '_basicauth', '_digestauth']
-
-    def __init__(self, *args, **kws):
-        inherited_handlers = ['_unknown', '_http_error',
-            '_http_request_upgrade', '_http_default_error', '_basicauth',
-            '_digestauth', '_redirect', '_cookies', '_referer',
-            '_refresh', '_equiv', '_gzip']
-
-        self.handler_classes = {"http": PublisherHTTPHandler}
-        for name in inherited_handlers:
-            self.handler_classes[name] = mechanize.Browser.handler_classes[name]
-
-        kws['request_class'] = kws.get('request_class',
-                                       mechanize._request.Request)
-
-        mechanize.Browser.__init__(self, *args, **kws)
-
-
-class Browser(browser.Browser):
-    """A Zope `testbrowser` Browser that uses the Zope Publisher."""
-
-    def __init__(self, url=None):
-        mech_browser = PublisherMechanizeBrowser()
-        super(Browser, self).__init__(url=url, mech_browser=mech_browser)

Copied: zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py (from rev 91989, zope.testbrowser/trunk/src/zope/testbrowser/testing.py)
===================================================================
--- zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py	                        (rev 0)
+++ zope.testbrowser/tags/3.5.1/src/zope/testbrowser/testing.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -0,0 +1,177 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Zope 3-specific testing code
+
+$Id$
+"""
+import re
+import sys
+import socket
+import unittest
+import httplib
+import urllib2
+from cStringIO import StringIO
+
+import mechanize
+
+import transaction
+from zope.testbrowser import browser
+from zope.testing import renormalizing, doctest
+
+from zope.app.testing import functional
+from zope.app.folder.folder import Folder
+from zope.app.component.site import LocalSiteManager
+
+class PublisherConnection(object):
+    """A ``urllib2`` compatible connection obejct."""
+
+    def __init__(self, host, timeout=None):
+        self.caller = functional.HTTPCaller()
+        self.host = host
+
+    def set_debuglevel(self, level):
+        pass
+
+    def _quote(self, url):
+        # the publisher expects to be able to split on whitespace, so we have
+        # to make sure there is none in the URL
+        return url.replace(' ', '%20')
+
+    def request(self, method, url, body=None, headers=None):
+        """Send a request to the publisher.
+
+        The response will be stored in ``self.response``.
+        """
+        if body is None:
+            body = ''
+
+        if url == '':
+            url = '/'
+
+        url = self._quote(url)
+        # Extract the handle_error option header
+        if sys.version_info >= (2,5):
+            handle_errors_key = 'X-Zope-Handle-Errors'
+        else:
+            handle_errors_key = 'X-zope-handle-errors'
+        handle_errors = headers.get(handle_errors_key, True)
+        if handle_errors_key in headers:
+            del headers[handle_errors_key]
+
+        # Construct the headers.
+        header_chunks = []
+        if headers is not None:
+            for header in headers.items():
+                header_chunks.append('%s: %s' % header)
+            headers = '\n'.join(header_chunks) + '\n'
+        else:
+            headers = ''
+
+        # Construct the full HTTP request string, since that is what the
+        # ``HTTPCaller`` wants.
+        request_string = (method + ' ' + url + ' HTTP/1.1\n'
+                          + headers + '\n' + body)
+        self.response = self.caller(request_string, handle_errors)
+
+    def getresponse(self):
+        """Return a ``urllib2`` compatible response.
+
+        The goal of ths method is to convert the Zope Publisher's reseponse to
+        a ``urllib2`` compatible response, which is also understood by
+        mechanize.
+        """
+        real_response = self.response._response
+        status = real_response.getStatus()
+        reason = real_response._reason # XXX add a getReason method
+
+        headers = real_response.getHeaders()
+        headers.sort()
+        headers.insert(0, ('Status', real_response.getStatusString()))
+        headers = '\r\n'.join('%s: %s' % h for h in headers)
+        content = real_response.consumeBody()
+        return PublisherResponse(content, headers, status, reason)
+
+
+class PublisherResponse(object):
+    """``urllib2`` compatible response object."""
+
+    def __init__(self, content, headers, status, reason):
+        self.content = content
+        self.status = status
+        self.reason = reason
+        self.msg = httplib.HTTPMessage(StringIO(headers), 0)
+        self.content_as_file = StringIO(self.content)
+
+    def read(self, amt=None):
+        return self.content_as_file.read(amt)
+
+    def close(self):
+        """To overcome changes in urllib2 and socket in python2.5"""
+        pass
+
+
+class PublisherHTTPHandler(urllib2.HTTPHandler):
+    """Special HTTP handler to use the Zope Publisher."""
+
+    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."""
+        # Here we connect to the publisher.
+        if sys.version_info > (2, 6) and not hasattr(req, 'timeout'):
+            # Workaround mechanize incompatibility with Python
+            # 2.6. See: LP #280334
+            req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
+        return self.do_open(PublisherConnection, req)
+
+
+class PublisherMechanizeBrowser(mechanize.Browser):
+    """Special ``mechanize`` browser using the Zope Publisher HTTP handler."""
+
+    default_schemes = ['http']
+    default_others = ['_http_error', '_http_request_upgrade',
+                      '_http_default_error']
+    default_features = ['_redirect', '_cookies', '_referer', '_refresh',
+                        '_equiv', '_basicauth', '_digestauth']
+
+    def __init__(self, *args, **kws):
+        inherited_handlers = ['_unknown', '_http_error',
+            '_http_request_upgrade', '_http_default_error', '_basicauth',
+            '_digestauth', '_redirect', '_cookies', '_referer',
+            '_refresh', '_equiv', '_gzip']
+
+        self.handler_classes = {"http": PublisherHTTPHandler}
+        for name in inherited_handlers:
+            self.handler_classes[name] = mechanize.Browser.handler_classes[name]
+
+        kws['request_class'] = kws.get('request_class',
+                                       mechanize._request.Request)
+
+        mechanize.Browser.__init__(self, *args, **kws)
+
+
+class Browser(browser.Browser):
+    """A Zope `testbrowser` Browser that uses the Zope Publisher."""
+
+    def __init__(self, url=None):
+        mech_browser = PublisherMechanizeBrowser()
+        super(Browser, self).__init__(url=url, mech_browser=mech_browser)

Deleted: zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py
===================================================================
--- zope.testbrowser/trunk/src/zope/testbrowser/tests.py	2008-10-08 09:30:09 UTC (rev 91902)
+++ zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -1,403 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (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.
-#
-##############################################################################
-"""Real test for file-upload and beginning of a better internal test framework
-
-$Id$
-"""
-
-from cStringIO import StringIO
-from zope.app.testing import functional
-from zope.app.testing.functional import FunctionalDocFileSuite
-from zope.testbrowser import browser
-from zope.testing import doctest
-from zope.testing import renormalizing, doctest
-import httplib
-import mechanize
-import os
-import re
-import unittest
-import unittest
-import urllib2
-
-
-def set_next_response(body, headers=None, status='200', reason='OK'):
-    global next_response_body
-    global next_response_headers
-    global next_response_status
-    global next_response_reason
-    if headers is None:
-        headers = (
-            'Content-Type: text/html\r\n'
-            'Content-Length: %s\r\n'
-            % len(body)
-            )
-    next_response_body = body
-    next_response_headers = headers
-    next_response_status = status
-    next_response_reason = reason
-
-
-class FauxConnection(object):
-    """A ``urllib2`` compatible connection object."""
-
-    def __init__(self, host):
-        pass
-
-    def set_debuglevel(self, level):
-        pass
-
-    def _quote(self, url):
-        # the publisher expects to be able to split on whitespace, so we have
-        # to make sure there is none in the URL
-        return url.replace(' ', '%20')
-
-
-    def request(self, method, url, body=None, headers=None):
-        if body is None:
-            body = ''
-
-        if url == '':
-            url = '/'
-
-        url = self._quote(url)
-
-        # Construct the headers.
-        header_chunks = []
-        if headers is not None:
-            for header in headers.items():
-                header_chunks.append('%s: %s' % header)
-            headers = '\n'.join(header_chunks) + '\n'
-        else:
-            headers = ''
-
-        # Construct the full HTTP request string, since that is what the
-        # ``HTTPCaller`` wants.
-        request_string = (method + ' ' + url + ' HTTP/1.1\n'
-                          + headers + '\n' + body)
-
-        print request_string.replace('\r', '')
-
-    def getresponse(self):
-        """Return a ``urllib2`` compatible response.
-
-        The goal of this method is to convert the Zope Publisher's response to
-        a ``urllib2`` compatible response, which is also understood by
-        mechanize.
-        """
-        return FauxResponse(next_response_body,
-                            next_response_headers,
-                            next_response_status,
-                            next_response_reason,
-                            )
-
-class FauxResponse(object):
-
-    def __init__(self, content, headers, status, reason):
-        self.content = content
-        self.status = status
-        self.reason = reason
-        self.msg = httplib.HTTPMessage(StringIO(headers), 0)
-        self.content_as_file = StringIO(self.content)
-
-    def read(self, amt=None):
-        return self.content_as_file.read(amt)
-
-    def close(self):
-        """To overcome changes in urllib2 and socket in python2.5"""
-        pass
-
-
-class FauxHTTPHandler(urllib2.HTTPHandler):
-
-    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(FauxConnection, req)
-
-
-class FauxMechanizeBrowser(mechanize.Browser):
-
-    handler_classes = {
-        # scheme handlers
-        "http": FauxHTTPHandler,
-
-        "_http_error": mechanize.HTTPErrorProcessor,
-        "_http_request_upgrade": mechanize.HTTPRequestUpgradeProcessor,
-        "_http_default_error": urllib2.HTTPDefaultErrorHandler,
-
-        # feature handlers
-        "_authen": urllib2.HTTPBasicAuthHandler,
-        "_redirect": mechanize.HTTPRedirectHandler,
-        "_cookies": mechanize.HTTPCookieProcessor,
-        "_refresh": mechanize.HTTPRefreshProcessor,
-        "_referer": mechanize.Browser.handler_classes['_referer'],
-        "_equiv": mechanize.HTTPEquivProcessor,
-        }
-
-    default_schemes = ["http"]
-    default_others = ["_http_error", "_http_request_upgrade",
-                      "_http_default_error"]
-    default_features = ["_authen", "_redirect", "_cookies"]
-
-
-class Browser(browser.Browser):
-
-    def __init__(self, url=None):
-        mech_browser = FauxMechanizeBrowser()
-        super(Browser, self).__init__(url=url, mech_browser=mech_browser)
-
-    def open(self, body, headers=None, status=200, reason='OK'):
-        set_next_response(body, headers, status, reason)
-        browser.Browser.open(self, 'http://localhost/')
-
-def test_submit_duplicate_name():
-    """
-
-This test was inspired by bug #723 as testbrowser would pick up the wrong
-button when having the same name twice in a form.
-
-    >>> browser = Browser()
-
-When given a form with two submit buttons that have the same name:
-
-    >>> browser.open('''\
-    ... <html><body>
-    ...   <form action="." method="post" enctype="multipart/form-data">
-    ...      <input type="submit" name="submit_me" value="GOOD" />
-    ...      <input type="submit" name="submit_me" value="BAD" />
-    ...   </form></body></html>
-    ... ''') # doctest: +ELLIPSIS
-    GET / HTTP/1.1
-    ...
-
-We can specify the second button through it's label/value:
-
-    >>> browser.getControl('BAD')
-    <SubmitControl name='submit_me' type='submit'>
-    >>> browser.getControl('BAD').value
-    'BAD'
-    >>> browser.getControl('BAD').click() # doctest: +REPORT_NDIFF
-    POST / HTTP/1.1
-    Content-length: 176
-    Connection: close
-    Content-type: multipart/form-data; boundary=---------------------------100167997466992641913031254
-    Host: localhost
-    User-agent: Python-urllib/2.4
-    <BLANKLINE>
-    -----------------------------100167997466992641913031254
-    Content-disposition: form-data; name="submit_me"
-    <BLANKLINE>
-    BAD
-    -----------------------------100167997466992641913031254--
-    <BLANKLINE>
-
-This also works if the labels have whitespace around them (this tests a
-regression caused by the original fix for the above):
-
-    >>> browser.open('''\
-    ... <html><body>
-    ...   <form action="." method="post" enctype="multipart/form-data">
-    ...      <input type="submit" name="submit_me" value=" GOOD " />
-    ...      <input type="submit" name="submit_me" value=" BAD " />
-    ...   </form></body></html>
-    ... ''') # doctest: +ELLIPSIS
-    GET / HTTP/1.1
-    ...
-    >>> browser.getControl('BAD')
-    <SubmitControl name='submit_me' type='submit'>
-    >>> browser.getControl('BAD').value
-    ' BAD '
-    >>> browser.getControl('BAD').click() # doctest: +REPORT_NDIFF
-    POST / HTTP/1.1
-    Content-length: 176
-    Connection: close
-    Content-type: multipart/form-data; boundary=---------------------------100167997466992641913031254
-    Host: localhost
-    User-agent: Python-urllib/2.4
-    <BLANKLINE>
-    -----------------------------100167997466992641913031254
-    Content-disposition: form-data; name="submit_me"
-    <BLANKLINE>
-     BAD 
-    -----------------------------100167997466992641913031254--
-    <BLANKLINE>
-
-"""
-
-def test_file_upload():
-    """
-
-    >>> browser = Browser()
-
-When given a form with a file-upload
-
-    >>> browser.open('''\
-    ... <html><body>
-    ...   <form action="." method="post" enctype="multipart/form-data">
-    ...      <input name="foo" type="file" />
-    ...      <input type="submit" value="OK" />
-    ...   </form></body></html>
-    ... ''') # doctest: +ELLIPSIS
-    GET / HTTP/1.1
-    ...
-
-Fill in the form value using add_file:
-
-    >>> browser.getControl(name='foo').add_file(
-    ...     StringIO('sample_data'), 'text/foo', 'x.foo')
-    >>> browser.getControl('OK').click()
-    POST / HTTP/1.1
-    Content-length: 173
-    Connection: close
-    Content-type: multipart/form-data; boundary=127.0.0.11000318041146699896411
-    Host: localhost
-    User-agent: Python-urllib/2.99
-    <BLANKLINE>
-    --127.0.0.11000318041146699896411
-    Content-disposition: form-data; name="foo"; filename="x.foo"
-    Content-type: text/foo
-    <BLANKLINE>
-    sample_data
-    --127.0.0.11000318041146699896411--
-    <BLANKLINE>
-
-You can pass a string to add_file:
-
-
-    >>> browser.getControl(name='foo').add_file(
-    ...     'blah blah blah', 'text/blah', 'x.blah')
-    >>> browser.getControl('OK').click()
-    POST / HTTP/1.1
-    Content-length: 178
-    Connection: close
-    Content-type: multipart/form-data; boundary=127.0.0.11000318541146700017052
-    Host: localhost
-    User-agent: Python-urllib/2.98
-    <BLANKLINE>
-    --127.0.0.11000318541146700017052
-    Content-disposition: form-data; name="foo"; filename="x.blah"
-    Content-type: text/blah
-    <BLANKLINE>
-    blah blah blah
-    --127.0.0.11000318541146700017052--
-    <BLANKLINE>
-
-
-    """
-
-
-def test_strip_linebreaks_from_textarea(self):
-    """
-
-    >>> browser = Browser()
-
-According to http://www.w3.org/TR/html4/appendix/notes.html#h-B.3.1 line break
-immediately after start tags or immediately before end tags must be ignored,
-but real browsers only ignore a line break after a start tag.  So if we give
-the following form:
-
-    >>> browser.open('''
-    ... <html><body>
-    ...   <form action="." method="post" enctype="multipart/form-data">
-    ...      <textarea name="textarea">
-    ... Foo
-    ... </textarea>
-    ...   </form></body></html>
-    ... ''') # doctest: +ELLIPSIS
-    GET / HTTP/1.1
-    ...
-
-The value of the textarea won't contain the first line break:
-
-    >>> browser.getControl(name='textarea').value
-    'Foo\\n'
-
-Of course, if we add line breaks, so that there are now two line breaks
-after the start tag, the textarea value will start and end with a line break.
-
-    >>> browser.open('''
-    ... <html><body>
-    ...   <form action="." method="post" enctype="multipart/form-data">
-    ...      <textarea name="textarea">
-    ...
-    ... Foo
-    ... </textarea>
-    ...   </form></body></html>
-    ... ''') # doctest: +ELLIPSIS
-    GET / HTTP/1.1
-    ...
-
-    >>> browser.getControl(name='textarea').value
-    '\\nFoo\\n'
-
-Also, if there is some other whitespace after the start tag, it will be preserved.
-
-    >>> browser.open('''
-    ... <html><body>
-    ...   <form action="." method="post" enctype="multipart/form-data">
-    ...      <textarea name="textarea">  Foo  </textarea>
-    ...   </form></body></html>
-    ... ''') # doctest: +ELLIPSIS
-    GET / HTTP/1.1
-    ...
-
-    >>> browser.getControl(name='textarea').value
-    '  Foo  '
-    """
-
-class win32CRLFtransformer(object):
-    def sub(self, replacement, text):
-        return text.replace(r'\r','')
-
-checker = renormalizing.RENormalizing([
-    (re.compile(r'^--\S+\.\S+\.\S+', re.M), '-'*30),
-    (re.compile(r'boundary=\S+\.\S+\.\S+'), 'boundary='+'-'*30),
-    (re.compile(r'^---{10}.*', re.M), '-'*30),
-    (re.compile(r'boundary=-{10}.*'), 'boundary='+'-'*30),
-    (re.compile(r'User-agent:\s+\S+'), 'User-agent: Python-urllib/2.4'),
-    (re.compile(r'HTTP_USER_AGENT:\s+\S+'), 'HTTP_USER_AGENT: Python-urllib/2.4'),
-    (re.compile(r'Content-[Ll]ength:.*'), 'Content-Length: 123'),
-    (re.compile(r'Status: 200.*'), 'Status: 200 OK'),
-    (win32CRLFtransformer(), None),
-    (re.compile(r'User-Agent: Python-urllib/2.5'), 'User-agent: Python-urllib/2.4'),
-    (re.compile(r'Host: localhost'), 'Connection: close'),
-    (re.compile(r'Content-Type: '), 'Content-type: '),
-    ])
-
-TestBrowserLayer = functional.ZCMLLayer(
-    os.path.join(os.path.split(__file__)[0], 'ftests/ftesting.zcml'),
-    __name__, 'TestBrowserLayer', allow_teardown=True)
-
-def test_suite():
-    flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
-
-    readme = FunctionalDocFileSuite('README.txt', optionflags=flags,
-        checker=checker)
-    readme.layer = TestBrowserLayer
-
-    fixed_bugs = FunctionalDocFileSuite('fixed-bugs.txt', optionflags=flags)
-    fixed_bugs.layer = TestBrowserLayer
-
-    wire = FunctionalDocFileSuite('over_the_wire.txt', optionflags=flags)
-    wire.level = 2
-    wire.layer = TestBrowserLayer
-
-    this_file = doctest.DocTestSuite(checker=checker)
-
-    return unittest.TestSuite((this_file, readme, fixed_bugs, wire))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Copied: zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py (from rev 91989, zope.testbrowser/trunk/src/zope/testbrowser/tests.py)
===================================================================
--- zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py	                        (rev 0)
+++ zope.testbrowser/tags/3.5.1/src/zope/testbrowser/tests.py	2008-10-10 16:57:17 UTC (rev 92000)
@@ -0,0 +1,411 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""Real test for file-upload and beginning of a better internal test framework
+
+$Id$
+"""
+
+from cStringIO import StringIO
+from zope.app.testing import functional
+from zope.app.testing.functional import FunctionalDocFileSuite
+from zope.testbrowser import browser
+from zope.testing import doctest
+from zope.testing import renormalizing, doctest
+import httplib
+import mechanize
+import os
+import re
+import sys
+import socket
+import unittest
+import unittest
+import urllib2
+
+
+def set_next_response(body, headers=None, status='200', reason='OK'):
+    global next_response_body
+    global next_response_headers
+    global next_response_status
+    global next_response_reason
+    if headers is None:
+        headers = (
+            'Content-Type: text/html\r\n'
+            'Content-Length: %s\r\n'
+            % len(body)
+            )
+    next_response_body = body
+    next_response_headers = headers
+    next_response_status = status
+    next_response_reason = reason
+
+
+class FauxConnection(object):
+    """A ``urllib2`` compatible connection object."""
+
+    def __init__(self, host, timeout=None):
+        pass
+
+    def set_debuglevel(self, level):
+        pass
+
+    def _quote(self, url):
+        # the publisher expects to be able to split on whitespace, so we have
+        # to make sure there is none in the URL
+        return url.replace(' ', '%20')
+
+
+    def request(self, method, url, body=None, headers=None):
+        if body is None:
+            body = ''
+
+        if url == '':
+            url = '/'
+
+        url = self._quote(url)
+
+        # Construct the headers.
+        header_chunks = []
+        if headers is not None:
+            for header in headers.items():
+                header_chunks.append('%s: %s' % header)
+            headers = '\n'.join(header_chunks) + '\n'
+        else:
+            headers = ''
+
+        # Construct the full HTTP request string, since that is what the
+        # ``HTTPCaller`` wants.
+        request_string = (method + ' ' + url + ' HTTP/1.1\n'
+                          + headers + '\n' + body)
+
+        print request_string.replace('\r', '')
+
+    def getresponse(self):
+        """Return a ``urllib2`` compatible response.
+
+        The goal of this method is to convert the Zope Publisher's response to
+        a ``urllib2`` compatible response, which is also understood by
+        mechanize.
+        """
+        return FauxResponse(next_response_body,
+                            next_response_headers,
+                            next_response_status,
+                            next_response_reason,
+                            )
+
+class FauxResponse(object):
+
+    def __init__(self, content, headers, status, reason):
+        self.content = content
+        self.status = status
+        self.reason = reason
+        self.msg = httplib.HTTPMessage(StringIO(headers), 0)
+        self.content_as_file = StringIO(self.content)
+
+    def read(self, amt=None):
+        return self.content_as_file.read(amt)
+
+    def close(self):
+        """To overcome changes in urllib2 and socket in python2.5"""
+        pass
+
+
+class FauxHTTPHandler(urllib2.HTTPHandler):
+
+    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.
+
+        if sys.version_info > (2, 6) and not hasattr(req, 'timeout'):
+            # Workaround mechanize incompatibility with Python
+            # 2.6. See: LP #280334
+            req.timeout = socket._GLOBAL_DEFAULT_TIMEOUT
+        return self.do_open(FauxConnection, req)
+
+
+class FauxMechanizeBrowser(mechanize.Browser):
+
+    handler_classes = {
+        # scheme handlers
+        "http": FauxHTTPHandler,
+
+        "_http_error": mechanize.HTTPErrorProcessor,
+        "_http_request_upgrade": mechanize.HTTPRequestUpgradeProcessor,
+        "_http_default_error": urllib2.HTTPDefaultErrorHandler,
+
+        # feature handlers
+        "_authen": urllib2.HTTPBasicAuthHandler,
+        "_redirect": mechanize.HTTPRedirectHandler,
+        "_cookies": mechanize.HTTPCookieProcessor,
+        "_refresh": mechanize.HTTPRefreshProcessor,
+        "_referer": mechanize.Browser.handler_classes['_referer'],
+        "_equiv": mechanize.HTTPEquivProcessor,
+        }
+
+    default_schemes = ["http"]
+    default_others = ["_http_error", "_http_request_upgrade",
+                      "_http_default_error"]
+    default_features = ["_authen", "_redirect", "_cookies"]
+
+
+class Browser(browser.Browser):
+
+    def __init__(self, url=None):
+        mech_browser = FauxMechanizeBrowser()
+        super(Browser, self).__init__(url=url, mech_browser=mech_browser)
+
+    def open(self, body, headers=None, status=200, reason='OK'):
+        set_next_response(body, headers, status, reason)
+        browser.Browser.open(self, 'http://localhost/')
+
+def test_submit_duplicate_name():
+    """
+
+This test was inspired by bug #723 as testbrowser would pick up the wrong
+button when having the same name twice in a form.
+
+    >>> browser = Browser()
+
+When given a form with two submit buttons that have the same name:
+
+    >>> browser.open('''\
+    ... <html><body>
+    ...   <form action="." method="post" enctype="multipart/form-data">
+    ...      <input type="submit" name="submit_me" value="GOOD" />
+    ...      <input type="submit" name="submit_me" value="BAD" />
+    ...   </form></body></html>
+    ... ''') # doctest: +ELLIPSIS
+    GET / HTTP/1.1
+    ...
+
+We can specify the second button through it's label/value:
+
+    >>> browser.getControl('BAD')
+    <SubmitControl name='submit_me' type='submit'>
+    >>> browser.getControl('BAD').value
+    'BAD'
+    >>> browser.getControl('BAD').click() # doctest: +REPORT_NDIFF
+    POST / HTTP/1.1
+    Content-length: 176
+    Connection: close
+    Content-type: multipart/form-data; boundary=---------------------------100167997466992641913031254
+    Host: localhost
+    User-agent: Python-urllib/2.4
+    <BLANKLINE>
+    -----------------------------100167997466992641913031254
+    Content-disposition: form-data; name="submit_me"
+    <BLANKLINE>
+    BAD
+    -----------------------------100167997466992641913031254--
+    <BLANKLINE>
+
+This also works if the labels have whitespace around them (this tests a
+regression caused by the original fix for the above):
+
+    >>> browser.open('''\
+    ... <html><body>
+    ...   <form action="." method="post" enctype="multipart/form-data">
+    ...      <input type="submit" name="submit_me" value=" GOOD " />
+    ...      <input type="submit" name="submit_me" value=" BAD " />
+    ...   </form></body></html>
+    ... ''') # doctest: +ELLIPSIS
+    GET / HTTP/1.1
+    ...
+    >>> browser.getControl('BAD')
+    <SubmitControl name='submit_me' type='submit'>
+    >>> browser.getControl('BAD').value
+    ' BAD '
+    >>> browser.getControl('BAD').click() # doctest: +REPORT_NDIFF
+    POST / HTTP/1.1
+    Content-length: 176
+    Connection: close
+    Content-type: multipart/form-data; boundary=---------------------------100167997466992641913031254
+    Host: localhost
+    User-agent: Python-urllib/2.4
+    <BLANKLINE>
+    -----------------------------100167997466992641913031254
+    Content-disposition: form-data; name="submit_me"
+    <BLANKLINE>
+     BAD 
+    -----------------------------100167997466992641913031254--
+    <BLANKLINE>
+
+"""
+
+def test_file_upload():
+    """
+
+    >>> browser = Browser()
+
+When given a form with a file-upload
+
+    >>> browser.open('''\
+    ... <html><body>
+    ...   <form action="." method="post" enctype="multipart/form-data">
+    ...      <input name="foo" type="file" />
+    ...      <input type="submit" value="OK" />
+    ...   </form></body></html>
+    ... ''') # doctest: +ELLIPSIS
+    GET / HTTP/1.1
+    ...
+
+Fill in the form value using add_file:
+
+    >>> browser.getControl(name='foo').add_file(
+    ...     StringIO('sample_data'), 'text/foo', 'x.foo')
+    >>> browser.getControl('OK').click()
+    POST / HTTP/1.1
+    Content-length: 173
+    Connection: close
+    Content-type: multipart/form-data; boundary=127.0.0.11000318041146699896411
+    Host: localhost
+    User-agent: Python-urllib/2.99
+    <BLANKLINE>
+    --127.0.0.11000318041146699896411
+    Content-disposition: form-data; name="foo"; filename="x.foo"
+    Content-type: text/foo
+    <BLANKLINE>
+    sample_data
+    --127.0.0.11000318041146699896411--
+    <BLANKLINE>
+
+You can pass a string to add_file:
+
+
+    >>> browser.getControl(name='foo').add_file(
+    ...     'blah blah blah', 'text/blah', 'x.blah')
+    >>> browser.getControl('OK').click()
+    POST / HTTP/1.1
+    Content-length: 178
+    Connection: close
+    Content-type: multipart/form-data; boundary=127.0.0.11000318541146700017052
+    Host: localhost
+    User-agent: Python-urllib/2.98
+    <BLANKLINE>
+    --127.0.0.11000318541146700017052
+    Content-disposition: form-data; name="foo"; filename="x.blah"
+    Content-type: text/blah
+    <BLANKLINE>
+    blah blah blah
+    --127.0.0.11000318541146700017052--
+    <BLANKLINE>
+
+
+    """
+
+
+def test_strip_linebreaks_from_textarea(self):
+    """
+
+    >>> browser = Browser()
+
+According to http://www.w3.org/TR/html4/appendix/notes.html#h-B.3.1 line break
+immediately after start tags or immediately before end tags must be ignored,
+but real browsers only ignore a line break after a start tag.  So if we give
+the following form:
+
+    >>> browser.open('''
+    ... <html><body>
+    ...   <form action="." method="post" enctype="multipart/form-data">
+    ...      <textarea name="textarea">
+    ... Foo
+    ... </textarea>
+    ...   </form></body></html>
+    ... ''') # doctest: +ELLIPSIS
+    GET / HTTP/1.1
+    ...
+
+The value of the textarea won't contain the first line break:
+
+    >>> browser.getControl(name='textarea').value
+    'Foo\\n'
+
+Of course, if we add line breaks, so that there are now two line breaks
+after the start tag, the textarea value will start and end with a line break.
+
+    >>> browser.open('''
+    ... <html><body>
+    ...   <form action="." method="post" enctype="multipart/form-data">
+    ...      <textarea name="textarea">
+    ...
+    ... Foo
+    ... </textarea>
+    ...   </form></body></html>
+    ... ''') # doctest: +ELLIPSIS
+    GET / HTTP/1.1
+    ...
+
+    >>> browser.getControl(name='textarea').value
+    '\\nFoo\\n'
+
+Also, if there is some other whitespace after the start tag, it will be preserved.
+
+    >>> browser.open('''
+    ... <html><body>
+    ...   <form action="." method="post" enctype="multipart/form-data">
+    ...      <textarea name="textarea">  Foo  </textarea>
+    ...   </form></body></html>
+    ... ''') # doctest: +ELLIPSIS
+    GET / HTTP/1.1
+    ...
+
+    >>> browser.getControl(name='textarea').value
+    '  Foo  '
+    """
+
+class win32CRLFtransformer(object):
+    def sub(self, replacement, text):
+        return text.replace(r'\r','')
+
+checker = renormalizing.RENormalizing([
+    (re.compile(r'^--\S+\.\S+\.\S+', re.M), '-'*30),
+    (re.compile(r'boundary=\S+\.\S+\.\S+'), 'boundary='+'-'*30),
+    (re.compile(r'^---{10}.*', re.M), '-'*30),
+    (re.compile(r'boundary=-{10}.*'), 'boundary='+'-'*30),
+    (re.compile(r'User-agent:\s+\S+'), 'User-agent: Python-urllib/2.4'),
+    (re.compile(r'HTTP_USER_AGENT:\s+\S+'), 'HTTP_USER_AGENT: Python-urllib/2.4'),
+    (re.compile(r'Content-[Ll]ength:.*'), 'Content-Length: 123'),
+    (re.compile(r'Status: 200.*'), 'Status: 200 OK'),
+    (win32CRLFtransformer(), None),
+    (re.compile(r'User-Agent: Python-urllib/2.5'), 'User-agent: Python-urllib/2.4'),
+    (re.compile(r'User-Agent: Python-urllib/2.6'), 'User-agent: Python-urllib/2.4'),
+    (re.compile(r'Host: localhost'), 'Connection: close'),
+    (re.compile(r'Content-Type: '), 'Content-type: '),
+    ])
+
+TestBrowserLayer = functional.ZCMLLayer(
+    os.path.join(os.path.split(__file__)[0], 'ftests/ftesting.zcml'),
+    __name__, 'TestBrowserLayer', allow_teardown=True)
+
+def test_suite():
+    flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+
+    readme = FunctionalDocFileSuite('README.txt', optionflags=flags,
+        checker=checker)
+    readme.layer = TestBrowserLayer
+
+    fixed_bugs = FunctionalDocFileSuite('fixed-bugs.txt', optionflags=flags)
+    fixed_bugs.layer = TestBrowserLayer
+
+    wire = FunctionalDocFileSuite('over_the_wire.txt', optionflags=flags)
+    wire.level = 2
+    wire.layer = TestBrowserLayer
+
+    this_file = doctest.DocTestSuite(checker=checker)
+
+    return unittest.TestSuite((this_file, readme, fixed_bugs, wire))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')



More information about the Checkins mailing list