[Checkins] SVN: zope.app.form/trunk/ * Get ready for release.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Wed Oct 24 11:27:12 EDT 2007
Log message for revision 81040:
* Get ready for release.
* Merge functional tests into generic tests/ folder.
* Made sure the long description will render.
Changed:
U zope.app.form/trunk/CHANGES.txt
A zope.app.form/trunk/README.txt
U zope.app.form/trunk/buildout.cfg
U zope.app.form/trunk/setup.py
_U zope.app.form/trunk/src/
D zope.app.form/trunk/src/zope/app/form/browser/ftests/
U zope.app.form/trunk/src/zope/app/form/browser/i18n.txt
A zope.app.form/trunk/src/zope/app/form/browser/tests/i18n.zcml
A zope.app.form/trunk/src/zope/app/form/browser/tests/locales/
U zope.app.form/trunk/src/zope/app/form/browser/tests/support.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_booleanradiowidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_checkboxwidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_datetimewidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_decimalwidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_editview.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_filewidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_floatwidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_i18n.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_intwidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_objectwidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_selectwidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textareawidget.py
A zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textwidget.py
U zope.app.form/trunk/src/zope/app/form/browser/widgets.txt
U zope.app.form/trunk/src/zope/app/form/ftesting.zcml
U zope.app.form/trunk/src/zope/app/form/utility.py
-=-
Modified: zope.app.form/trunk/CHANGES.txt
===================================================================
--- zope.app.form/trunk/CHANGES.txt 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/CHANGES.txt 2007-10-24 15:27:12 UTC (rev 81040)
@@ -1,13 +1,15 @@
=======
-Changes
+CHANGES
=======
-3.4.0
-=====
+3.4.0 (2007-10-24)
+==================
- - zope.app.form now supports Python2.5
+- ``zope.app.form`` now supports Python2.5
+- Initial release independent of the main Zope tree.
+
Before 3.4
==========
Added: zope.app.form/trunk/README.txt
===================================================================
--- zope.app.form/trunk/README.txt (rev 0)
+++ zope.app.form/trunk/README.txt 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,3 @@
+This package provides a form and widget framework for Zope 3. It also
+implements a few high-level ZCML directives for declaring forms. More advanced
+alternatives are implemented in ``zope.formlib`` and ``z3c.form``.
Property changes on: zope.app.form/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: zope.app.form/trunk/buildout.cfg
===================================================================
--- zope.app.form/trunk/buildout.cfg 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/buildout.cfg 2007-10-24 15:27:12 UTC (rev 81040)
@@ -1,9 +1,7 @@
[buildout]
develop = .
parts = test
-find-links = http://download.zope.org/distribution/
[test]
recipe = zc.recipe.testrunner
-defaults = ['--tests-pattern', '^f?tests$']
eggs = zope.app.form [test]
Modified: zope.app.form/trunk/setup.py
===================================================================
--- zope.app.form/trunk/setup.py 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/setup.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -1,16 +1,68 @@
+##############################################################################
+#
+# 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.app.form package
+
+$Id: setup.py 81002 2007-10-24 01:19:47Z srichter $
+"""
+import os
from setuptools import setup, find_packages
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
setup(name='zope.app.form',
- version = '3.4.0b3',
- url='http://svn.zope.org/zope.app.form',
+ version = '3.4.0',
author='Zope Corporation and Contributors',
author_email='zope3-dev at zope.org',
-
- packages=find_packages("src"),
- package_dir={"": "src"},
-
- namespace_packages=["zope", "zope.app"],
- include_package_data=True,
+ description='The Original Zope 3 Form Framework',
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ 'Detailed Documentation\n'
+ '----------------------\n'
+ + '\n\n' +
+ read('src', 'zope', 'app', 'form', 'browser', 'README.txt')
+ + '\n\n' +
+ read('src', 'zope', 'app', 'form', 'browser', 'widgets.txt')
+ + '\n\n' +
+ read('src', 'zope', 'app', 'form', 'browser', 'objectwidget.txt')
+ + '\n\n' +
+ read('src', 'zope', 'app', 'form', 'browser', 'source.txt')
+ + '\n\n' +
+ read('src', 'zope', 'app', 'form', 'browser', 'i18n.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ keywords = "zope3 form widget zcml",
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url='http://cheeseshop.python.org/pypi/zope.app.form',
+ license='ZPL 2.1',
+ packages=find_packages('src'),
+ package_dir = {'': 'src'},
+ namespace_packages=['zope', 'zope.app'],
+ extras_require={"test": ['zope.app.testing',
+ 'zope.app.securitypolicy',
+ 'zope.app.zcmlfiles']},
install_requires=[
"setuptools",
"ZODB3",
@@ -28,11 +80,9 @@
"zope.schema",
"zope.security",
"zope.app.basicskin",
- "zope.location>=3.4.0a1-1",
+ "zope.location",
],
- extras_require={"test": ['zope.app.testing',
- 'zope.app.securitypolicy',
- 'zope.app.zcmlfiles']},
- zip_safe=False,
+ include_package_data = True,
+ zip_safe = False,
)
Property changes on: zope.app.form/trunk/src
___________________________________________________________________
Name: svn:ignore
+ zope.app.form.egg-info
Modified: zope.app.form/trunk/src/zope/app/form/browser/i18n.txt
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/i18n.txt 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/src/zope/app/form/browser/i18n.txt 2007-10-24 15:27:12 UTC (rev 81040)
@@ -1,3 +1,4 @@
+====================
Internationalization
====================
@@ -21,27 +22,27 @@
... Authorization: Basic mgr:mgrpw
... Content-Length: 670
... Content-Type: multipart/form-data; boundary=---------------------------19588947601368617292863650127
- ...
+ ...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.title"
- ...
- ...
+ ...
+ ...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.description"
- ...
- ...
+ ...
+ ...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.somenumber"
- ...
+ ...
... 0
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
- ...
+ ...
... Hinzufxgen
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="add_input_name"
- ...
- ...
+ ...
+ ...
... -----------------------------19588947601368617292863650127--
... """, handle_errors=False)
HTTP/1.1 200 OK
@@ -79,27 +80,27 @@
... Authorization: Basic mgr:mgrpw
... Content-Length: 670
... Content-Type: multipart/form-data; boundary=---------------------------19588947601368617292863650127
- ...
+ ...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.title"
- ...
- ...
+ ...
+ ...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.description"
- ...
- ...
+ ...
+ ...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.somenumber"
- ...
+ ...
... 0
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
- ...
+ ...
... Hinzufxgen
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="add_input_name"
- ...
- ...
+ ...
+ ...
... -----------------------------19588947601368617292863650127--
... """, handle_errors=False)
HTTP/1.1 200 OK
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/i18n.zcml (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/i18n.zcml)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/i18n.zcml (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/i18n.zcml 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,17 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:i18n="http://namespaces.zope.org/i18n"
+ i18n_domain="formtest">
+
+ <browser:addform
+ schema=".test_functional_i18n.IFieldContent"
+ content_factory=".test_functional_i18n.FieldContent"
+ name="addfieldcontent.html"
+ label="Add Field Content"
+ permission="zope.Public"
+ />
+
+ <i18n:registerTranslations directory="locales"/>
+
+</configure>
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/locales (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/locales)
Modified: zope.app.form/trunk/src/zope/app/form/browser/tests/support.py
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/support.py 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/support.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -15,6 +15,9 @@
$Id$
"""
+import re
+from zope.configuration import xmlconfig
+
class VerifyResults(object):
"""Mix-in for test classes with helpers for checking string data."""
@@ -31,3 +34,88 @@
for check in check_list:
self.assert_(result.find(check) < 0,
"%r unexpectedly found in %r" % (check, result))
+
+def registerEditForm(schema, widgets={}):
+ """Registers an edit form for the specified schema.
+
+ widgets is a mapping of field name to dict. The dict for each field must
+ contain a 'class' item, which is the widget class, and any additional
+ widget attributes (e.g. text field size, rows, cols, etc.)
+ """
+ widgetsXml = []
+ for field in widgets:
+ widgetsXml.append('<widget field="%s"' % field)
+ for attr in widgets[field]:
+ widgetsXml.append(' %s="%s"' % (attr, widgets[field][attr]))
+ widgetsXml.append(' />')
+ xmlconfig.string("""
+ <configure xmlns="http://namespaces.zope.org/browser">
+ <include package="zope.app.form.browser" file="meta.zcml" />
+ <editform
+ name="edit.html"
+ schema="%s"
+ permission="zope.View">
+ %s
+ </editform>
+ </configure>
+ """ % (schema.__identifier__, ''.join(widgetsXml)))
+
+
+def defineSecurity(class_, schema):
+ class_ = '%s.%s' % (class_.__module__, class_.__name__)
+ schema = schema.__identifier__
+ xmlconfig.string("""
+ <configure xmlns="http://namespaces.zope.org/zope">
+ <include package="zope.app.component" file="meta.zcml" />
+ <class class="%s">
+ <require
+ permission="zope.Public"
+ interface="%s"
+ set_schema="%s" />
+ </class>
+ </configure>
+ """ % (class_, schema, schema))
+
+
+def defineWidgetView(field_interface, widget_class, view_type):
+ field_interface = field_interface.__identifier__
+ widget_class = '%s.%s' % (widget_class.__module__, widget_class.__name__)
+ view_type = '%s.%s' % (view_type.__module__, view_type.__name__)
+ xmlconfig.string("""
+ <configure xmlns="http://namespaces.zope.org/zope">
+ <include package="zope.app.component" file="meta.zcml" />
+ <view
+ for="%s"
+ type="zope.publisher.interfaces.browser.IBrowserRequest"
+ factory="%s"
+ provides="%s"
+ permission="zope.Public"
+ />
+ </configure>
+ """ % (field_interface, widget_class, view_type))
+
+
+def patternExists(pattern, source, flags=0):
+ return re.search(pattern, source, flags) is not None
+
+
+def validationErrorExists(field, error_msg, source):
+ regex = re.compile(r'%s.*?name="field.(\w+)(?:\.[\w\.]+)?"' % (error_msg,),
+ re.DOTALL)
+ # compile it first because Python 2.3 doesn't allow flags in findall
+ return field in regex.findall(source)
+
+
+def missingInputErrorExists(field, source):
+ return validationErrorExists(field, 'Required input is missing.', source)
+
+
+def invalidValueErrorExists(field, source):
+ # assumes this error is displayed for select elements
+ return patternExists(
+ 'Invalid value.*name="field.%s".*</select>' % field,
+ source, re.DOTALL)
+
+
+def updatedMsgExists(source):
+ return patternExists('<p>Updated .*</p>', source)
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_booleanradiowidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_booleanradiowidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_booleanradiowidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,149 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Radio Widget Functional Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import Bool
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class IFoo(Interface):
+
+ bar = Bool(title=u'Bar')
+
+class Foo(Persistent):
+
+ implements(IFoo)
+
+ def __init__(self):
+ self.bar = True
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IFoo, widgets={
+ 'bar': { 'class': 'zope.app.form.browser.BooleanRadioWidget' }})
+ defineSecurity(Foo, IFoo)
+
+ def test_display_editform(self):
+ self.getRootFolder()['foo'] = Foo()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/foo/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # bar field should be displayed as two radio buttons
+ self.assert_(patternExists(
+ '<input .*checked="checked".*name="field.bar".*type="radio".*'
+ 'value="on".* />',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<input .*name="field.bar".*type="radio".*value="off".* />',
+ response.getBody()))
+
+ # a hidden element is used to note that the field is present
+ self.assert_(patternExists(
+ '<input name="field.bar-empty-marker" type="hidden" value="1".* />',
+ response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['foo'] = Foo()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar' : 'off'})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'foo')
+ self.assertEqual(object.bar, False)
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['foo'] = Foo()
+ transaction.commit()
+
+ # temporarily make bar field not required
+ IFoo['bar'].required = False
+
+ # submit missing value for bar
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar-empty-marker' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # confirm use of missing_value as new object value
+ self.assert_(IFoo['bar'].missing_value is None)
+ object = traverse(self.getRootFolder(), 'foo')
+ self.assert_(object.bar is None)
+
+ # restore bar required state
+ IFoo['bar'].required = True
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['foo'] = Foo()
+ transaction.commit()
+
+ self.assert_(IFoo['bar'].required)
+
+ # submit missing value for required field bar
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar-empty-marker' : '1'})
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('bar', response.getBody()))
+
+
+ def test_invalid_allowed_value(self):
+ self.getRootFolder()['foo'] = Foo()
+ transaction.commit()
+
+ # submit a value for bar isn't allowed
+ response = self.publish('/foo/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.bar' : 'bogus' })
+ self.assertEqual(response.getStatus(), 200)
+
+ self.assert_(validationErrorExists('bar', 'Invalid value',
+ response.getBody()))
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_checkboxwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_checkboxwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_checkboxwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_checkboxwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,150 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Checkbox Widget tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import Bool
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser import CheckBoxWidget
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class IBoolTest(Interface):
+
+ b1 = Bool(
+ required=True)
+
+ b2 = Bool(
+ required=False)
+
+class BoolTest(Persistent):
+
+ implements(IBoolTest)
+
+ def __init__(self):
+ self.b1 = True
+ self.b2 = False
+
+class Test(BrowserTestCase):
+
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IBoolTest)
+ defineSecurity(BoolTest, IBoolTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = BoolTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # b1 and b2 should be displayed in checkbox input fields
+ self.assert_(patternExists(
+ '<input .* checked="checked".* name="field.b1".* ' \
+ 'type="checkbox".* />',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<input .* name="field.b2".* type="checkbox".* />',
+ response.getBody()))
+ # confirm that b2 is *not* checked
+ self.assert_(not patternExists(
+ '<input .* checked="checked".* name="field.b2".* ' \
+ 'type="checkbox".* />',
+ response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = BoolTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.b1' : '',
+ 'field.b2' : 'on' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.b1, False)
+ self.assertEqual(object.b2, True)
+
+
+ def test_unexpected_value(self):
+ object = BoolTest()
+ object.b1 = True
+ object.b2 = True
+ self.getRootFolder()['test'] = object
+ transaction.commit()
+
+ # submit invalud type for text line
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.b1' : 'true',
+ 'field.b2' : 'foo' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # values other than 'on' should be treated as False
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.b1, False)
+ self.assertEqual(object.b2, False)
+
+
+ def test_missing_value(self):
+ # Note: checkbox widget doesn't support a missing value. This
+ # test confirms that one cannot set a Bool field to None.
+
+ self.getRootFolder()['test'] = BoolTest()
+ transaction.commit()
+
+ # confirm default value of b1 is True
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.b1, True)
+
+ # submit missing for b1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.b1' : CheckBoxWidget._missing })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # confirm b1 is not missing
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.b1 != Bool.missing_value)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_datetimewidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_datetimewidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_datetimewidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_datetimewidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,231 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""DateTime Widget Functional Tests
+
+$Id$
+"""
+import unittest
+import re
+import transaction
+from persistent import Persistent
+from datetime import datetime
+
+import zope.security.checker
+from zope.datetime import parseDatetimetz, tzinfo
+from zope.interface import Interface, implements
+from zope.schema import Datetime, Choice
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+
+class IDatetimeTest(Interface):
+
+ d2 = Datetime(
+ required=False)
+
+ d3 = Choice(
+ required=False,
+ values=(
+ datetime(2003, 9, 15, tzinfo=tzinfo(0)),
+ datetime(2003, 10, 15, tzinfo=tzinfo(0))),
+ missing_value=datetime(2000, 1, 1, tzinfo=tzinfo(0)))
+
+ d1 = Datetime(
+ required=True,
+ min=datetime(2003, 1, 1, tzinfo=tzinfo(0)),
+ max=datetime(2020, 12, 31, tzinfo=tzinfo(0)))
+
+class DatetimeTest(Persistent):
+
+ implements(IDatetimeTest)
+
+ def __init__(self):
+ self.d1 = datetime(2003, 4, 6, tzinfo=tzinfo(0))
+ self.d2 = datetime(2003, 8, 6, tzinfo=tzinfo(0))
+ self.d3 = None
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IDatetimeTest)
+ defineSecurity(DatetimeTest, IDatetimeTest)
+
+ def getDateForField(self, field, source):
+ """Returns a datetime object for the specified field in source.
+
+ Returns None if the field value cannot be converted to date.
+ """
+
+ # look in input element first
+ pattern = '<input .* name="field.%s".* value="(.*)".*>' % field
+ m = re.search(pattern, source)
+ if m is None:
+ # look in a select element
+ pattern = '<select .* name="field.%s".*>.*' \
+ '<option value="(.*)" selected>*.</select>' % field
+ m = re.search(pattern, source, re.DOTALL)
+ if m is None:
+ return None
+ return parseDatetimetz(m.group(1))
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+ object = traverse(self.getRootFolder(), 'test')
+
+ # display edit view
+ response = self.publish('/test/edit.html',
+ env={"HTTP_ACCEPT_LANGUAGE": "ru"})
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm date values in form with actual values
+ self.assertEqual(self.getDateForField('d1', response.getBody()),
+ object.d1)
+ self.assertEqual(self.getDateForField('d2', response.getBody()),
+ object.d2)
+ self.assert_(self.getDateForField('d3', response.getBody()) is None)
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d1' : u'2003-02-01 00:00:00+00:00',
+ 'field.d2' : u'2003-02-02 00:00:00+00:00',
+ 'field.d3' : u'2003-10-15 00:00:00+00:00' },
+ env={"HTTP_ACCEPT_LANGUAGE": "en"})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+
+ self.assertEqual(object.d1, datetime(2003, 2, 1, tzinfo=tzinfo(0)))
+ self.assertEqual(object.d2, datetime(2003, 2, 2, tzinfo=tzinfo(0)))
+ self.assertEqual(object.d3, datetime(2003, 10, 15, tzinfo=tzinfo(0)))
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+
+ # submit missing values for d2 and d3
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d2' : '',
+ 'field.d3-empty-marker' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.d2 is None) # default missing_value for dates
+ # 2000-1-1 is missing_value for d3
+ self.assertEqual(object.d3, datetime(2000, 1, 1, tzinfo=tzinfo(0)))
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+
+ # submit missing values for required field d1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d1' : '',
+ 'field.d2' : '',
+ 'field.d3' : '' })
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('d1', response.getBody()))
+ self.assert_(not missingInputErrorExists('d2', response.getBody()))
+ self.assert_(not missingInputErrorExists('d3', response.getBody()))
+
+
+ def test_invalid_value(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+
+ # submit a value for d3 that isn't allowed
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d3' : u'2003-02-01 12:00:00+00:00'})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(invalidValueErrorExists('d3', response.getBody()))
+
+
+ def test_min_max_validation(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+
+ # submit value for d1 that is too low
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d1' : u'2002-12-31 12:00:00+00:00'},
+ env={"HTTP_ACCEPT_LANGUAGE": "en"})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('d1', 'Value is too small',
+ response.getBody()))
+
+ # submit value for i1 that is too high
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d1' : u'2021-12-01 12:00:00+00:00'},
+ env={"HTTP_ACCEPT_LANGUAGE": "en"})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('d1', 'Value is too big',
+ response.getBody()))
+
+
+ def test_omitted_value(self):
+ self.getRootFolder()['test'] = DatetimeTest()
+ transaction.commit()
+
+ # remember default values
+ object = traverse(self.getRootFolder(), 'test')
+ d1 = object.d1
+ d2 = object.d2
+ self.assert_(d2 is not None)
+ d3 = object.d3
+
+ # submit change with only d2 present -- note that required
+ # field d1 is omitted, which should not cause a validation error
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.d2' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new value in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.d1, d1)
+ self.assert_(object.d2 is None)
+ self.assertEqual(object.d3, d3)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_decimalwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_decimalwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_decimalwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_decimalwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,235 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002, 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.
+#
+##############################################################################
+"""Decimal Widget Functional Tests
+
+$Id$
+"""
+import unittest
+import decimal
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.traversing.api import traverse
+from zope.schema import Decimal, Choice
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class IDecimalTest(Interface):
+
+ f1 = Decimal(
+ required=False,
+ min=decimal.Decimal("1.1"),
+ max=decimal.Decimal("10.1"))
+
+ f2 = Decimal(
+ required=False)
+
+ f3 = Choice(
+ required=True,
+ values=(decimal.Decimal("0.0"), decimal.Decimal("1.1"),
+ decimal.Decimal("2.1"), decimal.Decimal("3.1"),
+ decimal.Decimal("5.1"), decimal.Decimal("7.1"),
+ decimal.Decimal("11.1")),
+ missing_value=0)
+
+ f4 = Decimal(readonly=True)
+
+
+class DecimalTest(Persistent):
+
+ implements(IDecimalTest)
+
+ def __init__(self):
+ self.f1 = None
+ self.f2 = decimal.Decimal("1.1")
+ self.f3 = decimal.Decimal("2.1")
+ self.f4 = decimal.Decimal("17.2")
+
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IDecimalTest)
+ defineSecurity(DecimalTest, IDecimalTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # f1 and f2 should be displayed in text fields
+ self.assert_(patternExists(
+ '<input .* name="field.f1".* value="".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<input .* name="field.f2".* value="1.1".*>', response.getBody()))
+
+ # f3 should be in a dropdown
+ self.assert_(patternExists(
+ '<select .*name="field.f3".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="2.1">2.1</option>',
+ response.getBody()))
+
+ # f4 should be rendered by the display widget
+ self.assert_(patternExists(
+ '<div class="field">17.2</div>', response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '1.123',
+ 'field.f2' : '2.23456789012345',
+ 'field.f3' : '11.1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.f1, decimal.Decimal("1.123"))
+ self.assertEqual(object.f2, decimal.Decimal("2.23456789012345"))
+ self.assertEqual(object.f3, decimal.Decimal("11.1"))
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # submit missing values for f2 and f3
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '',
+ 'field.f2' : '',
+ 'field.f3' : '1.1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.f1, None)
+ self.assertEqual(object.f2, None) # None is default missing_value
+ self.assertEqual(object.f3, decimal.Decimal("1.1")) # 0 is from f3.missing_value=0
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # submit missing values for required field f1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '',
+ 'field.f2' : '',
+ 'field.f3' : '' })
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(not missingInputErrorExists('f1', response.getBody()))
+ self.assert_(not missingInputErrorExists('f2', response.getBody()))
+ self.assert_(missingInputErrorExists('f3', response.getBody()))
+
+
+ def test_invalid_allowed_value(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # submit a value for f3 that isn't allowed
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f3' : '10000' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(invalidValueErrorExists('f3', response.getBody()))
+
+
+ def test_min_max_validation(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # submit value for f1 that is too low
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '-1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('f1', 'Value is too small',
+ response.getBody()))
+
+ # submit value for f1 that is too high
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '1000.2' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('f1', 'Value is too big',
+ response.getBody()))
+
+
+ def test_omitted_value(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # confirm default values
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.f1 is None)
+ self.assertEqual(object.f2, decimal.Decimal("1.1"))
+ self.assertEqual(object.f3, decimal.Decimal("2.1"))
+
+ # submit change with only f2 present -- note that required
+ # field f1 is omitted, which should not cause a validation error
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f2' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new value in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.f1 is None)
+ self.assert_(object.f2 is None)
+ self.assertEqual(object.f3, decimal.Decimal("2.1"))
+
+
+ def test_conversion(self):
+ self.getRootFolder()['test'] = DecimalTest()
+ transaction.commit()
+
+ # submit value for f1 that cannot be convert to an float
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : 'foo' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists(
+ 'f1',
+ 'Invalid decimal data', response.getBody()))
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_editview.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_editview.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_editview.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_editview.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Editview tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import TextLine
+from zope.traversing.api import traverse
+
+from zope.app.form.browser.editview import EditView
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class IFoo(Interface):
+
+ optional_text = TextLine(required=False)
+ required_text = TextLine(required=True)
+
+class Foo(Persistent):
+
+ implements(IFoo)
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IFoo)
+ defineSecurity(Foo, IFoo)
+
+ def test_rollback_on_error(self):
+ """Tests rollback when a widget error occurs.
+
+ When one or more errors are generated by input widgets, the current
+ transaction should be rolledback to ensure object integrity.
+ """
+ self.getRootFolder()['foo'] = Foo()
+ self.getRootFolder()['foo'].required_text = u'initial required'
+ self.getRootFolder()['foo'].optional_text = u'initial optional'
+ transaction.commit()
+
+ # submit form with legal value for optional_text and invalid for
+ # required_text
+ old_update = EditView.update
+ try:
+ def new_update(self):
+ # This update changes something after form validation has failed.
+ # Side effects like this should not be committed.
+ # http://www.zope.org/Collectors/Zope3-dev/655
+ result = old_update(self)
+ self.context.required_text = u'changed after form validation'
+ return result
+ EditView.update = new_update
+ response = self.publish('/foo/edit.html', form={
+ 'field.optional_text': u'',
+ 'field.required_text': u'',
+ 'UPDATE_SUBMIT': ''})
+ self.assertEqual(response.getStatus(), 200)
+ finally:
+ EditView.update = old_update
+
+ # confirm that one errors exists
+ self.assert_(patternExists(
+ 'There are <strong>1</strong> input errors.', response.getBody()))
+
+ # confirm that foo was not modified
+ foo = traverse(self.getRootFolder(), 'foo')
+ self.assertEquals(foo.required_text, u'initial required')
+ self.assertEquals(foo.optional_text, u'initial optional')
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_filewidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_filewidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_filewidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_filewidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,176 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""File Widget Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from StringIO import StringIO
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema.interfaces import IField
+from zope.schema import Field
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.textwidgets import FileWidget
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+from zope.app.form.interfaces import IInputWidget
+
+class IFileField(IField):
+ """Field for representing a file that can be edited by FileWidget."""
+
+
+class FileField(Field):
+
+ implements(IFileField)
+
+class IFileTest(Interface):
+
+ f1 = FileField(required=True)
+ f2 = FileField(required=False)
+
+class FileTest(Persistent):
+
+ implements(IFileTest)
+
+ def __init__(self):
+ self.f1 = None
+ self.f2 = 'foo'
+
+
+class SampleTextFile(StringIO):
+
+ def __init__(self, buf, filename=''):
+ StringIO.__init__(self, buf)
+ self.filename = filename
+
+
+class Test(BrowserTestCase):
+
+ sampleText = "The quick brown fox\njumped over the lazy dog."
+ sampleTextFile = SampleTextFile(sampleText)
+
+ emptyFileName = 'empty.txt'
+ emptyFile = SampleTextFile('', emptyFileName)
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ defineWidgetView(IFileField, FileWidget, IInputWidget)
+ registerEditForm(IFileTest)
+ defineSecurity(FileTest, IFileTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = FileTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # field should be displayed in a file input element
+ self.assert_(patternExists(
+ '<input .* name="field.f1".* type="file".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<input .* name="field.f2".* type="file".*>', response.getBody()))
+
+
+ def test_submit_text(self):
+ self.getRootFolder()['test'] = FileTest()
+ transaction.commit()
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.f1 is None)
+ self.assertEqual(object.f2, 'foo')
+
+ # submit a sample text file
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : self.sampleTextFile,
+ 'field.f2' : self.sampleTextFile,
+ 'field.f1.used' : '',
+ 'field.f2.used' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.f1, self.sampleText)
+ self.assertEqual(object.f2, self.sampleText)
+
+
+ def test_invalid_value(self):
+ self.getRootFolder()['test'] = FileTest()
+ transaction.commit()
+
+ # submit an invalid file value
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : 'not a file - same as missing input',
+ 'field.f1.used' : '',
+ 'field.f2.used' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('f1',
+ 'Form input is not a file object', response.getBody()))
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = FileTest()
+ transaction.commit()
+
+ # submit missing value for required field f1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1.used' : '',
+ 'field.f2.used' : ''})
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('f1', response.getBody()))
+ self.assert_(not missingInputErrorExists('f2', response.getBody()))
+
+
+ def test_empty_file(self):
+ self.getRootFolder()['test'] = FileTest()
+ transaction.commit()
+
+ # submit an empty text file
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f2' : self.emptyFile,
+ # 'field.f1.used' : '', # we don't let f1 know that it was rendered
+ # or else it will complain (see test_required_validation) and the
+ # change will not succeed.
+ 'field.f2.used' : ''})
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # new value for f1 should be field.missing_value (i.e, None)
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.f1 is None)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_floatwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_floatwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_floatwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_floatwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,224 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Float Widget Functional Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.traversing.api import traverse
+from zope.schema import Float, Choice
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class IFloatTest(Interface):
+
+ f1 = Float(
+ required=False,
+ min=1.1,
+ max=10.1)
+
+ f2 = Float(
+ required=False)
+
+ f3 = Choice(
+ required=True,
+ values=(0.0, 1.1, 2.1, 3.1, 5.1, 7.1, 11.1),
+ missing_value=0)
+
+
+class FloatTest(Persistent):
+
+ implements(IFloatTest)
+
+ def __init__(self):
+ self.f1 = None
+ self.f2 = 1.1
+ self.f3 = 2.1
+
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IFloatTest)
+ defineSecurity(FloatTest, IFloatTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # f1 and f2 should be displayed in text fields
+ self.assert_(patternExists(
+ '<input .* name="field.f1".* value="".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<input .* name="field.f2".* value="1.1".*>', response.getBody()))
+
+ # f3 should be in a dropdown
+ self.assert_(patternExists(
+ '<select .*name="field.f3".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="2.1">2.1</option>',
+ response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '1.123',
+ 'field.f2' : '2.23456789012345',
+ 'field.f3' : '11.1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.f1, 1.123)
+ self.assertEqual(object.f2, 2.23456789012345)
+ self.assertEqual(object.f3, 11.1)
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # submit missing values for f2 and f3
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '',
+ 'field.f2' : '',
+ 'field.f3' : '1.1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.f1, None)
+ self.assertEqual(object.f2, None) # None is default missing_value
+ self.assertEqual(object.f3, 1.1) # 0 is from f3.missing_value=0
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # submit missing values for required field f1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '',
+ 'field.f2' : '',
+ 'field.f3' : '' })
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(not missingInputErrorExists('f1', response.getBody()))
+ self.assert_(not missingInputErrorExists('f2', response.getBody()))
+ self.assert_(missingInputErrorExists('f3', response.getBody()))
+
+
+ def test_invalid_allowed_value(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # submit a value for f3 that isn't allowed
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f3' : '10000' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(invalidValueErrorExists('f3', response.getBody()))
+
+
+ def test_min_max_validation(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # submit value for f1 that is too low
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '-1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('f1', 'Value is too small',
+ response.getBody()))
+
+ # submit value for f1 that is too high
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : '1000.2' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('f1', 'Value is too big',
+ response.getBody()))
+
+
+ def test_omitted_value(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # confirm default values
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.f1 is None)
+ self.assertEqual(object.f2, 1.1)
+ self.assertEqual(object.f3, 2.1)
+
+ # submit change with only f2 present -- note that required
+ # field f1 is omitted, which should not cause a validation error
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f2' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new value in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.f1 is None)
+ self.assert_(object.f2 is None)
+ self.assertEqual(object.f3, 2.1)
+
+
+ def test_conversion(self):
+ self.getRootFolder()['test'] = FloatTest()
+ transaction.commit()
+
+ # submit value for f1 that cannot be convert to an float
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.f1' : 'foo' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists(
+ 'f1',
+ 'Invalid floating point data', response.getBody()))
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_i18n.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_i18n.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_i18n.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_i18n.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Test form i18n
+
+$Id$
+"""
+
+import re
+import unittest
+from persistent import Persistent
+from zope.testing import renormalizing, doctest
+from zope.interface import Interface, implements
+from zope.schema import TextLine, Text, Int, List
+from zope.i18nmessageid import MessageFactory
+from zope.app.testing.functional import FunctionalDocFileSuite
+from zope.app.form.testing import AppFormLayer
+
+
+_ = MessageFactory('formtest')
+
+__docformat__ = "reStructuredText"
+
+
+class IFieldContent(Interface):
+
+ title = TextLine(
+ title=_(u"Title"),
+ description=_(u"A short description of the event."),
+ default=u"",
+ required=True
+ )
+
+ description = Text(
+ title=_(u"Description"),
+ description=_(u"A long description of the event."),
+ default=u"",
+ required=False
+ )
+
+ somenumber = Int(
+ title=_(u"Some number"),
+ default=0,
+ required=False
+ )
+
+ somelist = List(
+ title=_(u"Some List"),
+ value_type=TextLine(title=_(u"Some item")),
+ default=[],
+ required=False
+ )
+
+
+class FieldContent(Persistent):
+ implements(IFieldContent)
+
+
+checker = renormalizing.RENormalizing([
+ (re.compile(r"HTTP/1\.1 200 .*"), "HTTP/1.1 200 OK"),
+ ])
+
+
+def test_suite():
+ i18n = FunctionalDocFileSuite('i18n.txt', package='zope.app.form.browser',
+ checker=checker)
+ i18n.layer = AppFormLayer
+ return unittest.TestSuite([
+ i18n,
+ ])
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_intwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_intwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_intwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_intwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,276 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Int Widget Functional Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import Int, Choice
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.testing.functional import BrowserTestCase
+from zope.app.form.browser.tests.support import *
+
+class IIntTest(Interface):
+
+ i2 = Int(
+ required=False)
+
+ i3 = Choice(
+ required=False,
+ values=(0, 1, 2, 3, 5, 7, 11),
+ missing_value=0)
+
+ i1 = Int(
+ required=True,
+ min=1,
+ max=10)
+
+
+class IIntTest2(Interface):
+ """Used to test an unusual care where missing_value is -1 and
+ not in allowed_values."""
+
+ i1 = Choice(
+ required=False,
+ missing_value=-1,
+ values=(10, 20, 30))
+
+
+class IntTest(Persistent):
+
+ implements(IIntTest)
+
+ def __init__(self):
+ self.i1 = None
+ self.i2 = 1
+ self.i3 = 2
+
+
+class IntTest2(Persistent):
+
+ implements(IIntTest2)
+
+ def __init__(self):
+ self.i1 = 10
+
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IIntTest)
+ registerEditForm(IIntTest2)
+ defineSecurity(IntTest, IIntTest)
+ defineSecurity(IntTest2, IIntTest2)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # i1 and i2 should be displayed in text fields
+ self.assert_(patternExists(
+ '<input .* name="field.i1".* value="".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<input .* name="field.i2".* value="1".*>', response.getBody()))
+
+ # i3 should be in a dropdown
+ self.assert_(patternExists(
+ '<select .*name="field.i3".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="2">2</option>',
+ response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1' : '1',
+ 'field.i2' : '2',
+ 'field.i3' : '3' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.i1, 1)
+ self.assertEqual(object.i2, 2)
+ self.assertEqual(object.i3, 3)
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # submit missing values for i2 and i3
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1' : '1',
+ 'field.i2' : '',
+ 'field.i3-empty-marker' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.i1, 1)
+ self.assertEqual(object.i2, None) # None is default missing_value
+ self.assertEqual(object.i3, 0) # 0 is from i3.missing_value=0
+
+
+ def test_alternative_missing_value(self):
+ """Tests the addition of an empty value at the top of the dropdown
+ that, when selected, updates the field with field.missing_value.
+ """
+
+ self.getRootFolder()['test'] = IntTest2() # note alt. class
+ transaction.commit()
+
+ # display edit form
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm that i1 is has a blank item at top with value=""
+ self.assert_(patternExists(
+ '<select id="field.i1" name="field.i1" .*>', response.getBody()))
+ self.assert_(patternExists(
+ '<option value="">.*</option>', response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="10">10</option>',
+ response.getBody()))
+
+ # submit form as if top item is selected
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1-empty-marker' : '1'})
+
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # confirm new value is -1 -- i1.missing_value
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.i1, -1)
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # submit missing values for required field i1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1' : '',
+ 'field.i2' : '',
+ 'field.i3' : '' })
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('i1', response.getBody()))
+ self.assert_(not missingInputErrorExists('i2', response.getBody()))
+ self.assert_(not missingInputErrorExists('i3', response.getBody()))
+
+
+ def test_invalid_allowed_value(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # submit a value for i3 that isn't allowed
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i3' : '12' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(invalidValueErrorExists('i3', response.getBody()))
+
+
+ def test_min_max_validation(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # submit value for i1 that is too low
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1' : '-1' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('i1', 'Value is too small',
+ response.getBody()))
+
+ # submit value for i1 that is too high
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1' : '11' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('i1', 'Value is too big',
+ response.getBody()))
+
+
+ def test_omitted_value(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # confirm default values
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.i1 is None)
+ self.assertEqual(object.i2, 1)
+ self.assertEqual(object.i3, 2)
+
+ # submit change with only i2 present -- note that required
+ # field i1 is omitted, which should not cause a validation error
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i2' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new value in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assert_(object.i1 is None)
+ self.assert_(object.i2 is None)
+ self.assertEqual(object.i3, 2)
+
+
+ def test_conversion(self):
+ self.getRootFolder()['test'] = IntTest()
+ transaction.commit()
+
+ # submit value for i1 that cannot be convert to an int
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.i1' : 'foo' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('i1', 'Invalid integer data',
+ response.getBody()))
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_objectwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_objectwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_objectwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_objectwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,79 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""Test object widget
+
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+from zope.interface import Interface, implements
+from zope.publisher.browser import TestRequest
+from zope.schema import Object, TextLine
+import zope.security.checker
+from zope.app.form.browser import ObjectWidget
+from zope.app.testing.functional import BrowserTestCase
+from zope.app.form.browser.tests import support
+from zope.app.form.testing import AppFormLayer
+
+class ITestContact(Interface):
+ name = TextLine()
+ email = TextLine()
+
+class TestContact(object):
+ implements(ITestContact)
+
+class Test(BrowserTestCase, support.VerifyResults):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ self.field = Object(ITestContact, __name__=u'foo')
+
+ def test_new(self):
+ request = TestRequest()
+ widget = ObjectWidget(self.field, request, TestContact)
+ self.assertEquals(int(widget.hasInput()), 0)
+ check_list = (
+ 'input', 'name="field.foo.name"',
+ 'input', 'name="field.foo.email"'
+ )
+ self.verifyResult(widget(), check_list)
+
+ def test_edit(self):
+ request = TestRequest(form={
+ 'field.foo.name': u'fred',
+ 'field.foo.email': u'fred at fred.com'
+ })
+ widget = ObjectWidget(self.field, request, TestContact)
+ self.assertEquals(int(widget.hasInput()), 1)
+ o = widget.getInputValue()
+ self.assertEquals(hasattr(o, 'name'), 1)
+ self.assertEquals(o.name, u'fred')
+ self.assertEquals(o.email, u'fred at fred.com')
+ check_list = (
+ 'input', 'name="field.foo.name"', 'value="fred"',
+ 'input', 'name="field.foo.email"', 'value="fred at fred.com"',
+ )
+ self.verifyResult(widget(), check_list)
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
+
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_selectwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_selectwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_selectwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_selectwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,147 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""RadioWidget Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import TextLine, Choice
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class IRadioTest(Interface):
+
+ s3 = Choice(
+ required=False,
+ values=(u'Bob', u'is', u'Your', u'Uncle'))
+
+ s4 = Choice(
+ required=True,
+ values=(u'1', u'2', u'3'))
+
+class RadioTest(Persistent):
+
+ implements(IRadioTest)
+
+ def __init__(self):
+ self.s3 = None
+ self.s4 = u'1'
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(IRadioTest)
+ defineSecurity(RadioTest, IRadioTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = RadioTest()
+ transaction.commit()
+
+ test = self.getRootFolder()['test']
+ test.s3 = u"Bob"
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # S3
+ self.assert_(patternExists(
+ '<select .* name="field.s3".*>',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option value="Bob">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option value="is">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option value="Your">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option value="Uncle">',
+ response.getBody()))
+
+ # S4
+ joined_body = "".join(response.getBody().split("\n"))
+ self.failIf(patternExists(
+ '<select.*name="field.s4".*>.*<option.*value="".*>',
+ joined_body))
+ self.assert_(patternExists(
+ '<select .* name="field.s4".*>',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="1">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option value="2">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option value="3">',
+ response.getBody()))
+
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT': '',
+ 'field.s3': u'Bob',
+ 'field.s4': u'2'})
+ self.assert_(patternExists(
+ '<option selected="selected" value="Bob">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="2">',
+ response.getBody()))
+
+ response = self.publish('/test/edit.html')
+ self.assert_(patternExists(
+ '<option selected="selected" value="Bob">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="2">',
+ response.getBody()))
+
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT': '',
+ 'field.s3': u''})
+ self.assert_(patternExists(
+ '<option selected="selected" value="">',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="2">',
+ response.getBody()))
+
+ response = self.publish('/test/edit.html')
+ self.assert_(patternExists(
+ '<option selected="selected" value="">',
+ response.getBody()))
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
+
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textareawidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_textareawidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textareawidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textareawidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,233 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""TextArea Functional Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import Text
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class ITextTest(Interface):
+
+ s2 = Text(
+ required=False,
+ missing_value=u'')
+
+ s3 = Text(
+ required=False)
+
+ s1 = Text(
+ required=True,
+ min_length=2,
+ max_length=10)
+
+
+class TextTest(Persistent):
+
+ implements(ITextTest)
+
+ def __init__(self):
+ self.s1 = ''
+ self.s2 = u'foo'
+ self.s3 = None
+
+
+class Test(BrowserTestCase):
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(ITextTest)
+ defineSecurity(TextTest, ITextTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # all fields should be displayed in text fields
+ self.assert_(patternExists(
+ '<textarea .* name="field.s1".*></textarea>',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<textarea .* name="field.s2".*>foo</textarea>',
+ response.getBody()))
+ self.assert_(patternExists(
+ '<textarea .* name="field.s3".*></textarea>',
+ response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'foo',
+ 'field.s2' : u'bar',
+ 'field.s3' : u'baz' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, u'foo')
+ self.assertEqual(object.s2, u'bar')
+ self.assertEqual(object.s3, u'baz')
+
+
+ def test_invalid_type(self):
+ """Tests textarea widget's handling of invalid unicode input.
+
+ The text widget will succeed in converting any form input into
+ unicode.
+ """
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # submit invalid type for text
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : 123 }) # not unicode
+ self.assertEqual(response.getStatus(), 200)
+ # Note: We don't have a invalid field value
+ # since we convert the value to unicode
+ self.assert_(not validationErrorExists(
+ 's1', 'Object is of wrong type.', response.getBody()))
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # submit missing values for s2 and s3
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'foo',
+ 'field.s2' : '',
+ 'field.s3' : '' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new value in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, u'foo')
+ self.assertEqual(object.s2, u'') # default missing_value
+ self.assertEqual(object.s3, None) # None is s3's missing_value
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # submit missing values for required field s1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : '',
+ 'field.s2' : '',
+ 'field.s3' : '' })
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('s1', response.getBody()))
+ self.assert_(not missingInputErrorExists('s2', response.getBody()))
+ self.assert_(not missingInputErrorExists('s3', response.getBody()))
+
+
+ def test_length_validation(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # submit value for s1 that is too short
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'a' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists(
+ 's1', 'Value is too short', response.getBody()))
+
+ # submit value for s1 that is too long
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'12345678901' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists('s1', 'Value is too long',
+ response.getBody()))
+
+
+ def test_omitted_value(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # confirm default values
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, '')
+ self.assertEqual(object.s2, u'foo')
+ self.assert_(object.s3 is None)
+
+ # submit change with only s2 present -- note that required
+ # field s1 is omitted, which should not cause a validation error
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s2' : u'bar' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, '')
+ self.assertEqual(object.s2, u'bar')
+ self.assert_(object.s3 is None)
+
+
+ def test_conversion(self):
+ self.getRootFolder()['test'] = TextTest()
+ transaction.commit()
+
+ # confirm that line terminators are converted correctly on post
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s2' : u'line1\r\nline2' }) # CRLF per RFC 822
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s2, u'line1\nline2')
+
+ # confirm conversion to HTML
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(patternExists('line1\r\nline2', response.getBody()))
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Copied: zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textwidget.py (from rev 81038, zope.app.form/trunk/src/zope/app/form/browser/ftests/test_textwidget.py)
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textwidget.py (rev 0)
+++ zope.app.form/trunk/src/zope/app/form/browser/tests/test_functional_textwidget.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -0,0 +1,230 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+#
+##############################################################################
+"""TextWidget Tests
+
+$Id$
+"""
+import unittest
+import transaction
+from persistent import Persistent
+
+import zope.security.checker
+from zope.interface import Interface, implements
+from zope.schema import TextLine, Choice
+from zope.traversing.api import traverse
+
+from zope.app.form.testing import AppFormLayer
+from zope.app.form.browser.tests.support import *
+from zope.app.testing.functional import BrowserTestCase
+
+class ITextLineTest(Interface):
+
+ s2 = TextLine(
+ required=False,
+ missing_value=u'')
+
+ s3 = Choice(
+ required=False,
+ values=(u'Bob', u'is', u'Your', u'Uncle'))
+
+ s1 = TextLine(
+ required=True,
+ min_length=2,
+ max_length=10)
+
+
+class TextLineTest(Persistent):
+
+ implements(ITextLineTest)
+
+ def __init__(self):
+ self.s1 = ''
+ self.s2 = u'foo'
+ self.s3 = None
+
+
+class Test(BrowserTestCase):
+
+
+ def setUp(self):
+ BrowserTestCase.setUp(self)
+ registerEditForm(ITextLineTest)
+ defineSecurity(TextLineTest, ITextLineTest)
+
+ def test_display_editform(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # display edit view
+ response = self.publish('/test/edit.html')
+ self.assertEqual(response.getStatus(), 200)
+
+ # s1 and s2 should be displayed in text fields
+ self.assert_(patternExists(
+ '<input .* name="field.s1".* value="".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<input .* name="field.s2".* value="foo".*>', response.getBody()))
+
+ # s3 should be in a dropdown
+ self.assert_(patternExists(
+ '<select .*name="field.s3".*>', response.getBody()))
+ self.assert_(patternExists(
+ '<option selected="selected" value="">.*</option>', response.getBody()))
+
+
+ def test_submit_editform(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # submit edit view
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'foo',
+ 'field.s2' : u'bar',
+ 'field.s3' : u'Uncle' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, u'foo')
+ self.assertEqual(object.s2, u'bar')
+ self.assertEqual(object.s3, u'Uncle')
+
+
+ def test_invalid_type(self):
+ """Tests text widget's handling of invalid unicode input.
+
+ The text widget will succeed in converting any form input into
+ unicode.
+ """
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # submit invalud type for text line
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : '' }) # not unicode (but automatically converted to it.
+ self.assertEqual(response.getStatus(), 200)
+
+ # We don't have a invalid field value
+ #since we convert the value to unicode
+ self.assert_(not validationErrorExists(
+ 's1', 'Object is of wrong type.', response.getBody()))
+
+
+ def test_missing_value(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # submit missing values for s2 and s3
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'foo',
+ 'field.s2' : u'',
+ 'field.s3' : u'' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new values in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, u'foo')
+ self.assertEqual(object.s2, u'') # default missing_value
+ self.assertEqual(object.s3, None) # None is s3's missing_value
+
+
+ def test_required_validation(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # submit missing values for required field s1
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'',
+ 'field.s2' : u'',
+ 'field.s3' : u'' })
+ self.assertEqual(response.getStatus(), 200)
+
+ # confirm error msgs
+ self.assert_(missingInputErrorExists('s1', response.getBody()))
+ self.assert_(not missingInputErrorExists('s2', response.getBody()))
+ self.assert_(not missingInputErrorExists('s3', response.getBody()))
+
+
+ def test_invalid_value(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # submit a value for s3 that isn't allowed
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s3' : u'Bob is *Not* My Uncle' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(invalidValueErrorExists('s3', response.getBody()))
+
+
+ def test_length_validation(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # submit value for s1 that is too short
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'a' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists(
+ 's1', 'Value is too short', response.getBody()))
+
+ # submit value for s1 that is too long
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s1' : u'12345678901' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(validationErrorExists(
+ 's1', 'Value is too long', response.getBody()))
+
+
+ def test_omitted_value(self):
+ self.getRootFolder()['test'] = TextLineTest()
+ transaction.commit()
+
+ # confirm default values
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, '')
+ self.assertEqual(object.s2, u'foo')
+ self.assert_(object.s3 is None)
+
+ # submit change with only s2 present -- note that required
+ # field s1 is omitted, which should not cause a validation error
+ response = self.publish('/test/edit.html', form={
+ 'UPDATE_SUBMIT' : '',
+ 'field.s2' : u'bar' })
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(updatedMsgExists(response.getBody()))
+
+ # check new value in object
+ object = traverse(self.getRootFolder(), 'test')
+ self.assertEqual(object.s1, '')
+ self.assertEqual(object.s2, u'bar')
+ self.assert_(object.s3 is None)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = AppFormLayer
+ suite.addTest(unittest.makeSuite(Test))
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Modified: zope.app.form/trunk/src/zope/app/form/browser/widgets.txt
===================================================================
--- zope.app.form/trunk/src/zope/app/form/browser/widgets.txt 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/src/zope/app/form/browser/widgets.txt 2007-10-24 15:27:12 UTC (rev 81040)
@@ -1,3 +1,4 @@
+============================================================
Simple example showing ObjectWidget and SequenceWidget usage
============================================================
Modified: zope.app.form/trunk/src/zope/app/form/ftesting.zcml
===================================================================
--- zope.app.form/trunk/src/zope/app/form/ftesting.zcml 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/src/zope/app/form/ftesting.zcml 2007-10-24 15:27:12 UTC (rev 81040)
@@ -9,7 +9,7 @@
<include package="zope.app.zcmlfiles" />
- <include package="zope.app.form.browser.ftests" file="i18n.zcml" />
+ <include package="zope.app.form.browser.tests" file="i18n.zcml" />
<include package="zope.app.securitypolicy" file="meta.zcml" />
<include package="zope.app.authentication" />
Modified: zope.app.form/trunk/src/zope/app/form/utility.py
===================================================================
--- zope.app.form/trunk/src/zope/app/form/utility.py 2007-10-24 15:19:13 UTC (rev 81039)
+++ zope.app.form/trunk/src/zope/app/form/utility.py 2007-10-24 15:27:12 UTC (rev 81040)
@@ -57,16 +57,16 @@
else:
fields = [ (name, schema[name]) for name in names ]
return fields
-
-
+
+
def _createWidget(context, field, viewType, request):
- """Creates a widget given a `context`, `field`, and `viewType`."""
+ """Creates a widget given a `context`, `field`, and `viewType`."""
field = field.bind(context)
return component.getMultiAdapter((field, request), viewType)
def _widgetHasStickyValue(widget):
"""Returns ``True`` if the widget has a sticky value.
-
+
A sticky value is input from the user that should not be overridden
by an object's current field value. E.g. a user may enter an invalid
postal code, submit the form, and receive a validation error - the postal
@@ -74,7 +74,7 @@
the object.
"""
return IInputWidget.providedBy(widget) and widget.hasInput()
-
+
def setUpWidget(view, name, field, viewType, value=no_value, prefix=None,
ignoreStickyValues=False, context=None):
"""Sets up a single view widget.
@@ -89,7 +89,7 @@
if context is None:
context = view.context
widgetName = name + '_widget'
-
+
# check if widget already exists
widget = getattr(view, widgetName, None)
if widget is None:
@@ -100,16 +100,16 @@
# exists, but is actually a factory - use it to create the widget
widget = widget(field.bind(context), view.request)
setattr(view, widgetName, widget)
-
+
# widget must implement IWidget
if not IWidget.providedBy(widget):
raise TypeError(
"Unable to configure a widget for %s - attribute %s does not "
"implement IWidget" % (name, widgetName))
-
+
if prefix:
widget.setPrefix(prefix)
-
+
if value is not no_value and (
ignoreStickyValues or not _widgetHasStickyValue(widget)):
widget.setRenderedValue(value)
@@ -118,11 +118,11 @@
def setUpWidgets(view, schema, viewType, prefix=None, ignoreStickyValues=False,
initial={}, names=None, context=None):
"""Sets up widgets for the fields defined by a `schema`.
-
+
Appropriate for collecting input without a current object implementing
the schema (such as an add form).
- `view` is the view that will be configured with widgets.
+ `view` is the view that will be configured with widgets.
`viewType` is the type of widgets to create (e.g. IInputWidget or
IDisplayWidget).
@@ -146,34 +146,34 @@
`context` provides an alternative context for acquisition.
"""
for (name, field) in _fieldlist(names, schema):
- setUpWidget(view, name, field, viewType,
+ setUpWidget(view, name, field, viewType,
value=initial.get(name, no_value),
prefix=prefix,
- ignoreStickyValues=ignoreStickyValues,
+ ignoreStickyValues=ignoreStickyValues,
context=context)
def setUpEditWidgets(view, schema, source=None, prefix=None,
ignoreStickyValues=False, names=None, context=None,
degradeInput=False, degradeDisplay=False):
"""Sets up widgets to collect input on a view.
-
+
See `setUpWidgets` for details on `view`, `schema`, `prefix`,
`ignoreStickyValues`, `names`, and `context`.
-
+
`source`, if specified, is an object from which initial widget values are
read. If source is not specified, the view context is used as the source.
-
+
`degradeInput` is a flag that changes the behavior when a user does not
have permission to edit a field in the names. By default, the function
raises Unauthorized. If degradeInput is True, the field is changed to
an IDisplayWidget.
-
+
`degradeDisplay` is a flag that changes the behavior when a user does not
have permission to access a field in the names. By default, the function
raises Unauthorized. If degradeDisplay is True, the field is removed from
the form.
-
- Returns a list of names, equal to or a subset of the names that were
+
+ Returns a list of names, equal to or a subset of the names that were
supposed to be drawn, with uninitialized undrawn fields missing.
"""
if context is None:
@@ -225,23 +225,23 @@
res_names.append(name)
return res_names
-def setUpDisplayWidgets(view, schema, source=None, prefix=None,
+def setUpDisplayWidgets(view, schema, source=None, prefix=None,
ignoreStickyValues=False, names=None, context=None,
degradeDisplay=False):
"""Sets up widgets to display field values on a view.
-
+
See `setUpWidgets` for details on `view`, `schema`, `prefix`,
`ignoreStickyValues`, `names`, and `context`.
-
+
`source`, if specified, is an object from which initial widget values are
read. If source is not specified, the view context is used as the source.
-
+
`degradeDisplay` is a flag that changes the behavior when a user does not
have permission to access a field in the names. By default, the function
raises Unauthorized. If degradeDisplay is True, the field is removed from
the form.
-
- Returns a list of names, equal to or a subset of the names that were
+
+ Returns a list of names, equal to or a subset of the names that were
supposed to be drawn, with uninitialized undrawn fields missing.
"""
if context is None:
@@ -268,9 +268,9 @@
def viewHasInput(view, schema, names=None):
"""Returns ``True`` if the any of the view's widgets contain user input.
-
+
`schema` specifies the set of fields that correspond to the view widgets.
-
+
`names` can be specified to provide a subset of these fields.
"""
for name, field in _fieldlist(names, schema):
@@ -280,14 +280,14 @@
def applyWidgetsChanges(view, schema, target=None, names=None):
"""Updates an object with values from a view's widgets.
-
+
`view` contained the widgets that perform the update. By default, the
widgets will update the view's context.
-
+
`target` can be specified as an alternative object to update.
-
+
`schema` contrains the values provided by the widgets.
-
+
`names` can be specified to update a subset of the schema constrained
values.
"""
@@ -310,30 +310,30 @@
def getWidgetsData(view, schema, names=None):
"""Returns user entered data for a set of `schema` fields.
-
+
The return value is a map of field names to data values.
-
+
`view` is the view containing the widgets. `schema` is the schema that
defines the widget fields. An optional `names` argument can be provided
to specify an alternate list of field values to return. If `names` is
not specified, or is ``None``, `getWidgetsData` will attempt to return
values for all of the fields in the schema.
-
+
A requested field value may be omitted from the result for one of two
reasons:
-
+
- The field is read only, in which case its widget will not have
user input.
-
- - The field is editable and not required but its widget does not
+
+ - The field is editable and not required but its widget does not
contain user input.
-
+
If a field is required and its widget does not have input, `getWidgetsData`
raises an error.
-
+
A widget may raise a validation error if it cannot return a value that
satisfies its field's contraints.
-
+
Errors, if any, are collected for all fields and reraised as a single
`WidgetsError`.
"""
@@ -351,9 +351,9 @@
elif field.required:
errors.append(MissingInputError(
name, widget.label, 'the field is required'))
-
+
if errors:
raise WidgetsError(errors, widgetsData=result)
-
+
return result
More information about the Checkins
mailing list