[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/ Fix for http://collector.zope.org/Zope3-dev/251

Garrett Smith garrett at mojave-corp.com
Thu Aug 5 19:43:16 EDT 2004


Log message for revision 26931:
  Fix for http://collector.zope.org/Zope3-dev/251
  
  Changes include:
  
  - Fixed incorrectly specified vocab terms for boolean widgets (boolwidgets.py).
  
  - New booleanradionwidget ftest, which is primarily used to verify the fix, but also flushed out some other itemswidget-related bugs.
  
  - Some (hopefully non-controversial) cleanup of itemswidgets.py:
  
    - Deleted some SimpleInputWidget overrides, which were originally 
      copied/pasted, but since became stale
  
    - Removed conditional use of _emptyMarker() -- it's always used now
      (this was the primary bug associated with the issue)
  
    - Raising ConversionError in _toFieldValue
  
  - Updated tests as needed.
  


Changed:
  U   Zope3/trunk/src/zope/app/configure.zcml
  U   Zope3/trunk/src/zope/app/form/browser/boolwidgets.py
  U   Zope3/trunk/src/zope/app/form/browser/ftests/support.py
  A   Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py
  U   Zope3/trunk/src/zope/app/form/browser/itemswidgets.py
  U   Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
  U   Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py
  U   Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py


-=-
Modified: Zope3/trunk/src/zope/app/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/configure.zcml	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/configure.zcml	2004-08-05 23:43:16 UTC (rev 26931)
@@ -60,7 +60,9 @@
   <include package="zope.app.uniqueid" />
 
   <!-- Misc. Service Manager objects -->
+  <!-- Aktari delete - we don't want this
   <include package="zope.app.module" />
+  -->
 
   <!-- Broken-object support -->
   <include package="zope.app.broken" />

Modified: Zope3/trunk/src/zope/app/form/browser/boolwidgets.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/boolwidgets.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/boolwidgets.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -91,17 +91,17 @@
 
 
 def BooleanRadioWidget(field, request, true=_('on'), false=_('off')):
-    vocabulary = SimpleVocabulary.fromItems( ((True, true), (False, false)) ) 
+    vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) ) 
     return RadioWidget(field, vocabulary, request)
 
 
 def BooleanSelectWidget(field, request, true=_('on'), false=_('off')):
-    vocabulary = SimpleVocabulary.fromItems( ((True, true), (False, false)) )
+    vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
     widget = SelectWidget(field, vocabulary, request)
     widget.size = 2
     return widget
 
 
 def BooleanDropdownWidget(field, request, true=_('on'), false=_('off')):
-    vocabulary = SimpleVocabulary.fromItems( ((True, true), (False, false)) )
+    vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
     return DropdownWidget(field, vocabulary, request)

Modified: Zope3/trunk/src/zope/app/form/browser/ftests/support.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/ftests/support.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/ftests/support.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -18,16 +18,30 @@
 import re
 from zope.configuration import xmlconfig
 
-def registerEditForm(schema):
+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" />
+            permission="zope.View">
+            %s
+          </editform>
         </configure>
-        """ % schema.__identifier__)
+        """ % (schema.__identifier__, ''.join(widgetsXml)))
 
 
 def defineSecurity(class_, schema):

Added: Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/ftests/test_booleanradiowidget.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -0,0 +1,153 @@
+##############################################################################
+#
+# 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
+from persistent import Persistent
+from transaction import get_transaction
+
+from support import *
+
+from zope.interface import Interface
+from zope.interface import implements
+
+from zope.schema import Bool
+
+from zope.app.traversing.api import traverse
+
+from zope.app.tests.functional import BrowserTestCase
+
+
+class IFoo(Interface):
+
+    bar = Bool(title=u'Bar')
+
+registerEditForm(IFoo, widgets={
+    'bar': { 'class': 'zope.app.form.browser.BooleanRadioWidget' }})
+
+
+class Foo(Persistent):
+
+    implements(IFoo)
+
+    def __init__(self):
+        self.bar = True
+
+defineSecurity(Foo, IFoo)
+
+
+class Test(BrowserTestCase):
+
+
+    def test_display_editform(self):
+        self.getRootFolder()['foo'] = Foo()
+        get_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()
+        get_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()
+        get_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()
+        get_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()
+        get_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()
+    suite.addTest(unittest.makeSuite(Test))
+    return suite
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Modified: Zope3/trunk/src/zope/app/form/browser/itemswidgets.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/itemswidgets.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/itemswidgets.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -28,7 +28,8 @@
 from zope.app.form.browser.widget import SimpleInputWidget, renderElement
 from zope.app.form.browser.interfaces import IVocabularyQueryView
 from zope.app.form.interfaces import IInputWidget, IDisplayWidget
-from zope.app.form.interfaces import WidgetInputError
+from zope.app.form.interfaces import ConversionError
+
 from zope.app.i18n import ZopeMessageIDFactory as _
 
 
@@ -126,29 +127,6 @@
                 values.append(term.value)
         return values
 
