[Checkins] SVN: zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/ Attempt to avoid subversion bailing ungraciously on multiple tree conflicts when I try merge.
Brian Sutherland
jinty at web.de
Sun Jan 30 06:36:35 EST 2011
Log message for revision 120000:
Attempt to avoid subversion bailing ungraciously on multiple tree conflicts when I try merge.
Changed:
D zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests.py
A zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests_old.py
A zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/webtest.py
D zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/wsgi.py
-=-
Deleted: zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests.py
===================================================================
--- zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests.py 2011-01-30 11:22:24 UTC (rev 119999)
+++ zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests.py 2011-01-30 11:36:35 UTC (rev 120000)
@@ -1,531 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Foundation 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
-"""
-import unittest
-import cStringIO
-import doctest
-import httplib
-import mechanize
-import os
-import re
-import socket
-import sys
-
-from webtest import TestApp
-from zope.app.testing.functional import FunctionalDocFileSuite
-import zope.app.testing.functional
-import zope.testing.renormalizing
-
-import zope.testbrowser.browser
-from zope.testbrowser.testing import Browser as TestingBrowser
-from zope.testbrowser.wsgi import Browser as WSGIBrowser
-from zope.testbrowser.ftests.wsgitestapp import WSGITestApplication
-
-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 ``mechanize`` 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 ``mechanize`` compatible response.
-
- The goal of this method is to convert the Zope Publisher's response to
- a ``mechanize`` 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(cStringIO.StringIO(headers), 0)
- self.content_as_file = cStringIO.StringIO(self.content)
-
- def read(self, amt=None):
- return self.content_as_file.read(amt)
-
- def close(self):
- """To overcome changes in mechanize and socket in python2.5"""
- pass
-
-
-class FauxHTTPHandler(mechanize.HTTPHandler):
-
- http_request = mechanize.HTTPHandler.do_request_
-
- def http_open(self, req):
- """Open an HTTP connection having a ``mechanize`` 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_default_error": mechanize.HTTPDefaultErrorHandler,
-
- # feature handlers
- "_authen": mechanize.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_default_error"]
- default_features = ["_authen", "_redirect", "_cookies"]
-
-
-class Browser(zope.testbrowser.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',
- url='http://localhost/'):
- set_next_response(body, headers, status, reason)
- zope.testbrowser.browser.Browser.open(self, url)
-
-
-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-type: multipart/form-data; ...
- Content-disposition: form-data; name="submit_me"
- <BLANKLINE>
- BAD
- ...
-
-
- 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 +ELLIPSIS
- POST / HTTP/1.1
- ...
- Content-type: multipart/form-data; ...
- Content-disposition: form-data; name="submit_me"
- <BLANKLINE>
- BAD
- ...
-"""
-
-
-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(
- ... cStringIO.StringIO('sample_data'), 'text/foo', 'x.foo')
- >>> browser.getControl('OK').click() # doctest: +REPORT_NDIFF +ELLIPSIS
- POST / HTTP/1.1
- ...
- Content-type: multipart/form-data; ...
- Content-disposition: form-data; name="foo"; filename="x.foo"
- Content-type: text/foo
- <BLANKLINE>
- sample_data
- ...
-
-
- 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() # doctest: +REPORT_NDIFF +ELLIPSIS
- POST / HTTP/1.1
- ...
- Content-type: multipart/form-data; ...
- Content-disposition: form-data; name="foo"; filename="x.blah"
- Content-type: text/blah
- <BLANKLINE>
- blah blah blah
- ...
- """
-
-def test_submit_gets_referrer():
- """
- Test for bug #98437: No HTTP_REFERER was sent when submitting a form.
-
- >>> browser = Browser()
-
-
- A simple form for testing, like abobe.
-
- >>> browser.open('''\
- ... <html><body>
- ... <form id="form" action="." method="post"
- ... enctype="multipart/form-data">
- ... <input type="submit" name="submit_me" value="GOOD" />
- ... </form></body></html>
- ... ''') # doctest: +ELLIPSIS
- GET / HTTP/1.1
- ...
-
-
- Now submit the form, and see that we get an referrer along:
-
- >>> form = browser.getForm(id='form')
- >>> form.submit(name='submit_me') # doctest: +ELLIPSIS
- POST / HTTP/1.1
- ...
- Referer: http://localhost/
- ...
-"""
-
-
-def test_new_instance_no_contents_should_not_fail(self):
- """
- When first instantiated, the browser has no contents.
- (Regression test for <http://bugs.launchpad.net/zope3/+bug/419119>)
-
- >>> browser = Browser()
- >>> print browser.contents
- None
- """
-
-
-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 '
- """
-
-
-def test_relative_link():
- """
- RFC 1808 specifies how relative URLs should be resolved, let's see
- that we conform to it. Let's start with a simple example.
-
- >>> browser = Browser()
- >>> browser.open('''\
- ... <html><body>
- ... <a href="foo">link</a>
- ... </body></html>
- ... ''', url='http://localhost/bar') # doctest: +ELLIPSIS
- GET /bar HTTP/1.1
- ...
-
- >>> link = browser.getLink('link')
- >>> link.url
- 'http://localhost/foo'
-
-
- It's possible to have a relative URL consisting of only a query part. In
- that case it should simply be appended to the base URL.
-
- >>> browser.open('''\
- ... <html><body>
- ... <a href="?key=value">link</a>
- ... </body></html>
- ... ''', url='http://localhost/bar') # doctest: +ELLIPSIS
- GET /bar HTTP/1.1
- ...
-
- >>> link = browser.getLink('link')
- >>> link.url
- 'http://localhost/bar?key=value'
-
-
- In the example above, the base URL was the page URL, but we can also
- specify a base URL using a <base> tag.
-
- >>> browser.open('''\
- ... <html><head><base href="http://localhost/base" /></head><body>
- ... <a href="?key=value">link</a>
- ... </body></html>
- ... ''', url='http://localhost/base/bar') # doctest: +ELLIPSIS
- GET /base/bar HTTP/1.1
- ...
-
- >>> link = browser.getLink('link')
- >>> link.url
- 'http://localhost/base?key=value'
- """
-
-
-class win32CRLFtransformer(object):
- def sub(self, replacement, text):
- return text.replace(r'\r', '')
-
-checker = zope.testing.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: '),
- (re.compile(r'Content-Disposition: '), 'Content-disposition: '),
- (re.compile(r'; charset=UTF-8'), ';charset=utf-8'),
- # webtest quotes cookies differently to zope.publisher
- (re.compile(r'\'comment\': \'"silly billy"\','), "'comment': 'silly%20billy',"),
- # webtest seems to expire cookies one second before the date set in set_cookie
- (re.compile(r"'expires': datetime.datetime\(2029, 12, 31, 23, 59, 59, tzinfo=<UTC>\),"), "'expires': datetime.datetime(2030, 1, 1, 0, 0, tzinfo=<UTC>),"),
- (re.compile(r"Object: <WSGI application>,"), "Object: <zope.site.folder.Folder object at ...>,"),
- ])
-
-TestBrowserLayer = zope.app.testing.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
-
- this_file = doctest.DocTestSuite(checker=checker)
- wire = doctest.DocFileSuite('over_the_wire.txt', optionflags=flags)
- wire.level = 2
-
- tests = [this_file, wire]
-
- # Zope Publisher Tests
- zope_publisher = FunctionalDocFileSuite('zope-publisher.txt', optionflags=flags,
- checker=checker)
- zope_publisher.layer = TestBrowserLayer
-
- tests.append(zope_publisher)
-
- # WSGI Browser tests
- def make_browser(*args, **kw):
- app = WSGITestApplication()
- test_app = TestApp(app)
- return WSGIBrowser(test_app, *args, **kw)
- globals = dict(Browser=make_browser)
-
- readme = doctest.DocFileSuite('README.txt', optionflags=flags,
- checker=checker, globs=globals)
-
- cookies = doctest.DocFileSuite('cookies.txt', optionflags=flags,
- checker=checker, globs=globals)
-
- fixed_bugs = doctest.DocFileSuite('fixed-bugs.txt', optionflags=flags,
- globs=globals)
-
- wsgi = doctest.DocFileSuite('wsgi.txt', optionflags=flags,
- checker=checker, globs=globals)
-
- tests.extend([readme, cookies, fixed_bugs, wsgi])
-
- return unittest.TestSuite(tests)
-
-
-def run_suite(suite):
- runner = unittest.TextTestRunner(sys.stdout, verbosity=1)
- result = runner.run(suite)
- if not result.wasSuccessful():
- if len(result.errors) == 1 and not result.failures:
- err = result.errors[0][1]
- elif len(result.failures) == 1 and not result.errors:
- err = result.failures[0][1]
- else:
- err = "errors occurred; run in verbose mode for details"
- print err
-
-if __name__ == "__main__":
- run_suite(test_suite())
Copied: zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests_old.py (from rev 119678, zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests.py)
===================================================================
--- zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests_old.py (rev 0)
+++ zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/tests_old.py 2011-01-30 11:36:35 UTC (rev 120000)
@@ -0,0 +1,531 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Foundation 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
+"""
+import unittest
+import cStringIO
+import doctest
+import httplib
+import mechanize
+import os
+import re
+import socket
+import sys
+
+from webtest import TestApp
+from zope.app.testing.functional import FunctionalDocFileSuite
+import zope.app.testing.functional
+import zope.testing.renormalizing
+
+import zope.testbrowser.browser
+from zope.testbrowser.testing import Browser as TestingBrowser
+from zope.testbrowser.wsgi import Browser as WSGIBrowser
+from zope.testbrowser.ftests.wsgitestapp import WSGITestApplication
+
+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 ``mechanize`` 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 ``mechanize`` compatible response.
+
+ The goal of this method is to convert the Zope Publisher's response to
+ a ``mechanize`` 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(cStringIO.StringIO(headers), 0)
+ self.content_as_file = cStringIO.StringIO(self.content)
+
+ def read(self, amt=None):
+ return self.content_as_file.read(amt)
+
+ def close(self):
+ """To overcome changes in mechanize and socket in python2.5"""
+ pass
+
+
+class FauxHTTPHandler(mechanize.HTTPHandler):
+
+ http_request = mechanize.HTTPHandler.do_request_
+
+ def http_open(self, req):
+ """Open an HTTP connection having a ``mechanize`` 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_default_error": mechanize.HTTPDefaultErrorHandler,
+
+ # feature handlers
+ "_authen": mechanize.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_default_error"]
+ default_features = ["_authen", "_redirect", "_cookies"]
+
+
+class Browser(zope.testbrowser.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',
+ url='http://localhost/'):
+ set_next_response(body, headers, status, reason)
+ zope.testbrowser.browser.Browser.open(self, url)
+
+
+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-type: multipart/form-data; ...
+ Content-disposition: form-data; name="submit_me"
+ <BLANKLINE>
+ BAD
+ ...
+
+
+ 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 +ELLIPSIS
+ POST / HTTP/1.1
+ ...
+ Content-type: multipart/form-data; ...
+ Content-disposition: form-data; name="submit_me"
+ <BLANKLINE>
+ BAD
+ ...
+"""
+
+
+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(
+ ... cStringIO.StringIO('sample_data'), 'text/foo', 'x.foo')
+ >>> browser.getControl('OK').click() # doctest: +REPORT_NDIFF +ELLIPSIS
+ POST / HTTP/1.1
+ ...
+ Content-type: multipart/form-data; ...
+ Content-disposition: form-data; name="foo"; filename="x.foo"
+ Content-type: text/foo
+ <BLANKLINE>
+ sample_data
+ ...
+
+
+ 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() # doctest: +REPORT_NDIFF +ELLIPSIS
+ POST / HTTP/1.1
+ ...
+ Content-type: multipart/form-data; ...
+ Content-disposition: form-data; name="foo"; filename="x.blah"
+ Content-type: text/blah
+ <BLANKLINE>
+ blah blah blah
+ ...
+ """
+
+def test_submit_gets_referrer():
+ """
+ Test for bug #98437: No HTTP_REFERER was sent when submitting a form.
+
+ >>> browser = Browser()
+
+
+ A simple form for testing, like abobe.
+
+ >>> browser.open('''\
+ ... <html><body>
+ ... <form id="form" action="." method="post"
+ ... enctype="multipart/form-data">
+ ... <input type="submit" name="submit_me" value="GOOD" />
+ ... </form></body></html>
+ ... ''') # doctest: +ELLIPSIS
+ GET / HTTP/1.1
+ ...
+
+
+ Now submit the form, and see that we get an referrer along:
+
+ >>> form = browser.getForm(id='form')
+ >>> form.submit(name='submit_me') # doctest: +ELLIPSIS
+ POST / HTTP/1.1
+ ...
+ Referer: http://localhost/
+ ...
+"""
+
+
+def test_new_instance_no_contents_should_not_fail(self):
+ """
+ When first instantiated, the browser has no contents.
+ (Regression test for <http://bugs.launchpad.net/zope3/+bug/419119>)
+
+ >>> browser = Browser()
+ >>> print browser.contents
+ None
+ """
+
+
+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 '
+ """
+
+
+def test_relative_link():
+ """
+ RFC 1808 specifies how relative URLs should be resolved, let's see
+ that we conform to it. Let's start with a simple example.
+
+ >>> browser = Browser()
+ >>> browser.open('''\
+ ... <html><body>
+ ... <a href="foo">link</a>
+ ... </body></html>
+ ... ''', url='http://localhost/bar') # doctest: +ELLIPSIS
+ GET /bar HTTP/1.1
+ ...
+
+ >>> link = browser.getLink('link')
+ >>> link.url
+ 'http://localhost/foo'
+
+
+ It's possible to have a relative URL consisting of only a query part. In
+ that case it should simply be appended to the base URL.
+
+ >>> browser.open('''\
+ ... <html><body>
+ ... <a href="?key=value">link</a>
+ ... </body></html>
+ ... ''', url='http://localhost/bar') # doctest: +ELLIPSIS
+ GET /bar HTTP/1.1
+ ...
+
+ >>> link = browser.getLink('link')
+ >>> link.url
+ 'http://localhost/bar?key=value'
+
+
+ In the example above, the base URL was the page URL, but we can also
+ specify a base URL using a <base> tag.
+
+ >>> browser.open('''\
+ ... <html><head><base href="http://localhost/base" /></head><body>
+ ... <a href="?key=value">link</a>
+ ... </body></html>
+ ... ''', url='http://localhost/base/bar') # doctest: +ELLIPSIS
+ GET /base/bar HTTP/1.1
+ ...
+
+ >>> link = browser.getLink('link')
+ >>> link.url
+ 'http://localhost/base?key=value'
+ """
+
+
+class win32CRLFtransformer(object):
+ def sub(self, replacement, text):
+ return text.replace(r'\r', '')
+
+checker = zope.testing.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: '),
+ (re.compile(r'Content-Disposition: '), 'Content-disposition: '),
+ (re.compile(r'; charset=UTF-8'), ';charset=utf-8'),
+ # webtest quotes cookies differently to zope.publisher
+ (re.compile(r'\'comment\': \'"silly billy"\','), "'comment': 'silly%20billy',"),
+ # webtest seems to expire cookies one second before the date set in set_cookie
+ (re.compile(r"'expires': datetime.datetime\(2029, 12, 31, 23, 59, 59, tzinfo=<UTC>\),"), "'expires': datetime.datetime(2030, 1, 1, 0, 0, tzinfo=<UTC>),"),
+ (re.compile(r"Object: <WSGI application>,"), "Object: <zope.site.folder.Folder object at ...>,"),
+ ])
+
+TestBrowserLayer = zope.app.testing.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
+
+ this_file = doctest.DocTestSuite(checker=checker)
+ wire = doctest.DocFileSuite('over_the_wire.txt', optionflags=flags)
+ wire.level = 2
+
+ tests = [this_file, wire]
+
+ # Zope Publisher Tests
+ zope_publisher = FunctionalDocFileSuite('zope-publisher.txt', optionflags=flags,
+ checker=checker)
+ zope_publisher.layer = TestBrowserLayer
+
+ tests.append(zope_publisher)
+
+ # WSGI Browser tests
+ def make_browser(*args, **kw):
+ app = WSGITestApplication()
+ test_app = TestApp(app)
+ return WSGIBrowser(test_app, *args, **kw)
+ globals = dict(Browser=make_browser)
+
+ readme = doctest.DocFileSuite('README.txt', optionflags=flags,
+ checker=checker, globs=globals)
+
+ cookies = doctest.DocFileSuite('cookies.txt', optionflags=flags,
+ checker=checker, globs=globals)
+
+ fixed_bugs = doctest.DocFileSuite('fixed-bugs.txt', optionflags=flags,
+ globs=globals)
+
+ wsgi = doctest.DocFileSuite('wsgi.txt', optionflags=flags,
+ checker=checker, globs=globals)
+
+ tests.extend([readme, cookies, fixed_bugs, wsgi])
+
+ return unittest.TestSuite(tests)
+
+
+def run_suite(suite):
+ runner = unittest.TextTestRunner(sys.stdout, verbosity=1)
+ result = runner.run(suite)
+ if not result.wasSuccessful():
+ if len(result.errors) == 1 and not result.failures:
+ err = result.errors[0][1]
+ elif len(result.failures) == 1 and not result.errors:
+ err = result.failures[0][1]
+ else:
+ err = "errors occurred; run in verbose mode for details"
+ print err
+
+if __name__ == "__main__":
+ run_suite(test_suite())
Copied: zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/webtest.py (from rev 119677, zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/wsgi.py)
===================================================================
--- zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/webtest.py (rev 0)
+++ zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/webtest.py 2011-01-30 11:36:35 UTC (rev 120000)
@@ -0,0 +1,152 @@
+##############################################################################
+#
+# Copyright (c) 2010 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.
+#
+##############################################################################
+"""WSGI-specific testing code
+"""
+import cStringIO
+import Cookie
+import httplib
+import socket
+import sys
+
+import mechanize
+
+import zope.testbrowser.browser
+import zope.testbrowser.testing
+
+class WSGIConnection(object):
+ """A ``mechanize`` compatible connection object."""
+
+ def __init__(self, test_app, host, timeout=None):
+ self._test_app = test_app
+ self.host = host
+
+ def set_debuglevel(self, level):
+ pass
+
+ def _quote(self, url):
+ # XXX: is this necessary with WebTest? Was cargeo-culted from the
+ # Zope Publisher Connection
+ 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_header = headers.get(handle_errors_key, True)
+ if handle_errors_key in headers:
+ del headers[handle_errors_key]
+
+ # Translate string to boolean.
+ handle_errors = {'False': False}.get(handle_errors_header, True)
+ extra_environ = {}
+ if not handle_errors:
+ # There doesn't seem to be a "Right Way" to do this
+ extra_environ['wsgi.handleErrors'] = False # zope.app.wsgi does this
+ extra_environ['paste.throw_errors'] = True # the paste way of doing this
+
+ scheme_key = 'X-Zope-Scheme'
+ extra_environ['wsgi.url_scheme'] = headers.get(scheme_key, 'http')
+ if scheme_key in headers:
+ del headers[scheme_key]
+
+ app = self._test_app
+
+ # Here we do a complicated dance to sync the webtest apps idea of what
+ # cookies there are with the testbrowsers. It's not quite perfect as
+ # they can still get un-synced if you don't execute a request via the
+ # testbrowser. But that's a veryvery edge case.
+ app.cookies.clear()
+ for h, v in headers.items():
+ if h.lower() == 'cookie':
+ cookies = Cookie.SimpleCookie()
+ cookies.load(v)
+ for key, morsel in cookies.items():
+ app.cookies[key] = morsel.value
+ break
+
+ # pass the request to webtest
+ if method == 'GET':
+ assert not body, body
+ response = app.get(url, headers=headers, expect_errors=True, extra_environ=extra_environ)
+ elif method == 'POST':
+ response = app.post(url, body, headers=headers, expect_errors=True, extra_environ=extra_environ)
+ else:
+ raise Exception('Couldnt handle method %s' % method)
+
+ self.response = response
+
+ def getresponse(self):
+ """Return a ``mechanize`` compatible response.
+
+ The goal of ths method is to convert the WebTest's reseponse to
+ a ``mechanize`` compatible response, which is also understood by
+ mechanize.
+ """
+ response = self.response
+ status = int(response.status[:3])
+ reason = response.status[4:]
+
+ headers = response.headers.items()
+ headers.sort()
+ headers.insert(0, ('Status', response.status))
+ headers = '\r\n'.join('%s: %s' % h for h in headers)
+ content = response.body
+ return zope.testbrowser.testing.Response(content, headers, status, reason)
+
+
+class WSGIHTTPHandler(zope.testbrowser.testing.HTTPHandler):
+
+ def __init__(self, test_app, *args, **kw):
+ self._test_app = test_app
+ zope.testbrowser.testing.HTTPHandler.__init__(self, *args, **kw)
+
+ def _connect(self, *args, **kw):
+ return WSGIConnection(self._test_app, *args, **kw)
+
+ def https_request(self, req):
+ req.add_unredirected_header('X-Zope-Scheme', 'https')
+ return self.http_request(req)
+
+
+class WSGIMechanizeBrowser(zope.testbrowser.testing.MechanizeBrowser):
+ """Special ``mechanize`` browser using the WSGI HTTP handler."""
+
+ def __init__(self, test_app, *args, **kw):
+ self._test_app = test_app
+ zope.testbrowser.testing.MechanizeBrowser.__init__(self, *args, **kw)
+
+ def _http_handler(self, *args, **kw):
+ return WSGIHTTPHandler(self._test_app, *args, **kw)
+
+
+class Browser(zope.testbrowser.browser.Browser):
+ """A WSGI `testbrowser` Browser that uses a WebTest wrapped WSGI app."""
+
+ def __init__(self, test_app, url=None):
+ mech_browser = WSGIMechanizeBrowser(test_app)
+ super(Browser, self).__init__(url=url, mech_browser=mech_browser)
Deleted: zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/wsgi.py
===================================================================
--- zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/wsgi.py 2011-01-30 11:22:24 UTC (rev 119999)
+++ zope.testbrowser/branches/jinty-webtest2/src/zope/testbrowser/wsgi.py 2011-01-30 11:36:35 UTC (rev 120000)
@@ -1,152 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2010 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.
-#
-##############################################################################
-"""WSGI-specific testing code
-"""
-import cStringIO
-import Cookie
-import httplib
-import socket
-import sys
-
-import mechanize
-
-import zope.testbrowser.browser
-import zope.testbrowser.testing
-
-class WSGIConnection(object):
- """A ``mechanize`` compatible connection object."""
-
- def __init__(self, test_app, host, timeout=None):
- self._test_app = test_app
- self.host = host
-
- def set_debuglevel(self, level):
- pass
-
- def _quote(self, url):
- # XXX: is this necessary with WebTest? Was cargeo-culted from the
- # Zope Publisher Connection
- 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_header = headers.get(handle_errors_key, True)
- if handle_errors_key in headers:
- del headers[handle_errors_key]
-
- # Translate string to boolean.
- handle_errors = {'False': False}.get(handle_errors_header, True)
- extra_environ = {}
- if not handle_errors:
- # There doesn't seem to be a "Right Way" to do this
- extra_environ['wsgi.handleErrors'] = False # zope.app.wsgi does this
- extra_environ['paste.throw_errors'] = True # the paste way of doing this
-
- scheme_key = 'X-Zope-Scheme'
- extra_environ['wsgi.url_scheme'] = headers.get(scheme_key, 'http')
- if scheme_key in headers:
- del headers[scheme_key]
-
- app = self._test_app
-
- # Here we do a complicated dance to sync the webtest apps idea of what
- # cookies there are with the testbrowsers. It's not quite perfect as
- # they can still get un-synced if you don't execute a request via the
- # testbrowser. But that's a veryvery edge case.
- app.cookies.clear()
- for h, v in headers.items():
- if h.lower() == 'cookie':
- cookies = Cookie.SimpleCookie()
- cookies.load(v)
- for key, morsel in cookies.items():
- app.cookies[key] = morsel.value
- break
-
- # pass the request to webtest
- if method == 'GET':
- assert not body, body
- response = app.get(url, headers=headers, expect_errors=True, extra_environ=extra_environ)
- elif method == 'POST':
- response = app.post(url, body, headers=headers, expect_errors=True, extra_environ=extra_environ)
- else:
- raise Exception('Couldnt handle method %s' % method)
-
- self.response = response
-
- def getresponse(self):
- """Return a ``mechanize`` compatible response.
-
- The goal of ths method is to convert the WebTest's reseponse to
- a ``mechanize`` compatible response, which is also understood by
- mechanize.
- """
- response = self.response
- status = int(response.status[:3])
- reason = response.status[4:]
-
- headers = response.headers.items()
- headers.sort()
- headers.insert(0, ('Status', response.status))
- headers = '\r\n'.join('%s: %s' % h for h in headers)
- content = response.body
- return zope.testbrowser.testing.Response(content, headers, status, reason)
-
-
-class WSGIHTTPHandler(zope.testbrowser.testing.HTTPHandler):
-
- def __init__(self, test_app, *args, **kw):
- self._test_app = test_app
- zope.testbrowser.testing.HTTPHandler.__init__(self, *args, **kw)
-
- def _connect(self, *args, **kw):
- return WSGIConnection(self._test_app, *args, **kw)
-
- def https_request(self, req):
- req.add_unredirected_header('X-Zope-Scheme', 'https')
- return self.http_request(req)
-
-
-class WSGIMechanizeBrowser(zope.testbrowser.testing.MechanizeBrowser):
- """Special ``mechanize`` browser using the WSGI HTTP handler."""
-
- def __init__(self, test_app, *args, **kw):
- self._test_app = test_app
- zope.testbrowser.testing.MechanizeBrowser.__init__(self, *args, **kw)
-
- def _http_handler(self, *args, **kw):
- return WSGIHTTPHandler(self._test_app, *args, **kw)
-
-
-class Browser(zope.testbrowser.browser.Browser):
- """A WSGI `testbrowser` Browser that uses a WebTest wrapped WSGI app."""
-
- def __init__(self, test_app, url=None):
- mech_browser = WSGIMechanizeBrowser(test_app)
- super(Browser, self).__init__(url=url, mech_browser=mech_browser)
More information about the checkins
mailing list