[Zope3-checkins] SVN: Zope3/branches/3.2/ - Backport of fix for bug #723.

Christian Theune ct at gocept.com
Sun Dec 10 18:29:18 EST 2006


Log message for revision 71516:
   - Backport of fix for bug #723.
  

Changed:
  U   Zope3/branches/3.2/doc/CHANGES.txt
  U   Zope3/branches/3.2/src/zope/testbrowser/browser.py
  A   Zope3/branches/3.2/src/zope/testbrowser/tests.py

-=-
Modified: Zope3/branches/3.2/doc/CHANGES.txt
===================================================================
--- Zope3/branches/3.2/doc/CHANGES.txt	2006-12-10 23:14:38 UTC (rev 71515)
+++ Zope3/branches/3.2/doc/CHANGES.txt	2006-12-10 23:29:17 UTC (rev 71516)
@@ -10,6 +10,9 @@
 
     Bug fixes
 
+      - Fixed bug #723: Testbrowser was handling multiple submit buttons with
+        the same name incorrectly.
+
       - Fixed HTML rendered by ItemsMultiDisplayWidget: The 'name' attribute
         is not allowed in list tags and 'type' has a different meaning.
 

Modified: Zope3/branches/3.2/src/zope/testbrowser/browser.py
===================================================================
--- Zope3/branches/3.2/src/zope/testbrowser/browser.py	2006-12-10 23:14:38 UTC (rev 71515)
+++ Zope3/branches/3.2/src/zope/testbrowser/browser.py	2006-12-10 23:29:17 UTC (rev 71516)
@@ -355,7 +355,7 @@
     def _clickSubmit(self, form, control, coord):
         self._start_timer()
         self.mech_browser.open(form.click(
-                    id=control.id, name=control.name, coord=coord))
+            id=control.id, name=control.name, label=control.value, coord=coord))
         self._stop_timer()
 
     def _changed(self):

Added: Zope3/branches/3.2/src/zope/testbrowser/tests.py
===================================================================
--- Zope3/branches/3.2/src/zope/testbrowser/tests.py	2006-12-10 23:14:38 UTC (rev 71515)
+++ Zope3/branches/3.2/src/zope/testbrowser/tests.py	2006-12-10 23:29:17 UTC (rev 71516)
@@ -0,0 +1,208 @@
+##############################################################################
+#
+# Copyright (c) 2004-2006 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.
+#
+##############################################################################
+"""Regression tests.
+
+$Id$
+"""
+
+import unittest
+import httplib
+import urllib2
+from cStringIO import StringIO
+
+import ClientCookie
+import mechanize
+
+from zope.testbrowser import browser
+from zope.testing import doctest
+
+
+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)
+
+
+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": urllib2.HTTPErrorProcessor,
+        "_http_request_upgrade": ClientCookie.HTTPRequestUpgradeProcessor,
+        "_http_default_error": urllib2.HTTPDefaultErrorHandler,
+
+        # feature handlers
+        "_authen": urllib2.HTTPBasicAuthHandler,
+        "_redirect": ClientCookie.HTTPRedirectHandler,
+        "_cookies": mechanize.Browser.handler_classes['_cookies'],
+        "_refresh": ClientCookie.HTTPRefreshProcessor,
+        "_referer": mechanize.Browser.handler_classes['_referer'],
+        "_equiv": ClientCookie.HTTPEquivProcessor,
+        "_seek": ClientCookie.SeekableProcessor,
+        }
+
+    default_schemes = ["http"]
+    default_others = ["_http_error", "_http_request_upgrade",
+                      "_http_default_error"]
+    default_features = ["_authen", "_redirect", "_cookies", "_seek"]
+
+
+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 +ELLIPSIS
+    POST / HTTP/1.1
+    Content-length: ...
+    Connection: close
+    Content-type: multipart/form-data; boundary=...
+    Host: localhost
+    User-agent: Python-urllib/2.4
+    <BLANKLINE>
+    ...
+    Content-disposition: form-data; name="submit_me"
+    <BLANKLINE>
+    BAD
+    ...
+    <BLANKLINE>
+"""
+
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocTestSuite(),
+        ))


Property changes on: Zope3/branches/3.2/src/zope/testbrowser/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id Rev Date
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list