-    def getInputValue(self):
-        """Get the value that was inputed."""
-        input = self.request.form.get(self.name, self._data_marker)
-        field = self.context
-
-        # missing value is okay if field is not required
-        if input == self._data_marker and not field.required and \
-               self.empty_marker_name in self.request.form:
-            return field.missing_value
-
-        # Now let's see whether we have a valid input value
-        try:
-            value = self._toFieldValue(input)
-        except ValidationError, error:
-            self._error = WidgetInputError(
-                self.context.__name__,
-                self.context.title,
-                error)
-
-            raise self._error
-
-        return value
-
     def _emptyMarker(self):
         """Mark the form so that empty selections are also valid."""
         return '<input name="%s" type="hidden" value="1" />' % (
@@ -159,10 +137,6 @@
         return (self.name in self.request.form or
                 self.empty_marker_name in self.request.form)
 
-    def setRenderedValue(self, value):
-        """Store the ready-for-HTML value."""
-        self._data = value
-
     def _toFieldValue(self, input):
         """See `SimpleInputWidget`"""
         raise NotImplementedError(
@@ -170,21 +144,7 @@
             "It may be inherited from the mix-in classes SingleDataHelper\n"
             "or MultiDataHelper")
 
-    def _getFormValue(self):
-        if self._data is self._data_marker:
-            # The data has not been retrieved from the form, so let's do that
-            if self.hasInput():
-                try:
-                    value = self.getInputValue()
-                except WidgetInputError:
-                    value = self.request.form.get(self.name, self._missing)
-            else:
-                value = self._getDefault()
-        else:
-            value = self._data
-        return value
 
-
 class SingleDataHelper(object):
     """Mix-in helper class for getting the term from the HTML form.
 
@@ -193,9 +153,12 @@
 
     def _toFieldValue(self, input):
         if input:
-            return self.convertTokensToValues([input])[0]
+            try:
+                return self.convertTokensToValues([input])[0]
+            except InvalidValue, e:
+                raise ConversionError("Invalid value", e)
         else:
-            return None
+            return self.context.missing_value
 
     def hidden(self):
         return renderElement(u'input',
@@ -221,7 +184,10 @@
             return []
         if not isinstance(input, list):
             input = [input]
-        return self.convertTokensToValues(input)
+        try:
+            return self.convertTokensToValues(input)
+        except InvalidValue, e:
+            raise ConversionError("Invalid value", e)
 
     def _getDefault(self):
         # Return the default value for this widget;
@@ -242,7 +208,7 @@
     def __call__(self):
         """See IBrowserWidget."""
         value = self._getFormValue()
-        if value is None:
+        if not value:
             return self.translate(self._messageNoValue)
         else:
             term = self.vocabulary.getTerm(value)
@@ -363,8 +329,7 @@
                 have_results = True
 
         contents.append(self._div('value', self.renderValue(value)))
-        if not self.context.required:
-            contents.append(self._emptyMarker())
+        contents.append(self._emptyMarker())
 
         if self.queryview is not None and not have_results:
             html = self.queryview.renderInput()

Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -22,7 +22,7 @@
 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 from zope.publisher.browser import TestRequest
 
-from zope.app.form.interfaces import WidgetInputError
+from zope.app.form.interfaces import ConversionError
 from zope.app.form.browser.itemswidgets import ItemsWidgetBase
 from zope.app.form.browser.itemswidgets import ItemDisplayWidget
 from zope.app.form.browser.itemswidgets import ItemsMultiDisplayWidget
@@ -201,7 +201,7 @@
         widget = self._makeWidget(form={'field.choice': 'ten'})
         widget.setPrefix('field.')
         widget._getFormValue()
-        self.assert_(isinstance(widget._error, WidgetInputError))
+        self.assert_(isinstance(widget._error, ConversionError))
     
     def test_hidden(self):
         widget = self._makeWidget(form={'field.choice': 'token2'})
@@ -398,7 +398,7 @@
         widget = self._makeWidget(form={'field.numbers': ['ten']})
         widget.setPrefix('field.')
         widget._getFormValue()
-        self.assert_(isinstance(widget._error, WidgetInputError))
+        self.assert_(isinstance(widget._error, ConversionError))
     
     def test_hidden(self):
         widget = self._makeWidget(

Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_radiowidget.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -103,6 +103,25 @@
         self.verifyResult(self._widget.hidden(), check_list)
 
 
+    def testHasInput(self):
+        self._widget.request.form.clear()
+        self.assert_(not self._widget.hasInput())
+        self._widget.request.form['field.foo-empty-marker'] = '1'
+        self.assert_(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'Foo Value'
+        self.assert_(self._widget.hasInput())
+        del self._widget.request.form['field.foo-empty-marker']
+        self.assert_(self._widget.hasInput())
+
+
+    def testRenderEmptyMarker(self):
+        self.verifyResult(self._widget(), ('field.foo-empty-marker',))
+        self._widget.setRenderedValue(u'bar')
+        self.verifyResult(self._widget(), ('field.foo-empty-marker',))
+        self._widget.setRenderedValue(u'not a legal value')
+        self.verifyResult(self._widget(), ('field.foo-empty-marker',))
+
+
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(RadioWidgetTest),

Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py	2004-08-05 22:09:09 UTC (rev 26930)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_selectwidget.py	2004-08-05 23:43:16 UTC (rev 26931)
@@ -47,6 +47,7 @@
 <option value="&amp;blah&amp;">&amp;blah&amp;</option>
 </select>
 </div>
+<input name="field.terms-empty-marker" type="hidden" value="1" />
 </div>'''
 
 class SelectWidgetHTMLEncodingTest(unittest.TestCase):



More information about the Zope3-Checkins mailing list