[Zope3-checkins] SVN: zope.formlib/branches/faassen-zaf/src/zope/formlib/ Move over a lot more widget tests from zope.app.form.

Martijn Faassen faassen at startifact.com
Wed Dec 30 17:26:16 EST 2009


Log message for revision 107397:
  Move over a lot more widget tests from zope.app.form.
  

Changed:
  U   zope.formlib/branches/faassen-zaf/src/zope/formlib/interfaces.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/objectwidget.txt
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/source.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/source.txt
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicecollections.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicewidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datetimewidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datewidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_decimalwidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_displaywidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_intwidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_itemswidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_multicheckboxwidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_objectwidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_passwordwidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_radiowidget.py
  A   zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_source.py

-=-
Modified: zope.formlib/branches/faassen-zaf/src/zope/formlib/interfaces.py
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/interfaces.py	2009-12-30 21:54:52 UTC (rev 107396)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/interfaces.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -310,6 +310,26 @@
         """Convert a widget input error to an html snippet."""
 
 
+class ISourceQueryView(Interface):
+    """View support for querying non-iterable sources
+    """
+
+    def render(name):
+        """Return a rendering of the search form elements
+
+        The query view should use `name` as the prefix for its widgets.
+        """
+
+    def results(name):
+        """Return the results of the query
+
+        The query view should use `name` as the prefix for its widgets.
+
+        The value returned is an iterable.
+
+        None may be returned to indicate that there are no results.
+        """
+
 class ISubPage(Interface):
     """A component that computes part of a page
     """

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/objectwidget.txt (from rev 107371, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/objectwidget.txt)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/objectwidget.txt	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/objectwidget.txt	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,152 @@
+=============
+Object Widget
+=============
+
+The following example shows a Family with Mother and Father.
+First define the interface for a person:
+
+  >>> from zope.interface import Interface, implements
+  >>> from zope.schema import TextLine
+
+  >>> class IPerson(Interface):
+  ...     """Interface for Persons."""
+  ...
+  ...     name = TextLine(title=u'Name', description=u'The first name')
+
+Let's define the class:
+
+  >>> class Person(object):
+  ...
+  ...     implements(IPerson)
+  ...
+  ...     def __init__(self, name=''):
+  ...         self.name = name
+
+Let's define the interface family:
+
+  >>> from zope.schema import Object
+
+  >>> class IFamily(Interface):
+  ...     """The familiy interface.""" 
+  ...  
+  ...     mother = Object(title=u'Mother',
+  ...                     required=False, 
+  ...                     schema=IPerson)
+  ...
+  ...     father = Object(title=u'Father',
+  ...                     required=False, 
+  ...                     schema=IPerson)
+
+Let's define the class family with FieldProperty's mother and father
+FieldProperty validate the values if they get added:
+
+  >>> from zope.schema.fieldproperty import FieldProperty
+
+  >>> class Family(object):
+  ...     """The familiy interface."""
+  ...
+  ...     implements(IFamily)
+  ...
+  ...     mother = FieldProperty(IFamily['mother'])
+  ...     father = FieldProperty(IFamily['father'])
+  ...
+  ...     def __init__(self, mother=None, father=None):
+  ...         self.mother = mother
+  ...         self.father = father
+
+Let's make a instance of Family with None attributes:
+
+  >>> family = Family()
+  >>> bool(family.mother == None)
+  True
+
+  >>> bool(family.father == None)
+  True
+
+Let's make a instance of Family with None attributes:
+
+  >>> mother = Person(u'Margrith')
+  >>> father = Person(u'Joe')
+  >>> family = Family(mother, father)
+  >>> IPerson.providedBy(family.mother)
+  True
+
+  >>> IPerson.providedBy(family.father)
+  True
+
+Let's define a dummy class which doesn't implements IPerson:
+
+  >>> class Dummy(object):
+  ...     """Dummy class."""
+  ...     def __init__(self, name=''):
+  ...         self.name = name
+
+Raise a SchemaNotProvided exception if we add a Dummy instance to a Family 
+object:
+
+  >>> foo = Dummy('foo')
+  >>> bar = Dummy('bar')
+  >>> family = Family(foo, bar)
+  Traceback (most recent call last):
+  ...
+  SchemaNotProvided
+
+Now let's setup a enviroment for use the widget like in a real application:
+
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> from zope.schema.interfaces import ITextLine
+  >>> from zope.schema import TextLine
+  >>> from zope.app.form.browser import TextWidget
+  >>> from zope.app.form.browser import ObjectWidget
+  >>> from zope.formlib.interfaces import IInputWidget
+
+Register the TextLine widget used in the IPerson interface for the field 'name'.
+
+  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+  >>> from zope.component import provideAdapter
+  >>> provideAdapter(TextWidget, (ITextLine, IDefaultBrowserLayer),
+  ...                IInputWidget)
+
+Let's define a request and provide input value for the mothers name used
+in the family object:
+
+  >>> request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
+  >>> request.form['field.mother.name'] = u'Margrith Ineichen'
+
+Before we update the object let's check the value name of the mother
+instance on the family object:
+
+  >>> family.mother.name
+  u'Margrith'
+
+Now let's initialize a ObjectWidget with the right attributes:
+
+  >>> mother_field = IFamily['mother']
+  >>> factory = Person
+  >>> widget = ObjectWidget(mother_field, request, factory)
+
+Now comes the magic. Apply changes means we force the ObjectWidget to read 
+the request, extract the value and save it on the content. The ObjectWidget 
+instance uses a real Person class (factory) for add the value. The value is 
+temporary stored in this factory class. The ObjectWidget reads the value from 
+this factory and set it to the attribute 'name' of the instance mother 
+(The object mother is allready there). If we don't have a instance mother 
+allready store in the family object, the factory instance will be stored 
+directly to the family attribute mother. For more information see the method 
+'applyChanges()' in the interface 
+zope.app.form.browser.objectwidget.ObjectWidget.
+
+  >>> widget.applyChanges(family)
+  True
+
+Test the updated mother's name value on the object family:
+
+  >>> family.mother.name
+  u'Margrith Ineichen'
+  
+  >>> IPerson.providedBy(family.mother)
+  True
+
+So, now you know my mothers and fathers name. I hope it's also clear how to 
+use the Object field and the ObjectWidget.

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/source.py (from rev 107373, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/source.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/source.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/source.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,610 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Source widgets support
+
+$Id$
+"""
+
+from itertools import imap
+
+import xml.sax.saxutils
+
+from zope.component import adapts, getMultiAdapter
+from zope.interface import implements
+import zope.browser.interfaces
+import zope.schema.interfaces
+from zope.schema.interfaces import \
+    ISourceQueriables, ValidationError, IVocabularyTokenized, IIterableSource
+
+import zope.formlib.interfaces
+import zope.formlib.widget
+from zope.formlib.interfaces import (
+    WidgetInputError,
+    MissingInputError,
+    ISourceQueryView,
+    IWidgetInputErrorView,
+    IDisplayWidget,
+    IInputWidget)
+
+from zope.formlib.i18n import _
+from zope.formlib.widgets import (
+    SelectWidget, RadioWidget, MultiSelectWidget, OrderedMultiSelectWidget,
+    MultiCheckBoxWidget, MultiSelectSetWidget, MultiSelectFrozenSetWidget)
+from zope.formlib.widget import InputWidget
+import zope.formlib.itemswidgets
+from zope.formlib.widget import DisplayWidget
+
+class SourceDisplayWidget(DisplayWidget):
+
+    implements(IDisplayWidget)
+
+    def __init__(self, field, source, request):
+        super(SourceDisplayWidget, self).__init__(field, request)
+        self.source = source
+
+    required = False
+
+    def hidden(self):
+        return ''
+
+    def error(self):
+        return ''
+
+    def __call__(self):
+        """Render the current value
+        """
+
+        if self._renderedValueSet():
+            value = self._data
+        else:
+            value = self.context.default
+
+        if value == self.context.missing_value:
+            value = self._translate(_("SourceDisplayWidget-missing",
+                                      default="Nothing"))
+        else:
+            terms = getMultiAdapter((self.source, self.request),
+                zope.browser.interfaces.ITerms)
+
+            try:
+                term = terms.getTerm(value)
+            except LookupError:
+                value = self._translate(_("SourceDisplayWidget-invalid",
+                                          default="Invalid value"))
+            else:
+                value = self.renderTermForDisplay(term)
+
+        return value
+
+    def renderTermForDisplay(self, term):
+        # Provide a rendering of `term` for display; this is not for
+        # use when generating a select list.
+        return xml.sax.saxutils.escape(self._translate(term.title))
+
+
+class SourceSequenceDisplayWidget(SourceDisplayWidget):
+
+    def __call__(self):
+
+        if self._renderedValueSet():
+            seq = self._data
+        else:
+            seq = self.context.default
+
+        terms = getMultiAdapter((self.source, self.request),
+            zope.browser.interfaces.ITerms)
+        result = []
+        for value in seq:
+            try:
+                term = terms.getTerm(value)
+            except LookupError:
+                value = self._translate(_("SourceDisplayWidget-invalid",
+                                          default="Invalid value"))
+            else:
+                value = self.renderTermForDisplay(term)
+
+            result.append(value)
+
+        return '<br />\n'.join(result)
+
+
+class SourceInputWidget(InputWidget):
+
+    _error = None
+
+    implements(IInputWidget)
+
+    def __init__(self, field, source, request):
+        super(SourceInputWidget, self).__init__(field, request)
+        self.source = source
+        self.terms = getMultiAdapter((source, self.request),
+            zope.browser.interfaces.ITerms)
+
+    def queryviews(self):
+        queriables = ISourceQueriables(self.source, None)
+        if queriables is None:
+            # treat the source itself as a queriable
+            queriables = ((self.name + '.query', self.source), )
+        else:
+            queriables = [
+                (self.name + '.' +
+                 unicode(i).encode('base64').strip().replace('=', '_'), s)
+                          for (i, s) in queriables.getQueriables()]
+
+        return [
+            (name, getMultiAdapter(
+                    (source, self.request),
+                    ISourceQueryView,
+                    )
+             ) for (name, source) in queriables]
+
+    queryviews = property(queryviews)
+
+    def _value(self):
+        if self._renderedValueSet():
+            value = self._data
+        else:
+            for name, queryview in self.queryviews:
+                if name+'.apply' in self.request:
+                    token = self.request.form.get(name+'.selection')
+                    if token is not None:
+                        break
+                else:
+                    token = self.request.form.get(self.name)
+
+            if token is not None:
+                try:
+                    value = self.terms.getValue(str(token))
+                except LookupError:
+                    value = self.context.missing_value
+            else:
+                value = self.context.missing_value
+
+        return value
+
+    def hidden(self):
+        value = self._value()
+        if value == self.context.missing_value:
+            return '' # Nothing to hide ;)
+
+        try:
+            term = self.terms.getTerm(value)
+        except LookupError:
+            # A value was set, but it's not valid.  Treat
+            # it as if it was missing and return nothing.
+            return ''
+
+        return ('<input type="hidden" name="%s" value=%s />'
+                % (self.name, xml.sax.saxutils.quoteattr(term.token))
+                )
+
+    def error(self):
+        if self._error:
+            # TODO This code path is untested.
+            return getMultiAdapter((self._error, self.request),
+                                   IWidgetInputErrorView).snippet()
+        return ""
+
+    def __call__(self):
+        result = ['<div class="value">']
+        value = self._value()
+        field = self.context
+
+        term = None
+        if value == field.missing_value:
+            result.append('  <div class="row">')
+            result.append('    <div class="label">')
+            result.append(u'     ' +
+                          self._translate(_("SourceDisplayWidget-label",
+                                            default="Selected"))
+                          )
+            result.append('    </div>')
+            result.append('    <div class="field">')
+            result.append(u'     ' +
+                          self._translate(_("SourceDisplayWidget-missing",
+                                            default="Nothing"))
+                          )
+            result.append('    </div>')
+            result.append('  </div>')
+        else:
+            try:
+                term = self.terms.getTerm(value)
+            except LookupError:
+                result.append(u'  ' +
+                              self._translate(_("SourceDisplayWidget-missing",
+                                                default="Nothing Valid"))
+                              )
+            else:
+                result.append('  <div class="row">')
+                result.append('    <div class="label">')
+                result.append(u'     ' +
+                              self._translate(_("SourceDisplayWidget-label",
+                                                default="Selected"))
+                              )
+                result.append('    </div>')
+                result.append('    <div class="field">')
+                result.append(u'     ' + self.renderTermForDisplay(term))
+                result.append('    </div>')
+                result.append('  </div>')
+                result.append(
+                    '  <input type="hidden" name="%s" value=%s />'
+                    % (self.name, xml.sax.saxutils.quoteattr(term.token)))
+
+        result.append('  <input type="hidden" name="%s.displayed" value="y" />'
+                      % self.name)
+
+        result.append('  <div class="queries">')
+        for name, queryview in self.queryviews:
+            result.append('    <div class="query">')
+            result.append('      <div class="queryinput">')
+            result.append(queryview.render(name))
+            result.append('      </div> <!-- queryinput -->')
+
+            qresults = queryview.results(name)
+            if qresults:
+                result.append('      <div class="queryresults">\n%s' %
+                              self._renderResults(qresults, name))
+                result.append('      </div> <!-- queryresults -->')
+            result.append('    </div> <!-- query -->')
+        result.append('  </div> <!-- queries -->')
+        result.append('</div> <!-- value -->')
+        return '\n'.join(result)
+
+    def _renderResults(self, results, name):
+        terms = []
+        for value in results:
+            term = self.terms.getTerm(value)
+            terms.append((self._translate(term.title), term.token))
+        terms.sort()
+
+        apply = self._translate(_("SourceInputWidget-apply", default="Apply"))
+        return (
+            '<select name="%s.selection">\n'
+            '%s\n'
+            '</select>\n'
+            '<input type="submit" name="%s.apply" value="%s" />'
+            % (name,
+               '\n'.join(
+                   [('<option value="%s">%s</option>'
+                     % (token, title))
+                    for (title, token) in terms]),
+               name,
+               apply)
+            )
+
+    def renderTermForDisplay(self, term):
+        # Provide a rendering of `term` for display; this is not for
+        # use when generating a select list.
+        return xml.sax.saxutils.escape(self._translate(term.title))
+
+    required = property(lambda self: self.context.required)
+
+    def getInputValue(self):
+        for name, queryview in self.queryviews:
+            if name+'.apply' in self.request:
+                token = self.request.form.get(name+'.selection')
+                if token is not None:
+                    break
+        else:
+            token = self.request.get(self.name)
+
+        field = self.context
+
+        if token is None:
+            if field.required:
+                # TODO This code path is untested.
+                raise MissingInputError(
+                    field.__name__, self.label,
+                    )
+            return field.missing_value
+
+        try:
+            value = self.terms.getValue(str(token))
+        except LookupError:
+            # TODO This code path is untested.
+            err = zope.schema.interfaces.ValidationError(
+                "Invalid value id", token)
+            raise WidgetInputError(field.__name__, self.label, err)
+
+        # Remaining code copied from SimpleInputWidget
+
+        # value must be valid per the field constraints
+        try:
+            field.validate(value)
+        except ValidationError, err:
+            # TODO This code path is untested.
+            self._error = WidgetInputError(field.__name__, self.label, err)
+            raise self._error
+
+        return value
+
+    def hasInput(self):
+        if self.name in self.request or self.name+'.displayed' in self.request:
+            return True
+
+        for name, queryview in self.queryviews:
+            if name+'.apply' in self.request:
+                token = self.request.form.get(name+'.selection')
+                if token is not None:
+                    return True
+
+        return False
+
+class SourceListInputWidget(SourceInputWidget):
+
+    def _input_value(self):
+        tokens = self.request.form.get(self.name)
+        for name, queryview in self.queryviews:
+            if name+'.apply' in self.request:
+                newtokens = self.request.form.get(name+'.selection')
+                if newtokens:
+                    if tokens:
+                        tokens = tokens + newtokens
+                    else:
+                        tokens = newtokens
+
+        if tokens:
+            remove = self.request.form.get(self.name+'.checked')
+            if remove and (self.name+'.remove' in self.request):
+                tokens = [token
+                          for token in tokens
+                          if token not in remove
+                          ]
+            value = []
+            for token in tokens:
+                try:
+                    v = self.terms.getValue(str(token))
+                except LookupError:
+                    pass # skip invalid tokens (shrug)
+                else:
+                    value.append(v)
+        else:
+            if self.name+'.displayed' in self.request:
+                value = []
+            else:
+                value = self.context.missing_value
+
+        if value:
+            r = []
+            seen = {}
+            for s in value:
+                if s not in seen:
+                    r.append(s)
+                    seen[s] = 1
+            value = r
+
+        return value
+
+    def _value(self):
+        if self._renderedValueSet():
+            value = self._data
+        else:
+            value = self._input_value()
+
+        return value
+
+    def hidden(self):
+        value = self._value()
+        if value == self.context.missing_value:
+            return '' # Nothing to hide ;)
+
+        result = []
+        for v in value:
+            try:
+                term = self.terms.getTerm(value)
+            except LookupError:
+                # A value was set, but it's not valid.  Treat
+                # it as if it was missing and skip
+                continue
+            else:
+                result.append(
+                    '<input type="hidden" name="%s:list" value=%s />'
+                    % (self.name, xml.sax.saxutils.quoteattr(term.token))
+                    )
+
+    def __call__(self):
+        result = ['<div class="value">']
+        value = self._value()
+        field = self.context
+
+        if value:
+            for v in value:
+                try:
+                    term = self.terms.getTerm(v)
+                except LookupError:
+                    continue # skip
+                else:
+                    result.append(
+                        '  <input type="checkbox" name="%s.checked:list"'
+                        ' value=%s />'
+                        % (self.name, xml.sax.saxutils.quoteattr(term.token))
+                        )
+                    result.append('  ' + self.renderTermForDisplay(term))
+                    result.append(
+                        '  <input type="hidden" name="%s:list" value=%s />'
+                        % (self.name, xml.sax.saxutils.quoteattr(term.token)))
+                    result.append('  <br />')
+
+            result.append(
+                '  <input type="submit" name="%s.remove" value="%s" />'
+                % (self.name,
+                   self._translate(_("MultipleSourceInputWidget-remove",
+                                     default="Remove")))
+                )
+            result.append('  <br />')
+
+        result.append('  <input type="hidden" name="%s.displayed" value="y" />'
+                      % self.name)
+
+        result.append('  <div class="queries">')
+
+        for name, queryview in self.queryviews:
+            result.append('    <div class="query">')
+            result.append('      <div class="queryinput">')
+            result.append(queryview.render(name))
+            result.append('      </div> <!-- queryinput -->')
+
+            qresults = queryview.results(name)
+            if qresults:
+                result.append('      <div class="queryresults">\n%s' %
+                              self._renderResults(qresults, name))
+                result.append('      </div> <!-- queryresults -->')
+            result.append('    </div> <!-- query -->')
+
+        result.append('  </div> <!-- queries -->')
+        result.append('</div> <!-- value -->')
+        return '\n'.join(result)
+
+    def _renderResults(self, results, name):
+        terms = []
+        apply = self._translate(_("SourceListInputWidget-apply",
+                                  default="Apply"))
+        for value in results:
+            term = self.terms.getTerm(value)
+            terms.append((self._translate(term.title), term.token))
+        terms.sort()
+        return (
+            '<select name="%s.selection:list" multiple>\n'
+            '%s\n'
+            '</select>\n'
+            '<input type="submit" name="%s.apply" value="%s" />'
+            % (name,
+               '\n'.join([('<option value="%s">%s</option>' % (token, title))
+                          for (title, token) in terms]),
+               name,
+               apply)
+            )
+
+    def getInputValue(self):
+        value = self._input_value()
+
+        # Remaining code copied from SimpleInputWidget
+
+        # value must be valid per the field constraints
+        field = self.context
+        try:
+            field.validate(value)
+        except ValidationError, err:
+            # TODO This code path is untested.
+            self._error = WidgetInputError(field.__name__, self.label, err)
+            raise self._error
+
+        return value
+
+    def hasInput(self):
+        return self.name+'.displayed' in self.request.form
+
+
+# Input widgets for IIterableSource:
+
+# These widgets reuse the old-style vocabulary widgets via the class
+# IterableSourceVocabulary that adapts a source (and its ITerms object)
+# into a vocabulary. When/if vocabularies go away, these classes
+# should be updated into full implementations.
+
+
+class IterableSourceVocabulary(object):
+
+    """Adapts an iterable source into a legacy vocabulary.
+
+    This can be used to wrap sources to make them usable with widgets that
+    expect vocabularies. Note that there must be an ITerms implementation
+    registered to obtain the terms.
+    """
+
+    implements(IVocabularyTokenized)
+    adapts(IIterableSource);
+
+    def __init__(self, source, request):
+        self.source = source
+        self.terms = getMultiAdapter((source, request),
+            zope.browser.interfaces.ITerms)
+
+    def getTerm(self, value):
+        return self.terms.getTerm(value)
+
+    def getTermByToken(self, token):
+        value = self.terms.getValue(token)
+        return self.getTerm(value)
+
+    def __iter__(self):
+        return imap(
+            lambda value: self.getTerm(value), self.source.__iter__())
+
+    def __len__(self):
+        return self.source.__len__()
+
+    def __contains__(self, value):
+        return self.source.__contains__(value)
+
+
+class SourceSelectWidget(SelectWidget):
+    """Provide a selection list for the item."""
+
+    def __init__(self, field, source, request):
+        super(SourceSelectWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)
+        # BBB
+        if not zope.formlib.itemswidgets.EXPLICIT_EMPTY_SELECTION:
+            # Even if the field is required, no input is needed, so don't
+            # worry the user about it:
+            self.required = False
+
+class SourceDropdownWidget(SourceSelectWidget):
+    """Variation of the SourceSelectWidget that uses a drop-down list."""
+
+    size = 1
+    explicit_empty_selection = True
+
+class SourceRadioWidget(RadioWidget):
+    """Radio widget for single item choices."""
+
+    def __init__(self, field, source, request):
+        super(SourceRadioWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)
+
+class SourceMultiSelectWidget(MultiSelectWidget):
+    """A multi-selection widget with ordering support."""
+
+    def __init__(self, field, source, request):
+        super(SourceMultiSelectWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)
+
+class SourceOrderedMultiSelectWidget(OrderedMultiSelectWidget):
+    """A multi-selection widget with ordering support."""
+
+    def __init__(self, field, source, request):
+        super(SourceOrderedMultiSelectWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)
+
+class SourceMultiSelectSetWidget(MultiSelectSetWidget):
+    """Provide a selection list for the set to be selected."""
+
+    def __init__(self, field, source, request):
+        super(SourceMultiSelectSetWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)
+
+class SourceMultiSelectFrozenSetWidget(MultiSelectFrozenSetWidget):
+    """Provide a selection list for the frozenset to be selected."""
+
+    def __init__(self, field, source, request):
+        super(SourceMultiSelectFrozenSetWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)
+
+class SourceMultiCheckBoxWidget(MultiCheckBoxWidget):
+    """Provide a list of checkboxes that provide the choice for the list."""
+
+    def __init__(self, field, source, request):
+        super(SourceMultiCheckBoxWidget, self).__init__(
+            field, IterableSourceVocabulary(source, request), request)

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/source.txt (from rev 107371, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/source.txt)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/source.txt	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/source.txt	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,1113 @@
+==============
+Source Widgets
+==============
+
+Sources are objects that represent sets of values from which one might choose
+and are used with Choice schema fields. Source widgets currently fall into two
+categories:
+
+- widgets for iterable sources
+
+- widgets for queryable sources
+
+Sources (combined with the available adapters) may support both approaches, but
+no widgets currently support both.
+
+In both cases, the widgets need views that can be used to get tokens to
+represent source values in forms, as well as textual representations of values.
+We use the `zope.browser.interfaces.ITerms` views for that.
+
+All of our examples will be using the component architecture::
+
+  >>> import zope.interface
+  >>> import zope.component
+  >>> import zope.schema
+
+This `ITerms` implementation can be used for the sources involved in
+our tests::
+
+  >>> from zope.browser.interfaces import ITerms
+  >>> import zope.publisher.interfaces.browser
+  >>> import zope.app.form.browser.interfaces
+  >>> from zope.schema.vocabulary import SimpleTerm
+  >>> class ListTerms:
+  ...
+  ...     zope.interface.implements(ITerms)
+  ...
+  ...     def __init__(self, source, request):
+  ...         pass # We don't actually need the source or the request :)
+  ...
+  ...     def getTerm(self, value):
+  ...         title = unicode(value)
+  ...         try:
+  ...             token = title.encode('base64').strip()
+  ...         except binascii.Error:
+  ...             raise LookupError(token)
+  ...         return SimpleTerm(value, token=token, title=title)
+  ...
+  ...     def getValue(self, token):
+  ...         return token.decode('base64')
+
+This view just uses the unicode representations of values as titles and the
+base-64 encoding of the titles as tokens.  This is a very simple strategy
+that's only approriate when the values have short and unique unicode
+representations.
+
+All of the source widgets are in a single module::
+
+  >>> import zope.app.form.browser.source
+
+We'll also need request objects::
+
+  >>> from zope.publisher.browser import TestRequest
+
+
+Iterable Source Widgets
+=======================
+
+Iterable sources are expected to be simpler than queriable sources, so they
+represent a good place to start.  The most important aspect of iterable sources
+for widgets is that it's actually possible to enumerate all the values from the
+source.  This allows each possible value to be listed in a  <select> form field.
+
+Let's start with a simple example.  We have a very trivial source,
+which is basically a list::
+
+  >>> class SourceList(list):
+  ...     zope.interface.implements(zope.schema.interfaces.IIterableSource)
+
+We need to register our `ITerms` view::
+
+  >>> zope.component.provideAdapter(
+  ...     ListTerms,
+  ...     (SourceList, zope.publisher.interfaces.browser.IBrowserRequest))
+
+Let's define a choice field using our iterable source::
+
+  >>> dog = zope.schema.Choice(
+  ...    __name__ = 'dog',
+  ...    title=u"Dogs",
+  ...    source=SourceList(['spot', 'bowser', 'prince', 'duchess', 'lassie']),
+  ...    )
+
+  >>> dog = dog.bind(object())
+
+When we get a choice input widget for a choice field, the default widget
+factory gets a view on the field and the field's source.  We'll just create the
+view directly::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceSelectWidget(
+  ...     dog, dog.source, request)
+
+  >>> print widget()
+  <div>
+  <div class="value">
+  <select id="field.dog" name="field.dog" size="5" >
+  <option value="c3BvdA==">spot</option>
+  <option value="Ym93c2Vy">bowser</option>
+  <option value="cHJpbmNl">prince</option>
+  <option value="ZHVjaGVzcw==">duchess</option>
+  <option value="bGFzc2ll">lassie</option>
+  </select>
+  </div>
+  <input name="field.dog-empty-marker" type="hidden" value="1" />
+  </div>
+
+Since the field is required, an empty selection is not valid:
+
+  >>> widget.getInputValue()
+  Traceback (most recent call last):
+  MissingInputError: ('field.dog', u'Dogs', None)
+
+Also, the widget is required in this case:
+
+  >>> widget.required
+  True
+
+If the request contains a value, it is marked as selected::
+
+  >>> request.form["field.dog-empty-marker"] = "1"
+  >>> request.form["field.dog"] = "Ym93c2Vy"
+
+  >>> print widget()
+  <div>
+  <div class="value">
+  <select id="field.dog" name="field.dog" size="5" >
+  <option value="c3BvdA==">spot</option>
+  <option selected="selected" value="Ym93c2Vy">bowser</option>
+  <option value="cHJpbmNl">prince</option>
+  <option value="ZHVjaGVzcw==">duchess</option>
+  <option value="bGFzc2ll">lassie</option>
+  </select>
+  </div>
+  <input name="field.dog-empty-marker" type="hidden" value="1" />
+  </div>
+
+If we set the displayed value for the widget, that value is marked as
+selected::
+
+  >>> widget.setRenderedValue("duchess")
+  >>> print widget()
+  <div>
+  <div class="value">
+  <select id="field.dog" name="field.dog" size="5" >
+  <option value="c3BvdA==">spot</option>
+  <option value="Ym93c2Vy">bowser</option>
+  <option value="cHJpbmNl">prince</option>
+  <option selected="selected" value="ZHVjaGVzcw==">duchess</option>
+  <option value="bGFzc2ll">lassie</option>
+  </select>
+  </div>
+  <input name="field.dog-empty-marker" type="hidden" value="1" />
+  </div>
+
+Dropdown widgets are achieved with SourceDropdownWidget, which simply
+generates a selection list of size 1::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceDropdownWidget(
+  ...     dog, dog.source, request)
+  >>> print widget() # doctest: +ELLIPSIS
+  <div>
+  <div class="value">
+  <select id="field.dog" name="field.dog" size="1" >
+  <option selected="selected" value="">(no value)</option>...
+
+An alternative to SourceSelectWidget for small numbers of items is
+SourceRadioWidget that provides a radio button group for the items::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceRadioWidget(
+  ...     dog, dog.source, request)
+  >>> print widget() # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <label for="field.dog.0"><input class="radioType" id="field.dog.0"
+      name="field.dog" type="radio" value="c3BvdA==" />&nbsp;spot</label><br
+  /><label for="field.dog.1"><input class="radioType" id="field.dog.1"
+      name="field.dog" type="radio" value="Ym93c2Vy" />&nbsp;bowser</label><br
+  /><label for="field.dog.2"><input class="radioType" id="field.dog.2"
+      name="field.dog" type="radio" value="cHJpbmNl" />&nbsp;prince</label><br
+  /><label for="field.dog.3"><input class="radioType" id="field.dog.3"
+      name="field.dog" type="radio" value="ZHVjaGVzcw==" />&nbsp;duchess</label><br
+  /><label for="field.dog.4"><input class="radioType" id="field.dog.4"
+      name="field.dog" type="radio" value="bGFzc2ll" />&nbsp;lassie</label>
+  </div>
+  <input name="field.dog-empty-marker" type="hidden" value="1" />
+  </div>
+
+We'll select an item by setting the appropriate fields in the request::
+
+  >>> request.form['field.dog-empty-marker'] = '1'
+  >>> request.form['field.dog'] = 'bGFzc2ll'
+  >>>
+  >>> widget = zope.app.form.browser.source.SourceRadioWidget(
+  ...     dog, dog.source, request)
+  >>> print widget() # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <label for="field.dog.0"><input class="radioType" id="field.dog.0"
+      name="field.dog" type="radio" value="c3BvdA==" />&nbsp;spot</label><br
+  /><label for="field.dog.1"><input class="radioType" id="field.dog.1"
+      name="field.dog" type="radio" value="Ym93c2Vy" />&nbsp;bowser</label><br
+  /><label for="field.dog.2"><input class="radioType" id="field.dog.2"
+      name="field.dog" type="radio" value="cHJpbmNl" />&nbsp;prince</label><br
+  /><label for="field.dog.3"><input class="radioType" id="field.dog.3"
+      name="field.dog" type="radio" value="ZHVjaGVzcw==" />&nbsp;duchess</label><br
+  /><label for="field.dog.4"><input class="radioType" checked="checked"
+      id="field.dog.4" name="field.dog" type="radio" value="bGFzc2ll"
+      />&nbsp;lassie</label>
+  </div>
+  <input name="field.dog-empty-marker" type="hidden" value="1" />
+  </div>
+
+For list-valued fields with items chosen from iterable sources, there are the
+SourceMultiSelectWidget and SourceOrderedMultiSelectWidget widgets. The latter
+widget includes support for re-ording the list items.
+SourceOrderedMultiSelectWidget is configured as the default widget for lists of
+choices.
+
+If you don't need ordering support through the web UI, then you can use
+the simpler SourceMultiSelectWidget::
+
+  >>> dogSource = SourceList([
+  ...     u'spot', u'bowser', u'prince', u'duchess', u'lassie'])
+  >>> dogs = zope.schema.List(
+  ...     __name__ = 'dogs',
+  ...     title=u"Dogs",
+  ...     value_type=zope.schema.Choice(
+  ...         source=dogSource,
+  ...     )
+  ... )
+  >>> dogs = dogs.bind(object()) # give the field a context
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceMultiSelectWidget(
+  ...     dogs, dogSource, request)
+
+Let's look at the rendered widget::
+
+  >>> print widget() # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <select id="field.dogs" multiple="multiple" name="field.dogs:list"
+    size="5" ><option value="c3BvdA==">spot</option>
+  <option value="Ym93c2Vy">bowser</option>
+  <option value="cHJpbmNl">prince</option>
+  <option value="ZHVjaGVzcw==">duchess</option>
+  <option value="bGFzc2ll">lassie</option></select>
+  </div>
+  <input name="field.dogs-empty-marker" type="hidden" value="1" />
+  </div>
+
+We have no input yet::
+
+  >>> try:
+  ...     widget.getInputValue()
+  ... except zope.formlib.interfaces.MissingInputError:
+  ...     print 'no input'
+  no input
+
+Select an item::
+
+  >>> request.form['field.dogs-empty-marker'] = '1'
+  >>> request.form['field.dogs'] = ['bGFzc2ll']
+  >>> widget.getInputValue()
+  ['lassie']
+
+and another::
+
+  >>> request.form['field.dogs'] = ['cHJpbmNl', 'bGFzc2ll']
+  >>> widget.getInputValue()
+  ['prince', 'lassie']
+
+Finally, what does the widget look like now::
+
+  >>> print widget() # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <select id="field.dogs" multiple="multiple" name="field.dogs:list"
+    size="5" ><option value="c3BvdA==">spot</option>
+  <option value="Ym93c2Vy">bowser</option>
+  <option selected="selected" value="cHJpbmNl">prince</option>
+  <option value="ZHVjaGVzcw==">duchess</option>
+  <option selected="selected" value="bGFzc2ll">lassie</option></select>
+  </div>
+  <input name="field.dogs-empty-marker" type="hidden" value="1" />
+  </div>
+
+
+An alternative for small numbers of items is to use SourceMultiCheckBoxWidget::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceMultiCheckBoxWidget(
+  ...     dogs, dogSource, request)
+
+The rendered widget::
+
+  >>> print widget() # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <label for="field.dogs.0"><input class="checkboxType" id="field.dogs.0"
+    name="field.dogs" type="checkbox" value="c3BvdA==" />&nbsp;spot</label><br
+    /><label for="field.dogs.1"><input class="checkboxType" id="field.dogs.1"
+        name="field.dogs" type="checkbox" value="Ym93c2Vy"
+        />&nbsp;bowser</label><br
+    /><label for="field.dogs.2"><input class="checkboxType" id="field.dogs.2"
+        name="field.dogs" type="checkbox" value="cHJpbmNl"
+        />&nbsp;prince</label><br
+    /><label for="field.dogs.3"><input class="checkboxType" id="field.dogs.3"
+        name="field.dogs" type="checkbox"
+        value="ZHVjaGVzcw==" />&nbsp;duchess</label><br
+    /><label for="field.dogs.4"><input class="checkboxType" id="field.dogs.4"
+        name="field.dogs" type="checkbox" value="bGFzc2ll"
+        />&nbsp;lassie</label>
+  </div>
+  <input name="field.dogs-empty-marker" type="hidden" value="1" />
+  </div>
+
+We have no input yet::
+
+  >>> try:
+  ...     widget.getInputValue()
+  ... except zope.formlib.interfaces.MissingInputError:
+  ...     print 'no input'
+  no input
+
+Select an item::
+
+  >>> request.form['field.dogs-empty-marker'] = '1'
+  >>> request.form['field.dogs'] = ['bGFzc2ll']
+  >>> widget.getInputValue()
+  ['lassie']
+
+and another::
+
+  >>> request.form['field.dogs'] = ['c3BvdA==', 'bGFzc2ll']
+  >>> widget.getInputValue()
+  ['spot', 'lassie']
+
+Finally, what does the widget look like now::
+
+  >>> print widget() # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <label for="field.dogs.0"><input class="checkboxType" checked="checked"
+    id="field.dogs.0" name="field.dogs" type="checkbox" value="c3BvdA=="
+    />&nbsp;spot</label><br
+    /><label for="field.dogs.1"><input class="checkboxType" id="field.dogs.1"
+        name="field.dogs" type="checkbox" value="Ym93c2Vy"
+        />&nbsp;bowser</label><br
+    /><label for="field.dogs.2"><input class="checkboxType" id="field.dogs.2"
+        name="field.dogs" type="checkbox" value="cHJpbmNl"
+        />&nbsp;prince</label><br
+    /><label for="field.dogs.3"><input class="checkboxType" id="field.dogs.3"
+        name="field.dogs" type="checkbox"
+        value="ZHVjaGVzcw==" />&nbsp;duchess</label><br
+    /><label for="field.dogs.4"><input class="checkboxType" checked="checked"
+        id="field.dogs.4" name="field.dogs" type="checkbox" value="bGFzc2ll"
+        />&nbsp;lassie</label>
+  </div>
+  <input name="field.dogs-empty-marker" type="hidden" value="1" />
+  </div>
+
+
+For list ordering support, use SourceOrderedMultiSelectWidget::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceOrderedMultiSelectWidget(
+  ...     dogs, dogSource, request)
+
+The widget is too complicated to show in complete rendered form here.
+Insted, we'll inspect the properties of the widget::
+
+  >>> from zope.formlib.interfaces import MissingInputError
+  >>> try:
+  ...     widget.getInputValue()
+  ... except MissingInputError:
+  ...     print 'no input'
+  no input
+
+  >>> widget.choices() == [
+  ...     {'text': u'spot',    'value': 'c3BvdA=='},
+  ...     {'text': u'bowser',  'value': 'Ym93c2Vy'},
+  ...     {'text': u'prince',  'value': 'cHJpbmNl'},
+  ...     {'text': u'duchess', 'value': 'ZHVjaGVzcw=='},
+  ...     {'text': u'lassie',  'value': 'bGFzc2ll'}
+  ... ]
+  True
+
+  >>> widget.selected()
+  []
+
+Let's try out selecting items. Select one item::
+
+  >>> request.form['field.dogs-empty-marker'] = '1'
+  >>> request.form['field.dogs'] = ['bGFzc2ll']
+  >>> widget.selected() # doctest: +NORMALIZE_WHITESPACE
+  [{'text': u'lassie',  'value': 'bGFzc2ll'}]
+
+  >>> widget.getInputValue()
+  ['lassie']
+
+Select two items::
+
+  >>> request.form['field.dogs'] = ['c3BvdA==', 'bGFzc2ll']
+  >>> widget.selected()  # doctest: +NORMALIZE_WHITESPACE
+  [{'text': u'spot',    'value': 'c3BvdA=='},
+   {'text': u'lassie',  'value': 'bGFzc2ll'}]
+
+  >>> widget.getInputValue()
+  ['spot', 'lassie']
+
+
+For set-valued fields, use SourceMultiSelectSetWidget::
+
+  >>> dogSet = zope.schema.Set(
+  ...     __name__ = 'dogSet',
+  ...     title=u"Dogs",
+  ...     value_type=zope.schema.Choice(
+  ...         source=dogSource,
+  ...     )
+  ... )
+  >>> dogSet = dogSet.bind(object()) # give the field a context
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceMultiSelectSetWidget(
+  ...     dogSet, dogSource, request)
+
+  >>> try:
+  ...     widget.getInputValue()
+  ... except zope.formlib.interfaces.MissingInputError:
+  ...     print 'no input'
+  no input
+
+  >>> print widget()  # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <select id="field.dogSet" multiple="multiple"
+      name="field.dogSet:list" size="5" ><option value="c3BvdA==">spot</option>
+  <option value="Ym93c2Vy">bowser</option>
+  <option value="cHJpbmNl">prince</option>
+  <option value="ZHVjaGVzcw==">duchess</option>
+  <option value="bGFzc2ll">lassie</option></select>
+  </div>
+  <input name="field.dogSet-empty-marker" type="hidden" value="1" />
+  </div>
+
+Let's try out selecting items. Select one item::
+
+  >>> request.form['field.dogSet-empty-marker'] = '1'
+  >>> request.form['field.dogSet'] = ['bGFzc2ll']
+  >>> widget.getInputValue()
+  set(['lassie'])
+
+Select two items::
+
+  >>> request.form['field.dogSet'] = ['c3BvdA==', 'bGFzc2ll']
+  >>> widget.getInputValue()
+  set(['spot', 'lassie'])
+
+The rendered widget (still with the two items selected) looks like this::
+
+  >>> print widget()  # doctest: +NORMALIZE_WHITESPACE
+  <div>
+  <div class="value">
+  <select id="field.dogSet" multiple="multiple"
+      name="field.dogSet:list" size="5" ><option selected="selected"
+      value="c3BvdA==">spot</option>
+  <option value="Ym93c2Vy">bowser</option>
+  <option value="cHJpbmNl">prince</option>
+  <option value="ZHVjaGVzcw==">duchess</option>
+  <option selected="selected" value="bGFzc2ll">lassie</option></select>
+  </div>
+  <input name="field.dogSet-empty-marker" type="hidden" value="1" />
+  </div>
+
+
+
+Source Widget Query Framework
+=============================
+
+An important aspect of sources is that they may have too many values to
+enumerate.  Rather than listing all of the values, we, instead, provide
+interfaces for querying values and selecting values from query results.
+Matters are further complicated by the fact that different sources may have
+very different interfaces for querying them.
+
+To make matters more interesting, a source may be an aggregation of several
+collections, each with their own querying facilities. An example of such a
+source is a principal source, where principals might come from a number of
+places, such as an LDAP database and ZCML-based principal definitions.
+
+The default widgets for selecting values from sources use the
+following approach:
+
+- One or more query objects are obtained from the source by adapting the source
+  to `zope.schema.ISourceQueriables`. If no adapter is obtained, then the
+  source itself is assumed to be queriable.
+
+- For each queriable found, a
+  `zope.app.form.browser.interfaces.ISourceQueryView` view is looked up. This
+  view is used to obtain the HTML for displaying a query form. The view is also
+  used to obtain search results.
+
+Let's start with a simple example.  We have a very trivial source,
+which is basically a list:
+
+  >>> class SourceList(list):
+  ...     zope.interface.implements(zope.schema.interfaces.ISource)
+
+We need to register our `ITerms` view::
+
+  >>> zope.component.provideAdapter(
+  ...     ListTerms,
+  ...     (SourceList, zope.publisher.interfaces.browser.IBrowserRequest))
+
+We aren't going to provide an adapter to `ISourceQueriables`, so the source
+itself will be used as it's own queriable.  We need to provide a query view
+for the source::
+
+  >>> class ListQueryView:
+  ...
+  ...     zope.interface.implements(
+  ...         zope.app.form.browser.interfaces.ISourceQueryView)
+  ...     zope.component.adapts(
+  ...         SourceList,
+  ...         zope.publisher.interfaces.browser.IBrowserRequest,
+  ...         )
+  ...
+  ...     def __init__(self, source, request):
+  ...         self.source = source
+  ...         self.request = request
+  ...
+  ...     def render(self, name):
+  ...         return (
+  ...             '<input name="%s.string">\n'
+  ...             '<input type="submit" name="%s" value="Search">'
+  ...             % (name, name)
+  ...             )
+  ...
+  ...     def results(self, name):
+  ...         if name in self.request:
+  ...             search_string = self.request.get(name+'.string')
+  ...             if search_string is not None:
+  ...                 return [value
+  ...                         for value in self.source
+  ...                         if search_string in value
+  ...                         ]
+  ...         return None
+
+  >>> zope.component.provideAdapter(ListQueryView)
+
+Now, we can define a choice field::
+
+  >>> dog = zope.schema.Choice(
+  ...    __name__ = 'dog',
+  ...    title=u"Dogs",
+  ...    source=SourceList(['spot', 'bowser', 'prince', 'duchess', 'lassie']),
+  ...    )
+
+As before, we'll just create the view directly::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceInputWidget(
+  ...     dog, dog.source, request)
+
+Now if we render the widget, we'll see the input value (initially nothing) and
+a form elements for seaching for values::
+
+  >>> print widget()
+  <div class="value">
+    <div class="row">
+      <div class="label">
+       Selected
+      </div>
+      <div class="field">
+       Nothing
+      </div>
+    </div>
+    <input type="hidden" name="field.dog.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.dog.query.string">
+  <input type="submit" name="field.dog.query" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+This shows that we haven't selected a dog. We get a search box that we can type
+seach strings into.  Let's supply a search string. We do this by providing data
+in the form and by "selecting" the submit button::
+
+  >>> request.form['field.dog.displayed'] = u'y'
+  >>> request.form['field.dog.query.string'] = u'o'
+  >>> request.form['field.dog.query'] = u'Search'
+
+Because the field is required, a non-selection is not valid. Thus, while the
+widget still hasInput, it will raise an error when you getInputValue::
+
+  >>> widget.hasInput()
+  True
+  >>> widget.getInputValue()
+  Traceback (most recent call last):
+  ...
+  MissingInputError: ('dog', u'Dogs', None)
+
+If the field is not required::
+
+  >>> dog.required = False
+
+then as long as the field is displayed, the widget still has input but returns
+the field's missing value::
+
+  >>> widget.hasInput()
+  True
+  >>> widget.getInputValue() # None
+
+Now if we render the widget, we'll see the search results::
+
+  >>> dog.required = True
+  >>> print widget()
+  <div class="value">
+    <div class="row">
+      <div class="label">
+       Selected
+      </div>
+      <div class="field">
+       Nothing
+      </div>
+    </div>
+    <input type="hidden" name="field.dog.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.dog.query.string">
+  <input type="submit" name="field.dog.query" value="Search">
+        </div> <!-- queryinput -->
+        <div class="queryresults">
+  <select name="field.dog.query.selection">
+  <option value="Ym93c2Vy">bowser</option>
+  <option value="c3BvdA==">spot</option>
+  </select>
+  <input type="submit" name="field.dog.query.apply" value="Apply" />
+        </div> <!-- queryresults -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+If we select an item::
+
+  >>> request.form['field.dog.displayed'] = u'y'
+  >>> del request.form['field.dog.query.string']
+  >>> del request.form['field.dog.query']
+  >>> request.form['field.dog.query.selection'] = u'c3BvdA=='
+  >>> request.form['field.dog.query.apply'] = u'Apply'
+
+Then we'll show the newly selected value::
+
+  >>> print widget()
+  <div class="value">
+    <div class="row">
+      <div class="label">
+       Selected
+      </div>
+      <div class="field">
+       spot
+      </div>
+    </div>
+    <input type="hidden" name="field.dog" value="c3BvdA==" />
+    <input type="hidden" name="field.dog.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.dog.query.string">
+  <input type="submit" name="field.dog.query" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+Note that we should have an input value now, since pressing the 'Apply' button
+provides us with input::
+
+  >>> widget.hasInput()
+  True
+
+We should also be able to get the input value::
+
+  >>> widget.getInputValue()
+  'spot'
+
+Now, let's look at a more complicated example. We'll define a source that
+combines multiple sources::
+
+  >>> class MultiSource:
+  ...
+  ...     zope.interface.implements(
+  ...        zope.schema.interfaces.ISource,
+  ...        zope.schema.interfaces.ISourceQueriables,
+  ...        )
+  ...
+  ...     def __init__(self, *sources):
+  ...         self.sources = [(unicode(i), s) for (i, s) in enumerate(sources)]
+  ...
+  ...     def __contains__(self, value):
+  ...         for i, s in self.sources:
+  ...             if value in s:
+  ...                 return True
+  ...         return False
+  ...
+  ...     def getQueriables(self):
+  ...         return self.sources
+
+This multi-source implements `ISourceQueriables`. It assumes that the sources
+it's given are queriable and just returns the sources as the queryable objects.
+
+We can reuse our terms view::
+
+  >>> zope.component.provideAdapter(
+  ...     ListTerms,
+  ...     (MultiSource, zope.publisher.interfaces.browser.IBrowserRequest))
+
+Now, we'll create a pet choice that combines dogs and cats::
+
+  >>> pet = zope.schema.Choice(
+  ...    __name__ = 'pet',
+  ...    title=u"Dogs and Cats",
+  ...    source=MultiSource(
+  ...      dog.source,
+  ...      SourceList(['boots', 'puss', 'tabby', 'tom', 'tiger']),
+  ...      ),
+  ...    )
+
+and a widget::
+
+  >>> widget = zope.app.form.browser.source.SourceInputWidget(
+  ...     pet, pet.source, request)
+
+Now if we display the widget, we'll see search inputs for both dogs
+and cats::
+
+  >>> print widget()
+  <div class="value">
+    <div class="row">
+      <div class="label">
+       Selected
+      </div>
+      <div class="field">
+       Nothing
+      </div>
+    </div>
+    <input type="hidden" name="field.pet.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pet.MA__.string">
+  <input type="submit" name="field.pet.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pet.MQ__.string">
+  <input type="submit" name="field.pet.MQ__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+As before, we can perform a search::
+
+  >>> request.form['field.pet.displayed'] = u'y'
+  >>> request.form['field.pet.MQ__.string'] = u't'
+  >>> request.form['field.pet.MQ__'] = u'Search'
+
+In which case, we'll get some results::
+
+  >>> print widget() # doctest:
+  <div class="value">
+    <div class="row">
+      <div class="label">
+       Selected
+      </div>
+      <div class="field">
+       Nothing
+      </div>
+    </div>
+    <input type="hidden" name="field.pet.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pet.MA__.string">
+  <input type="submit" name="field.pet.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pet.MQ__.string">
+  <input type="submit" name="field.pet.MQ__" value="Search">
+        </div> <!-- queryinput -->
+        <div class="queryresults">
+  <select name="field.pet.MQ__.selection">
+  <option value="Ym9vdHM=">boots</option>
+  <option value="dGFiYnk=">tabby</option>
+  <option value="dGlnZXI=">tiger</option>
+  <option value="dG9t">tom</option>
+  </select>
+  <input type="submit" name="field.pet.MQ__.apply" value="Apply" />
+        </div> <!-- queryresults -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+from which we can choose::
+
+  >>> request.form['field.pet.displayed'] = u'y'
+  >>> del request.form['field.pet.MQ__.string']
+  >>> del request.form['field.pet.MQ__']
+  >>> request.form['field.pet.MQ__.selection'] = u'dGFiYnk='
+  >>> request.form['field.pet.MQ__.apply'] = u'Apply'
+
+and get a selection::
+
+  >>> print widget()
+  <div class="value">
+    <div class="row">
+      <div class="label">
+       Selected
+      </div>
+      <div class="field">
+       tabby
+      </div>
+    </div>
+    <input type="hidden" name="field.pet" value="dGFiYnk=" />
+    <input type="hidden" name="field.pet.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pet.MA__.string">
+  <input type="submit" name="field.pet.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pet.MQ__.string">
+  <input type="submit" name="field.pet.MQ__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+Note that we should have an input value now, since pressing the 'Apply' button
+provides us with input::
+
+  >>> widget.hasInput()
+  True
+
+and we can get the input value::
+
+  >>> widget.getInputValue()
+  'tabby'
+
+There's a display widget, which doesn't use queriables, since it doesn't assign
+values::
+
+  >>> request = TestRequest()
+  >>> widget = zope.app.form.browser.source.SourceDisplayWidget(
+  ...     pet, pet.source, request)
+  >>> print widget()
+  Nothing
+  >>> from zope.app.form.browser.interfaces import IBrowserWidget
+  >>> IBrowserWidget.providedBy(widget)
+  True
+
+  >>> widget.setRenderedValue('tabby')
+  >>> print widget()
+  tabby
+
+Like any good display widget, input is not required::
+
+  >>> widget.required
+  False
+
+If we specify a list of choices::
+
+  >>> pets = zope.schema.List(__name__ = 'pets', title=u"Pets",
+  ...                         value_type=pet)
+
+when a widget is computed for the field, a view will be looked up for the field
+and the source, where, in this case, the field is a list field. We'll just call
+the widget factory directly::
+
+  >>> widget = zope.app.form.browser.source.SourceListInputWidget(
+  ...     pets, pets.value_type.source, request)
+
+If we render the widget::
+
+  >>> print widget()
+  <div class="value">
+    <input type="hidden" name="field.pets.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MA__.string">
+  <input type="submit" name="field.pets.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MQ__.string">
+  <input type="submit" name="field.pets.MQ__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+Here the output looks very similar to the simple choice case.  We get a search
+input for each source.  In this case, we don't show any inputs
+(TODO we probably should make it clearer that there are no selected values.)
+
+As before, we can search one of the sources::
+
+  >>> request.form['field.pets.displayed'] = u'y'
+  >>> request.form['field.pets.MQ__.string'] = u't'
+  >>> request.form['field.pets.MQ__'] = u'Search'
+
+In which case, we'll get some results::
+
+  >>> print widget()
+  <div class="value">
+    <input type="hidden" name="field.pets.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MA__.string">
+  <input type="submit" name="field.pets.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MQ__.string">
+  <input type="submit" name="field.pets.MQ__" value="Search">
+        </div> <!-- queryinput -->
+        <div class="queryresults">
+  <select name="field.pets.MQ__.selection:list" multiple>
+  <option value="Ym9vdHM=">boots</option>
+  <option value="dGFiYnk=">tabby</option>
+  <option value="dGlnZXI=">tiger</option>
+  <option value="dG9t">tom</option>
+  </select>
+  <input type="submit" name="field.pets.MQ__.apply" value="Apply" />
+        </div> <!-- queryresults -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+from which we can select some values::
+
+  >>> request.form['field.pets.displayed'] = u'y'
+  >>> del request.form['field.pets.MQ__.string']
+  >>> del request.form['field.pets.MQ__']
+  >>> request.form['field.pets.MQ__.selection'] = [
+  ...     u'dGFiYnk=', u'dGlnZXI=', u'dG9t']
+  >>> request.form['field.pets.MQ__.apply'] = u'Apply'
+
+Which then leads to the selections appearing as widget selections::
+
+  >>> print widget()
+  <div class="value">
+    <input type="checkbox" name="field.pets.checked:list" value="dGFiYnk=" />
+    tabby
+    <input type="hidden" name="field.pets:list" value="dGFiYnk=" />
+    <br />
+    <input type="checkbox" name="field.pets.checked:list" value="dGlnZXI=" />
+    tiger
+    <input type="hidden" name="field.pets:list" value="dGlnZXI=" />
+    <br />
+    <input type="checkbox" name="field.pets.checked:list" value="dG9t" />
+    tom
+    <input type="hidden" name="field.pets:list" value="dG9t" />
+    <br />
+    <input type="submit" name="field.pets.remove" value="Remove" />
+    <br />
+    <input type="hidden" name="field.pets.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MA__.string">
+  <input type="submit" name="field.pets.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MQ__.string">
+  <input type="submit" name="field.pets.MQ__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+We can get the selected values::
+
+  >>> widget.getInputValue()
+  ['tabby', 'tiger', 'tom']
+
+We now see the values we selected.  We also have checkboxes and buttons that
+allow us to remove selections::
+
+  >>> request.form['field.pets.displayed'] = u'y'
+  >>> request.form['field.pets'] = [u'dGFiYnk=', u'dGlnZXI=', u'dG9t']
+  >>> del request.form['field.pets.MQ__.selection']
+  >>> del request.form['field.pets.MQ__.apply']
+  >>> request.form['field.pets.checked'] = [u'dGFiYnk=', u'dG9t']
+  >>> request.form['field.pets.remove'] = u'Remove'
+
+  >>> print widget()
+  <div class="value">
+    <input type="checkbox" name="field.pets.checked:list" value="dGlnZXI=" />
+    tiger
+    <input type="hidden" name="field.pets:list" value="dGlnZXI=" />
+    <br />
+    <input type="submit" name="field.pets.remove" value="Remove" />
+    <br />
+    <input type="hidden" name="field.pets.displayed" value="y" />
+    <div class="queries">
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MA__.string">
+  <input type="submit" name="field.pets.MA__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+      <div class="query">
+        <div class="queryinput">
+  <input name="field.pets.MQ__.string">
+  <input type="submit" name="field.pets.MQ__" value="Search">
+        </div> <!-- queryinput -->
+      </div> <!-- query -->
+    </div> <!-- queries -->
+  </div> <!-- value -->
+
+
+Using vocabulary-dependent widgets with sources
+===============================================
+
+if you have a widget that uses old-style vocabularies but don't have the time
+to rewrite it for sources, all is not lost! The wrapper
+IterableSourceVocabulary can be used to make sources and ITerms look like a
+vocabulary. This allows us to use vocabulary-based widgets with sources
+instead of vocabularies.
+
+Usage::
+
+  >>> from zope.schema.vocabulary import SimpleTerm
+
+  >>> values  = [u'a', u'b', u'c']
+  >>> tokens  = [ '0',  '1',  '2']
+  >>> titles  = [u'A', u'B', u'C']
+
+  >>> terms = [SimpleTerm(values[i], token=tokens[i], title=titles[i]) \
+  ...     for i in range(0,len(values))]
+
+  >>> class TestSource(list):
+  ...     zope.interface.implements(zope.schema.interfaces.IIterableSource)
+  >>> source = TestSource(values)
+
+  >>> class TestTerms(object):
+  ...     zope.interface.implements(ITerms)
+  ...     def __init__(self, source, request):
+  ...         pass
+  ...     def getTerm(self, value):
+  ...         index = values.index(value)
+  ...         return terms[index]
+  ...     def getValue(self, token):
+  ...         index = tokens.index(token)
+  ...         return values[index]
+
+  >>> zope.component.provideAdapter(
+  ...     TestTerms,
+  ...     (TestSource, zope.publisher.interfaces.browser.IBrowserRequest))
+
+  >>> from zope.app.form.browser.source import IterableSourceVocabulary
+  >>> request = TestRequest()
+  >>> vocab = IterableSourceVocabulary(source, request)
+  >>> from zope.interface.verify import verifyClass, verifyObject
+  >>> verifyClass(zope.schema.interfaces.IVocabularyTokenized, \
+  ...     IterableSourceVocabulary)
+  True
+  >>> verifyObject(zope.schema.interfaces.IVocabularyTokenized, vocab)
+  True
+
+  >>> len(vocab)
+  3
+  >>> (u'a' in vocab) and (u'b' in vocab) and (u'c' in vocab)
+  True
+  >>> [value for value in vocab] == terms
+  True
+  >>> term = vocab.getTerm(u'b')
+  >>> (term.value, term.token, term.title)
+  (u'b', '1', u'B')
+  >>> term = vocab.getTermByToken('2')
+  >>> (term.value, term.token, term.title)
+  (u'c', '2', u'C')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicecollections.py (from rev 107371, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_choicecollections.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicecollections.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicecollections.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,73 @@
+##############################################################################
+#
+# 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 the choice collections widgets (function).
+
+$Id$
+"""
+import unittest
+from zope.component.testing import PlacelessSetup
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.browser import TestRequest
+from zope.schema.interfaces import IList, IChoice, IIterableVocabulary
+from zope.schema import Choice, List
+
+from zope.component import provideAdapter
+
+from zope.formlib.interfaces import IInputWidget, IDisplayWidget
+from zope.formlib.widgets import CollectionDisplayWidget
+from zope.formlib.widgets import CollectionInputWidget
+from zope.formlib.widgets import ChoiceCollectionDisplayWidget
+from zope.formlib.widgets import ChoiceCollectionInputWidget
+from zope.formlib.widgets import ItemsMultiDisplayWidget, SelectWidget
+
+class ListOfChoicesWidgetTest(PlacelessSetup, unittest.TestCase):
+
+    def test_ListOfChoicesDisplayWidget(self):
+        provideAdapter(ChoiceCollectionDisplayWidget,
+                       (IList, IChoice, IBrowserRequest),
+                       IDisplayWidget)
+        provideAdapter(ItemsMultiDisplayWidget,
+                       (IList, IIterableVocabulary, IBrowserRequest),
+                       IDisplayWidget)
+        field = List(value_type=Choice(values=[1, 2, 3]))
+        bound = field.bind(object())
+        widget = CollectionDisplayWidget(bound, TestRequest())
+        self.assert_(isinstance(widget, ItemsMultiDisplayWidget))
+        self.assertEqual(widget.context, bound)
+        self.assertEqual(widget.vocabulary, bound.value_type.vocabulary)
+
+
+    def test_ChoiceSequenceEditWidget(self):
+        provideAdapter(ChoiceCollectionInputWidget,
+                       (IList, IChoice, IBrowserRequest),
+                       IInputWidget)
+        provideAdapter(SelectWidget,
+                       (IList, IIterableVocabulary, IBrowserRequest),
+                       IInputWidget)
+        field = List(value_type=Choice(values=[1, 2, 3]))
+        bound = field.bind(object())
+        widget = CollectionInputWidget(bound, TestRequest())
+        self.assert_(isinstance(widget, SelectWidget))
+        self.assertEqual(widget.context, bound)
+        self.assertEqual(widget.vocabulary, bound.value_type.vocabulary)
+        
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(ListOfChoicesWidgetTest),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicewidget.py (from rev 107371, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_choicewidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicewidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_choicewidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# 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 the Choice display and edit widget (function).
+
+$Id$
+"""
+import unittest
+from zope.component.testing import PlacelessSetup
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.browser import TestRequest
+from zope.schema.interfaces import IChoice, IIterableVocabulary
+from zope.schema import Choice
+
+from zope.component import provideAdapter
+from zope.formlib.interfaces import IInputWidget, IDisplayWidget
+from zope.formlib.widgets import ChoiceDisplayWidget, ChoiceInputWidget
+from zope.formlib.widgets import ItemDisplayWidget, DropdownWidget
+
+
+class ChoiceWidgetTest(PlacelessSetup, unittest.TestCase):
+
+    def test_ChoiceDisplayWidget(self):
+        provideAdapter(ItemDisplayWidget,
+                       (IChoice, IIterableVocabulary, IBrowserRequest),
+                       IDisplayWidget)
+        field = Choice(values=[1, 2, 3])
+        bound = field.bind(object())
+        widget = ChoiceDisplayWidget(bound, TestRequest())
+        self.assert_(isinstance(widget, ItemDisplayWidget))
+        self.assertEqual(widget.context, bound)
+        self.assertEqual(widget.vocabulary, bound.vocabulary)
+    
+    def test_ChoiceInputWidget(self):
+        provideAdapter(DropdownWidget,
+                       (IChoice, IIterableVocabulary, IBrowserRequest),
+                       IInputWidget)
+        field = Choice(values=[1, 2, 3])
+        bound = field.bind(object())
+        widget = ChoiceInputWidget(bound, TestRequest())
+        self.assert_(isinstance(widget, DropdownWidget))
+        self.assertEqual(widget.context, bound)
+        self.assertEqual(widget.vocabulary, bound.vocabulary)
+        
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(ChoiceWidgetTest),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datetimewidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_datetimewidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datetimewidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datetimewidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,177 @@
+##############################################################################
+#
+# 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 Tests
+
+$Id$
+"""
+import datetime
+import unittest
+from zope.testing import doctest
+
+from zope.schema import Datetime
+from zope.datetime import parseDatetimetz, tzinfo
+from zope.interface.verify import verifyClass
+
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import DatetimeWidget
+from zope.formlib.widgets import DatetimeI18nWidget
+from zope.formlib.interfaces import ConversionError, WidgetInputError
+
+
+class DatetimeWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the datetime widget.
+
+        >>> verifyClass(IInputWidget, DatetimeWidget)
+        True
+    """
+
+    _FieldFactory = Datetime
+    _WidgetFactory = DatetimeWidget
+
+    def testRender(self):
+        super(DatetimeWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="2004-03-26 12:58:59"'))
+
+    def test_hasInput(self):
+        del self._widget.request.form['field.foo']
+        self.failIf(self._widget.hasInput())
+        # widget has input, even if input is an empty string
+        self._widget.request.form['field.foo'] = u''
+        self.failUnless(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'2003-03-26 12:00:00'
+        self.failUnless(self._widget.hasInput())
+
+    def test_getInputValue(self,
+            value=u'2004-03-26 12:58:59',
+            check_value=parseDatetimetz('2004-03-26 12:58:59')):
+        self._widget.request.form['field.foo'] = u''
+        self.assertRaises(WidgetInputError, self._widget.getInputValue)
+        self._widget.request.form['field.foo'] = value
+        self.assertEquals(self._widget.getInputValue(), check_value)
+        self._widget.request.form['field.foo'] = u'abc'
+        self.assertRaises(ConversionError, self._widget.getInputValue)
+
+class DatetimeI18nWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the i18n datetime widget.
+
+        >>> verifyClass(IInputWidget, DatetimeI18nWidget)
+        True
+    """
+
+    _FieldFactory = Datetime
+    _WidgetFactory = DatetimeI18nWidget
+
+    def testDefaultDisplayStyle(self):
+        self.failIf(self._widget.displayStyle)
+
+    def testRender(self):
+        super(DatetimeI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="26.03.2004 12:58:59"'))
+
+    def testRenderShort(self):
+        self._widget.displayStyle = "short"
+        super(DatetimeI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="26.03.04 12:58"'))
+
+    def testRenderMedium(self):
+        self._widget.displayStyle = "medium"
+        super(DatetimeI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="26.03.2004 12:58:59"'))
+
+    def testRenderLong(self):
+        self._widget.displayStyle = "long"
+        super(DatetimeI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                u'value="26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433.'
+                u' 12:58:59 +000"'))
+
+    def testRenderFull(self):
+        self._widget.displayStyle = "full"
+        super(DatetimeI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                u'value="26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433.'
+                u' 12:58:59 +000"'))
+
+    def test_hasInput(self):
+        del self._widget.request.form['field.foo']
+        self.failIf(self._widget.hasInput())
+        # widget has input, even if input is an empty string
+        self._widget.request.form['field.foo'] = u''
+        self.failUnless(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'26.03.2003 12:00:00'
+        self.failUnless(self._widget.hasInput())
+
+    def test_getDefaultInputValue(self,
+            value=u'26.03.2004 12:58:59',
+            check_value=datetime.datetime(2004, 3, 26, 12, 58, 59)):
+        self._widget.request.form['field.foo'] = u''
+        self.assertRaises(WidgetInputError, self._widget.getInputValue)
+        self._widget.request.form['field.foo'] = value
+        self.assertEquals(self._widget.getInputValue(), check_value)
+        self._widget.request.form['field.foo'] = u'abc'
+        self.assertRaises(ConversionError, self._widget.getInputValue)
+
+    def test_getShortInputValue(self):
+        self._widget.displayStyle = "short"
+        self.test_getDefaultInputValue(
+            value=u'26.03.04 12:58',
+            check_value=datetime.datetime(2004, 3, 26, 12, 58)
+            )
+
+    def test_getMediumInputValue(self):
+        self._widget.displayStyle = "medium"
+        self.test_getDefaultInputValue(
+            value=u'26.03.2004 12:58:59',
+            check_value=datetime.datetime(2004, 3, 26, 12, 58, 59)
+            )
+
+    def test_getLongInputValue(self):
+        self._widget.displayStyle = "long"
+        self.test_getDefaultInputValue(
+            value=(u'26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433.'
+                u' 12:58:59 +030'),
+            check_value=datetime.datetime(2004, 3, 26, 12, 58, 59,
+                tzinfo=tzinfo(30))
+            )
+
+    def test_getFullInputValue(self):
+        self._widget.displayStyle = "full"
+        self.test_getDefaultInputValue(
+            value=(u'26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433.'
+                u' 12:58:59 +030'),
+            check_value=datetime.datetime(2004, 3, 26, 12, 58, 59,
+                tzinfo=tzinfo(30))
+            )
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(DatetimeWidgetTest),
+        unittest.makeSuite(DatetimeI18nWidgetTest),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datewidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_datewidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datewidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_datewidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,160 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Date Widget tests
+
+$Id$
+"""
+import datetime
+import unittest
+from zope.testing import doctest
+from zope.datetime import parseDatetimetz
+from zope.schema import Date
+from zope.interface.verify import verifyClass
+
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import DateWidget
+from zope.formlib.widgets import DateI18nWidget
+from zope.formlib.interfaces import ConversionError, WidgetInputError
+
+
+class DateWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the date widget.
+
+        >>> verifyClass(IInputWidget, DateWidget)
+        True
+    """
+
+    _FieldFactory = Date
+    _WidgetFactory = DateWidget
+
+    def testRender(self):
+        super(DateWidgetTest, self).testRender(
+            datetime.date(2003, 3, 26),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="2003-03-26"'))
+
+    def test_hasInput(self):
+        del self._widget.request.form['field.foo']
+        self.failIf(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u''
+        self.failUnless(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'2003-03-26'
+        self.failUnless(self._widget.hasInput())
+
+    def test_getInputValue(self,
+            value=u'2004-03-26',
+            check_value=datetime.date(2004, 3, 26)):
+        self._widget.request.form['field.foo'] = u''
+        self.assertRaises(WidgetInputError, self._widget.getInputValue)
+        self._widget.request.form['field.foo'] = value
+        self.assertEquals(self._widget.getInputValue(), check_value)
+        self._widget.request.form['field.foo'] = u'abc'
+        self.assertRaises(ConversionError, self._widget.getInputValue)
+
+class DateI18nWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the i18n date widget.
+
+        >>> verifyClass(IInputWidget, DateI18nWidget)
+        True
+    """
+
+    _FieldFactory = Date
+    _WidgetFactory = DateI18nWidget
+
+    def testDefaultDisplayStyle(self):
+        self.failIf(self._widget.displayStyle)
+
+    def testRender(self):
+        super(DateI18nWidgetTest, self).testRender(
+            datetime.date(2003, 3, 26),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="26.03.2003"'))
+
+    def testRenderShort(self):
+        self._widget.displayStyle = "short"
+        super(DateI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="26.03.04"'))
+
+    def testRenderMedium(self):
+        self._widget.displayStyle = "medium"
+        super(DateI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                'value="26.03.2004"'))
+
+    def testRenderLong(self):
+        self._widget.displayStyle = "long"
+        super(DateI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                u'value="26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433."'))
+
+    def testRenderFull(self):
+        self._widget.displayStyle = "full"
+        super(DateI18nWidgetTest, self).testRender(
+            datetime.datetime(2004, 3, 26, 12, 58, 59),
+            ('type="text"', 'id="field.foo"', 'name="field.foo"',
+                u'value="26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433."'))
+
+    def test_hasInput(self):
+        del self._widget.request.form['field.foo']
+        self.failIf(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u''
+        self.failUnless(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'26.03.2003'
+        self.failUnless(self._widget.hasInput())
+
+    def test_getDefaultInputValue(self,
+            value=u'26.03.2004',
+            check_value=datetime.date(2004, 3, 26)):
+        self._widget.request.form['field.foo'] = u''
+        self.assertRaises(WidgetInputError, self._widget.getInputValue)
+        self._widget.request.form['field.foo'] = value
+        self.assertEquals(self._widget.getInputValue(), check_value)
+        self._widget.request.form['field.foo'] = u'abc'
+        self.assertRaises(ConversionError, self._widget.getInputValue)
+
+    def test_getShortInputValue(self):
+        self._widget.displayStyle = "short"
+        self.test_getDefaultInputValue(u'26.03.04')
+
+    def test_getMediumInputValue(self):
+        self._widget.displayStyle = "medium"
+        self.test_getDefaultInputValue(u'26.03.2004')
+
+    def test_getLongInputValue(self):
+        self._widget.displayStyle = "long"
+        self.test_getDefaultInputValue(
+            u'26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433.'
+            )
+
+    def test_getFullInputValue(self):
+        self._widget.displayStyle = "full"
+        self.test_getDefaultInputValue(
+            u'26 \u043c\u0430\u0440\u0442\u0430 2004 \u0433.'
+            )
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(DateWidgetTest),
+        unittest.makeSuite(DateI18nWidgetTest),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_decimalwidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_decimalwidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_decimalwidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_decimalwidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# 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 tests
+
+$Id$
+"""
+import unittest
+import decimal
+from zope.testing import doctest
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import DecimalWidget
+from zope.formlib.interfaces import ConversionError, WidgetInputError
+from zope.interface.verify import verifyClass
+
+from zope.schema import Decimal
+
+
+class DecimalWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the float widget.
+        
+        >>> verifyClass(IInputWidget, DecimalWidget)
+        True
+    """
+
+    _FieldFactory = Decimal
+    _WidgetFactory = DecimalWidget
+
+    def test_hasInput(self):
+        del self._widget.request.form['field.foo']
+        self.failIf(self._widget.hasInput())
+        # widget has input, even if input is an empty string
+        self._widget.request.form['field.foo'] = u''
+        self.failUnless(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'123'
+        self.failUnless(self._widget.hasInput())
+
+    def test_getInputValue(self):
+        self._widget.request.form['field.foo'] = u''
+        self.assertRaises(WidgetInputError, self._widget.getInputValue)
+        self._widget.request.form['field.foo'] = u'123.45'
+        self.assertEquals(self._widget.getInputValue(),
+                          decimal.Decimal("123.45"))
+        self._widget.request.form['field.foo'] = u'abc'
+        self.assertRaises(ConversionError, self._widget.getInputValue)
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(DecimalWidgetTest),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_displaywidget.py (from rev 107373, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_displaywidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_displaywidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_displaywidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,129 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Generic Text Widgets tests
+
+$Id$
+"""
+import unittest
+from zope.interface.verify import verifyClass
+from zope.interface.exceptions import DoesNotImplement
+from zope.publisher.browser import TestRequest
+from zope.schema import TextLine
+from zope.testing.doctest import DocTestSuite
+
+from zope.formlib.widget import DisplayWidget, UnicodeDisplayWidget
+
+
+def test_implemented_interfaces():
+    """Make sure that the display widget implements the correct interfaces.
+
+    Like all browser-used widgets, DisplayWidget must implement
+    `IBrowserWidget`.
+
+    >>> from zope.app.form.browser.interfaces import IBrowserWidget
+    >>> verifyClass(IBrowserWidget, DisplayWidget)
+    True
+
+    But unlike most other widgets in this package, the display widget is *not*
+    an `IInputWidget`.
+
+    >>> from zope.formlib.interfaces import IInputWidget
+    >>> try:
+    ...     verifyClass(IInputWidget, DisplayWidget)    
+    ... except DoesNotImplement:
+    ...     'not implemented'
+    'not implemented'
+    """
+
+def test_not_required():
+    """Make sure that display widgets are not required
+
+    >>> field = TextLine(title = u'Title',
+    ...                  __name__ = u'title',
+    ...                  default = u'<My Title>')
+    >>> widget = DisplayWidget(field, TestRequest())
+    >>> widget.required
+    False
+    
+    """
+
+def test_value_escaping():
+    """Make sure that the returned values are correctly escaped.
+
+    First we need to create a field that is the context of the display widget.
+    >>> field = TextLine(title = u'Title',
+    ...                  __name__ = u'title',
+    ...                  default = u'<My Title>')
+
+    >>> field = field.bind(None)
+
+    Now we are ready to instantiate our widget.
+
+    >>> widget = DisplayWidget(field, TestRequest())
+
+    If no data was specified in the widget, the field's default value will be
+    chosen.
+
+    >>> widget()
+    u'&lt;My Title&gt;'
+
+    Now let's set a value and make sure that, when output, it is also
+    correctly escaped.
+
+    >>> widget.setRenderedValue(u'<Another Title>')
+    >>> widget()
+    u'&lt;Another Title&gt;'
+
+    When the value is the missing_value, the empty string should be
+    displayed::
+
+    >>> field = TextLine(title = u'Title',
+    ...                  __name__ = u'title',
+    ...                  required = False)
+
+    >>> field = field.bind(None)
+    >>> widget = DisplayWidget(field, TestRequest())
+    >>> widget.setRenderedValue(field.missing_value)
+
+    >>> widget()
+    ''
+
+    If there's no default for the field and the value is missing on
+    the bound object, the empty string should still be displayed::
+
+    >>> field = TextLine(title=u'Title',
+    ...                  __name__=u'title',
+    ...                  required=False)
+
+    >>> class Thing:
+    ...    title = field.missing_value
+
+    >>> field = field.bind(Thing())
+    >>> widget = DisplayWidget(field, TestRequest())
+
+    >>> widget()
+    ''
+
+    """
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(DocTestSuite())
+    suite.addTest(DocTestSuite(
+        extraglobs={"DisplayWidget": UnicodeDisplayWidget}))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_intwidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_intwidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_intwidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_intwidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,65 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Integer Widget Tests
+
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+from unittest import main, makeSuite
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import IntWidget
+from zope.formlib.interfaces import ConversionError, WidgetInputError
+from zope.interface.verify import verifyClass
+
+from zope.schema import Int
+
+
+class IntWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the int widget.
+
+        >>> verifyClass(IInputWidget, IntWidget)
+        True
+    """
+
+    _FieldFactory = Int
+    _WidgetFactory = IntWidget
+
+    def test_hasInput(self):
+        del self._widget.request.form['field.foo']
+        self.failIf(self._widget.hasInput())
+        # widget has input, even if input is an empty string
+        self._widget.request.form['field.foo'] = u''
+        self.failUnless(self._widget.hasInput())
+        self._widget.request.form['field.foo'] = u'123'
+        self.failUnless(self._widget.hasInput())
+
+    def test_getInputValue(self):
+        self._widget.request.form['field.foo'] = u''
+        self.assertRaises(WidgetInputError, self._widget.getInputValue)
+        self._widget.request.form['field.foo'] = u'123'
+        self.assertEquals(self._widget.getInputValue(), 123)
+        self._widget.request.form['field.foo'] = u'abc'
+        self.assertRaises(ConversionError, self._widget.getInputValue)
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(IntWidgetTest),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_itemswidget.py (from rev 107377, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_itemswidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_itemswidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_itemswidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,595 @@
+##############################################################################
+#
+# Copyright (c) 2004 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.
+#
+##############################################################################
+"""Select Widget Tests
+
+$Id$
+"""
+import unittest
+
+from zope.component.testing import PlacelessSetup
+from zope.interface import Interface, implements
+from zope.publisher.browser import TestRequest
+from zope.schema import Choice, List, Set, FrozenSet
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
+
+import zope.formlib.itemswidgets
+from zope.formlib.itemswidgets import ItemsWidgetBase
+from zope.formlib.itemswidgets import ItemDisplayWidget
+from zope.formlib.itemswidgets import ItemsMultiDisplayWidget
+from zope.formlib.itemswidgets import ListDisplayWidget
+from zope.formlib.itemswidgets import SetDisplayWidget
+from zope.formlib.itemswidgets import ItemsEditWidgetBase
+from zope.formlib.itemswidgets import SelectWidget, DropdownWidget
+from zope.formlib.itemswidgets import RadioWidget
+from zope.formlib.itemswidgets import ItemsMultiEditWidgetBase
+from zope.formlib.itemswidgets import MultiSelectWidget
+from zope.formlib.itemswidgets import OrderedMultiSelectWidget
+from zope.formlib.itemswidgets import MultiCheckBoxWidget
+from zope.formlib.tests.support import VerifyResults
+
+vocab = SimpleVocabulary(
+    [SimpleTerm(value, token, title) for value, token, title in (
+        ('one', 'token1', 'One'),
+        ('two', 'token2', 'Two'),
+        ('three', 'token3', 'Three'))])
+
+class ICollector(Interface):
+    choice = Choice(
+        title=u"Number",
+        description=u"The Number",
+        # we want to be able to distinguish between tokens and values
+        vocabulary=vocab,
+        required=True)
+
+    numbers = List(
+        title=u"Numbers",
+        description=u"The Numbers",
+        value_type=choice,
+        required=False)
+
+    letters = Set(
+        title=u"Letters",
+        description=u"The Letters",
+        value_type=choice,
+        required=False)
+
+    frozenLetters = FrozenSet(
+        title=u"Frozen Letters",
+        description=u"The Frozen Letters",
+        value_type=choice,
+        required=False)
+
+
+class Collector(object):
+    implements(ICollector)
+
+    def __init__(self, numbers=None):
+        self.numbers = numbers or []
+
+
+class ItemsWidgetBaseTest(VerifyResults, PlacelessSetup, unittest.TestCase):
+
+    _widget = ItemsWidgetBase
+    _field = ICollector.get('choice')
+    _vocabulary = _field.vocabulary
+
+    def _makeWidget(self, form=None, nums=None):
+        request = TestRequest(form=form or {})
+        collector = Collector(nums)
+        bound = self._field.bind(collector)
+        return self._widget(bound, self._vocabulary, request)
+
+    def test_setPrefix(self):
+        widget = self._makeWidget()
+        name = self._field.getName()
+        # Default prefix
+        self.assertEqual(widget._prefix, 'field.')
+        self.assertEqual(widget.name, 'field.%s' %name)
+        self.assertEqual(widget.empty_marker_name,
+                         'field.%s-empty-marker' %name)
+        # Declaring custom prefix
+        widget.setPrefix('foo')
+        self.assertEqual(widget._prefix, 'foo.')
+        self.assertEqual(widget.name, 'foo.%s' %name)
+        self.assertEqual(widget.empty_marker_name,
+                         'foo.%s-empty-marker' %name)
+        # Declaring empty prefix
+        widget.setPrefix('')
+        self.assertEqual(widget._prefix, '')
+        self.assertEqual(widget.name, name)
+        self.assertEqual(widget.empty_marker_name,
+                         '%s-empty-marker' %name)
+
+    def test_convertTokensToValues(self):
+        widget = self._makeWidget()
+        self.assertEqual(widget.convertTokensToValues(['token1', 'token2']),
+                         ['one', 'two'])
+
+
+class ItemDisplayWidgetTest(ItemsWidgetBaseTest):
+
+    _widget = ItemDisplayWidget
+
+    def test_setVocabulary(self):
+        widget = self._makeWidget()
+        self.assert_(widget.vocabulary is not None)
+        self.assertEqual(widget.vocabulary, self._field.vocabulary)
+
+    def test__call__(self):
+        widget = self._makeWidget()
+        self.assertEqual(widget(), '')
+        widget = self._makeWidget(form={'field.choice': 'token1'})
+        self.assertEqual(widget(), 'One')
+
+    def test_not_required(self):
+        self.failIf(self._makeWidget().required)
+
+
+class ItemsMultiDisplayWidgetTest(ItemsWidgetBaseTest):
+
+    _widget = ItemsMultiDisplayWidget
+    _field = ICollector.get('numbers')
+    _vocabulary = _field.value_type.vocabulary
+    _tag = 'ol'
+
+    def test__call__(self):
+        widget = self._makeWidget()
+        self.assertEqual(widget(), '')
+        widget = self._makeWidget(form={'field.numbers': ['token1', 'token2']})
+        self.assertEqual(
+            widget(),
+            '<%s id="field.numbers" >'
+            '<li>One</li>\n<li>Two</li>'
+            '</%s>' %(self._tag, self._tag))
+
+    def test_renderItems(self):
+        widget = self._makeWidget()
+        self.assertEqual(
+            widget.renderItems(['one', 'two']),
+            [u'<li>One</li>', u'<li>Two</li>'])
+        self.assertRaises(LookupError, widget.renderItems, 'one')
+        self.assertRaises(TypeError, widget.renderItems, 1)
+
+
+    def test_not_required(self):
+        numbers = List(value_type=ICollector['choice']).bind(Collector(None))
+        request = TestRequest()
+        widget = self._widget(numbers, self._vocabulary, request)
+        self.failIf(widget.required)
+
+
+class ListDisplayWidgetTest(ItemsMultiDisplayWidgetTest):
+    _widget = ListDisplayWidget
+    _tag = 'ol'
+
+
+class SetDisplayWidgetTest(ItemsMultiDisplayWidgetTest):
+    _widget = SetDisplayWidget
+    _tag = 'ul'
+
+
+class ItemsEditWidgetBaseTest(ItemsWidgetBaseTest):
+
+    _widget = ItemsEditWidgetBase
+
+    def test_div(self):
+        widget = self._makeWidget()
+        self.assertEqual(widget._div('', ''), '')
+        self.assertEqual(widget._div('foo', ''), '')
+        self.assertEqual(widget._div('', 'bar'), '<div>\nbar\n</div>')
+        self.assertEqual(widget._div('foo', 'bar'),
+                         '<div class="foo">\nbar\n</div>')
+        self.assertEqual(widget._div('foo', 'bar', style='blah'),
+                         '<div class="foo" style="blah">\nbar\n</div>')
+
+    def test_renderItem(self):
+        widget = self._makeWidget()
+        self.assertEqual(widget.renderItem('', 'Foo', 'foo', '', None),
+                         '<option value="foo">Foo</option>')
+        self.assertEqual(widget.renderItem('', 'Foo', 'foo', '', 'klass'),
+                         '<option class="klass" value="foo">Foo</option>')
+
+    def test_renderSelectedItem(self):
+        widget = self._makeWidget()
+        self.verifyResult(
+          widget.renderSelectedItem('', 'Foo', 'foo', '', None),
+          ['<option', 'value="foo"', 'selected="selected"', '>Foo</option>'])
+        self.verifyResult(
+          widget.renderSelectedItem('', 'Foo', 'foo', '', 'klass'),
+          ['<option', 'class="klass"', 'value="foo"', 'selected="selected"',
+           '>Foo</option>'])
+
+    def test_renderItemsWithValues(self):
+        widget = self._makeWidget()
+        self.assertEqual(
+            widget.renderItemsWithValues(['one', 'two']),
+            [u'<option selected="selected" value="token1">One</option>',
+             u'<option selected="selected" value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+        self.assertEqual(
+            widget.renderItemsWithValues([]),
+            [u'<option value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+
+# This test is disabled because it tests for the presense of a missfeature,
+# which has been removed.  Did someone actually *want* this?
+##     def test_error(self):
+##         widget = self._makeWidget(form={'field.choice': 'ten'})
+##         widget.setPrefix('field.')
+##         widget._getFormValue()
+##         self.assert_(isinstance(widget._error, ConversionError))
+
+    def test_hidden(self):
+        widget = self._makeWidget(form={'field.choice': 'token2'})
+        widget.setPrefix('field.')
+        widget.context.required = False
+        self.verifyResult(
+            widget.hidden(),
+            ['<input', 'type="hidden"', 'value="token2"', 'id="field.choice"',
+             'name="field.choice"'])
+
+class SelectWidgetTest(ItemsEditWidgetBaseTest):
+
+    _widget = SelectWidget
+    _size = 5
+
+    def test__call__(self):
+        widget = self._makeWidget(form={'field.choice': 'token1'})
+        widget.setPrefix('field.')
+        widget.context.required = False
+        self.assertEqual(
+            widget(),
+            '<div>\n'
+            '<div class="value">\n'
+            '<select id="field.choice" name="field.choice" size="%i" >\n'
+            '<option value="">(no value)</option>\n'
+            '<option selected="selected" value="token1">One</option>\n'
+            '<option value="token2">Two</option>\n'
+            '<option value="token3">Three</option>\n'
+            '</select>\n</div>\n'
+            '<input name="field.choice-empty-marker" '
+            'type="hidden" value="1" />\n</div>' %self._size)
+
+    def test_renderValue(self):
+        widget = self._makeWidget()
+        widget.setPrefix('field.')
+        self.assertEqual(
+            widget.renderValue('one'),
+            '<select id="field.choice" name="field.choice" size="%i" >\n'
+            '<option selected="selected" value="token1">One</option>\n'
+            '<option value="token2">Two</option>\n'
+            '<option value="token3">Three</option>\n'
+            '</select>' %self._size)
+
+    def test_renderItems(self):
+        widget = self._makeWidget()
+        widget.setPrefix('field.')
+        self.assertEqual(
+            widget.renderItems('one'),
+            [u'<option selected="selected" value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+        self.assertEqual(
+            widget.renderItems('two'),
+            [u'<option value="token1">One</option>',
+             u'<option selected="selected" value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+        self.assertEqual(
+            widget.renderItems(None),
+            [u'<option value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+
+    def test_renderItems_notRequired(self):
+        widget = self._makeWidget()
+        widget.setPrefix('field.')
+        widget.context.required = False
+        self.assertEqual(
+            widget.renderItems([]),
+            [u'<option value="">(no value)</option>',
+             u'<option value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+
+    def test_renderItems_firstItem(self):
+        widget = self._makeWidget()
+        widget.setPrefix('field.')
+        widget.firstItem = True
+        self.assertEqual(
+            widget.renderItems(widget._toFormValue(widget.context.missing_value)),
+            [u'<option value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+
+
+class DropdownWidgetTest(SelectWidgetTest):
+
+    _widget = DropdownWidget
+    _size = 1
+
+    def test_renderItems_firstItem(self):
+        widget = self._makeWidget()
+        widget.setPrefix('field.')
+        widget.firstItem = True
+        self.assertEqual(
+            widget.renderItems(widget._toFormValue(widget.context.missing_value)),
+            [u'<option selected="selected" value="">(no value)</option>',
+             u'<option value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+        try:
+            # test BBB starting with 3.6.0
+            zope.formlib.itemswidgets.EXPLICIT_EMPTY_SELECTION = False
+            self.assertEqual(
+                widget.renderItems(widget._toFormValue(widget.context.missing_value)),
+                [u'<option value="token1">One</option>',
+                 u'<option value="token2">Two</option>',
+                 u'<option value="token3">Three</option>'])
+        finally:
+            zope.formlib.itemswidgets.EXPLICIT_EMPTY_SELECTION = True
+
+
+class RadioWidgetTest(ItemsEditWidgetBaseTest):
+
+    _widget = RadioWidget
+
+    def test_renderItem(self):
+        widget = self._makeWidget()
+        self.verifyResult(
+            widget.renderItem('', 'Foo', 'foo', 'bar', None),
+            ['<label', '<input', 'type="radio"', 'name="bar"', 'value="foo"',
+             'class="radioType"', '>&nbsp;Foo'])
+        self.verifyResult(
+            widget.renderItem('bar', 'Foo', 'foo', 'bar', 'klass'),
+            ['<input', 'type="radio"', 'name="bar"', 'value="foo"',
+             'class="klass radioType"', '>&nbsp;Foo'])
+
+    def test_renderSelectedItem(self):
+        widget = self._makeWidget()
+        self.verifyResult(
+            widget.renderSelectedItem('', 'Foo', 'foo', 'bar', 'klass'),
+            ['<label', '<input', 'type="radio"', 'name="bar"', 'value="foo"',
+             'checked="checked"', '>&nbsp;Foo'])
+        self.verifyResult(
+            widget.renderSelectedItem('', 'Foo', 'foo', 'bar', 'klass'),
+            ['<label', '<input', 'type="radio"', 'name="bar"', 'value="foo"',
+             'class="klass radioType"', 'checked="checked"', '>&nbsp;Foo'])
+
+    def test_renderItemsWithValues(self):
+        widget = self._makeWidget()
+        items = widget.renderItemsWithValues(['one'])
+        values = [('token1','One'), ('token2','Two'), ('token3','Three')]
+        for index, item in enumerate(items):
+            self.verifyResult(
+                item,
+                ['<label', '<input', 'class="radioType"', 'name="field.choice"',
+                 'id="field.choice.%i' %index, 'type="radio"',
+                 'value="%s"' %values[index][0],
+                 '&nbsp;%s' %values[index][1]])
+        self.verifyResult(items[0], ['checked="checked"'])
+
+    def test_renderItems(self):
+        widget = self._makeWidget()
+        items = widget.renderItems('one')
+        values = [('token1','One'), ('token2','Two'), ('token3','Three')]
+        for index, item in enumerate(items):
+            self.verifyResult(
+                item,
+                ['<label', '<input', 'class="radioType"', 'name="field.choice"',
+                 'id="field.choice.%i' %index, 'type="radio"',
+                 'value="%s"' %values[index][0], '&nbsp;%s' %values[index][1]])
+        self.verifyResult(items[0], ['checked="checked"'])
+
+    def test_renderItems_notRequired(self):
+        widget = self._makeWidget()
+        widget.context.required = False
+        items = widget.renderItems([])
+        values = [('', '(no value)'),
+                  ('token1','One'),
+                  ('token2','Two'),
+                  ('token3','Three')]
+        for index, item in enumerate(items):
+            self.verifyResult(
+                item,
+                ['<label', '<input', 'class="radioType"',
+                 'name="field.choice"', 'type="radio"',
+                 'value="%s"' %values[index][0], '&nbsp;%s' %values[index][1]])
+
+    def test_renderItems_firstItem(self):
+        widget = self._makeWidget()
+        items = widget.renderItems(
+                widget._toFormValue(widget.context.missing_value))
+        values = [('token1','One'), ('token2','Two'), ('token3','Three')]
+        for index, item in enumerate(items):
+            self.verifyResult(
+                item,
+                ['<label', '<input', 'class="radioType"',
+                 'name="field.choice"', 'id="field.choice.%i"' % index,
+                 'type="radio"', 'value="%s"' % values[index][0],
+                 '&nbsp;%s' % values[index][1]])
+
+    def test_renderValue(self):
+        widget = self._makeWidget()
+        self.verifyResult(widget.renderValue(None), ['<br /><label for='])
+        widget.orientation = 'horizontal'
+        self.verifyResult(widget.renderValue(None),
+                          ['&nbsp;&nbsp;<label for='])
+
+
+class ItemsMultiEditWidgetBaseTest(ItemsEditWidgetBaseTest):
+
+    _widget = ItemsMultiEditWidgetBase
+    _field = ICollector.get('numbers')
+    _vocabulary = _field.value_type.vocabulary
+
+    def test_renderValue(self):
+        widget = self._makeWidget()
+        self.verifyResult(
+            widget.renderValue(['one', 'two']),
+            ['<select', 'multiple="multiple"', 'name="field.numbers:list"',
+             'size="5"', '><option', 'selected="selected"', 'value="token1"',
+             '>One</option>\n', 'value="token2"', '>Two</option>\n',
+             'value="token3"', '>Three</option>', '</select>'])
+
+    def test_renderItemsWithValues(self):
+        widget = self._makeWidget()
+        self.assertEqual(
+            widget.renderItemsWithValues(['one', 'two']),
+            [u'<option selected="selected" value="token1">One</option>',
+             u'<option selected="selected" value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+        self.assertEqual(
+            widget.renderItemsWithValues([]),
+            [u'<option value="token1">One</option>',
+             u'<option value="token2">Two</option>',
+             u'<option value="token3">Three</option>'])
+
+# This test is disabled because it tests for the presense of a missfeature,
+# which has been removed.  Did someone actually *want* this?
+##     def test_error(self):
+##         widget = self._makeWidget(form={'field.numbers': ['ten']})
+##         widget.setPrefix('field.')
+##         widget._getFormValue()
+##         self.assert_(isinstance(widget._error, ConversionError))
+
+    def test_hidden(self):
+        widget = self._makeWidget(
+            form={'field.numbers': ['two','three']})
+        widget.setPrefix('field.')
+        widget.context.required = False
+        self.verifyResult(
+            widget.hidden(),
+            ['<input', 'type="hidden"', 'value="token2"', 'id="field.numbers"',
+             'name="field.numbers:list"', 'value="token3"'])
+
+    def test_getInputValue(self):
+        widget = self._makeWidget(form={'field.numbers': ['token2', 'token3']})
+        widget.setPrefix('field.')
+        self.assertEqual(widget.getInputValue(), ['two', 'three'])
+
+        self._field = ICollector.get('letters')
+        widget = self._makeWidget(form={'field.letters-empty-marker': '1'})
+        widget.setPrefix('field.')
+        self.assertEqual(widget.getInputValue(), set([]))
+        widget = self._makeWidget(form={'field.letters': ['token2', 'token3']})
+        widget.setPrefix('field.')
+        self.assertEqual(widget.getInputValue(), set(['two', 'three']))
+
+        self._field = ICollector.get('frozenLetters')
+        widget = self._makeWidget(form={'field.frozenLetters-empty-marker':
+                                        '1'})
+        widget.setPrefix('field.')
+        field_value = widget.getInputValue()
+        self.assertEqual(field_value, frozenset())
+        widget = self._makeWidget(form={'field.frozenLetters':
+                                        ['token2', 'token3']})
+        widget.setPrefix('field.')
+        field_value = widget.getInputValue()
+        self.assertEqual(field_value, frozenset(['two', 'three']))
+        self.assert_(isinstance(field_value, frozenset))
+
+
+class MultiSelectWidgetTest(ItemsMultiEditWidgetBaseTest):
+
+    _widget = MultiSelectWidget
+
+
+class OrderedMultiSelectWidgetTest(ItemsMultiEditWidgetBaseTest):
+
+    _widget = OrderedMultiSelectWidget
+
+    def test_choices(self):
+        widget = self._makeWidget()
+        choices = [choice['text'] for choice in widget.choices()]
+        choices.sort()
+        self.assertEqual(choices, ['One', 'Three', 'Two'])
+
+    def test_selected(self):
+        widget = self._makeWidget(nums=['one'])
+        widget._data = ['two']
+        selected = [select['text'] for select in widget.selected()]
+        selected.sort()
+        self.assertEqual(selected, ['One', 'Two'])
+        widget._data = ['one']
+        selected = [select['text'] for select in widget.selected()]
+        selected.sort()
+        self.assertEqual(selected, ['One'])
+
+
+class MultiCheckBoxWidgetTest(ItemsMultiEditWidgetBaseTest):
+
+    _widget = MultiCheckBoxWidget
+
+    def test_renderItem(self):
+        widget = self._makeWidget()
+        self.verifyResult(
+            widget.renderItem('', 'Foo', 'foo', 'bar', None),
+            ['<input', 'type="checkbox"', 'name="bar"', 'value="foo"',
+             'class="checkboxType"', '>&nbsp;Foo'])
+        self.verifyResult(
+            widget.renderItem('bar', 'Foo', 'foo', 'bar', 'klass'),
+            ['<input', 'type="checkbox"', 'name="bar"', 'value="foo"',
+             'class="klass checkboxType"', '>&nbsp;Foo'])
+
+    def test_renderSelectedItem(self):
+        widget = self._makeWidget()
+        self.verifyResult(
+            widget.renderSelectedItem('', 'Foo', 'foo', 'bar', 'klass'),
+            ['<input', 'type="checkbox"', 'name="bar"', 'value="foo"',
+             'checked="checked"', '>&nbsp;Foo'])
+        self.verifyResult(
+            widget.renderSelectedItem('', 'Foo', 'foo', 'bar', 'klass'),
+            ['<input', 'type="checkbox"', 'name="bar"', 'value="foo"',
+             'class="klass checkboxType"', 'checked="checked"', '>&nbsp;Foo'])
+
+    def test_renderValue(self):
+        widget = self._makeWidget()
+        self.verifyResult(widget.renderValue(None), ['<br /><label for='])
+        widget.orientation='horizontal'
+        self.verifyResult(widget.renderValue(None),
+                          ['&nbsp;&nbsp;<label for='])
+
+    def test_renderItemsWithValues(self):
+        widget = self._makeWidget()
+        items = widget.renderItemsWithValues(['one'])
+        values = [('token1','One'), ('token2','Two'), ('token3','Three')]
+        for index, item in enumerate(items):
+            self.verifyResult(
+                item,
+                ['<input', 'class="checkboxType',
+                 'id="field.numbers.%i"' %index, 'type="checkbox"',
+                 'value="%s"' % values[index][0],
+                 '/>&nbsp;%s' % values[index][1]])
+
+        self.verifyResult(items[0], ['checked="checked"'])
+
+
+def test_suite():
+    suite = unittest.makeSuite(ItemDisplayWidgetTest)
+    suite.addTest(unittest.makeSuite(ItemsMultiDisplayWidgetTest))
+    suite.addTest(unittest.makeSuite(ListDisplayWidgetTest))
+    suite.addTest(unittest.makeSuite(SetDisplayWidgetTest))
+    suite.addTest(unittest.makeSuite(ItemsEditWidgetBaseTest))
+    suite.addTest(unittest.makeSuite(SelectWidgetTest))
+    suite.addTest(unittest.makeSuite(DropdownWidgetTest))
+    suite.addTest(unittest.makeSuite(RadioWidgetTest))
+    suite.addTest(unittest.makeSuite(ItemsMultiEditWidgetBaseTest))
+    suite.addTest(unittest.makeSuite(MultiSelectWidgetTest))
+    suite.addTest(unittest.makeSuite(OrderedMultiSelectWidgetTest))
+    suite.addTest(unittest.makeSuite(MultiCheckBoxWidgetTest))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest="test_suite")

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_multicheckboxwidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_multicheckboxwidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_multicheckboxwidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_multicheckboxwidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Multi-Checkbox Widget Tests 
+
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+from zope.interface import Interface, implements
+from zope.publisher.browser import TestRequest
+from zope.schema import Choice, List
+
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import MultiCheckBoxWidget
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+from zope.interface.verify import verifyClass
+
+class MultiCheckBoxWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the multi checkbox widget.
+        
+        >>> verifyClass(IInputWidget, MultiCheckBoxWidget)
+        True
+    """
+
+    _WidgetFactory = MultiCheckBoxWidget
+    _FieldFactory = List
+
+    def setUpContent(self, desc=u'', title=u'Foo Title'):
+        class ITestContent(Interface):
+            foo = self._FieldFactory(
+                    title=title,
+                    description=desc,
+                    value_type=Choice(values=[u'foo', u'bar'])
+                    )
+        class TestObject(object):
+            implements(ITestContent)
+
+        self.content = TestObject()
+        field = ITestContent['foo']
+        field = field.bind(self.content)
+        request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl',
+                              form={'field.foo': u'bar'})
+        self._widget = self._WidgetFactory(field, field.value_type.vocabulary,
+                                           request)
+
+    def testProperties(self):
+        self.assertEqual(self._widget.cssClass, "")
+        self.assertEqual(self._widget.extra, '')
+        self.assertEqual(self._widget.orientation, 'vertical')
+
+
+    def testRenderItem(self):
+        check_list = ('type="checkbox"', 'id="field.bar.',
+                      'name="field.bar"', 'value="foo"', 'Foo')
+        self.verifyResult(
+            self._widget.renderItem(0, 'Foo', 'foo', 'field.bar', None),
+            check_list)
+        check_list += ('checked="checked"',)
+        self.verifyResult(
+            self._widget.renderSelectedItem(
+                0, 'Foo', 'foo', 'field.bar', None),
+            check_list)
+
+
+    def testRenderItems(self):
+        check_list = ('type="checkbox"', 'id="field.foo.',
+                      'name="field.foo"', 'value="bar"', 'bar',
+                      'value="foo"', 'foo', 'checked="checked"')
+        self.verifyResult('\n'.join(self._widget.renderItems(['bar'])),
+                          check_list)
+
+
+    def testRender(self):
+        check_list = ('type="checkbox"', 'id="field.foo.',
+                      'name="field.foo"', 'value="bar"', 'bar',
+                      'value="foo"', 'foo', 'checked="checked"')
+        self.verifyResult(self._widget(), check_list)
+
+        check_list = ('type="hidden"', 'id="field.foo', 'name="field.foo:list"',
+                      'value="bar"')
+        self.verifyResult(self._widget.hidden(), check_list)
+        check_list = ('style="color: red"',) + check_list
+        self._widget.extra = 'style="color: red"'
+        self.verifyResult(self._widget.hidden(), check_list)
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(MultiCheckBoxWidgetTest),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_objectwidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_objectwidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_objectwidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_objectwidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,148 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Object Widget tests
+
+$Id$
+"""
+import unittest
+import sys
+from zope.component import testing
+from zope.interface import Interface, implements
+from zope.publisher.browser import TestRequest
+from zope.schema import Object, TextLine
+from zope.schema.interfaces import ITextLine
+from zope.testing import doctest
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.component import provideAdapter
+
+from zope.formlib.interfaces import IInputWidget, MissingInputError
+from zope.formlib.widgets import TextWidget, ObjectWidget
+from zope.formlib.tests.test_browserwidget import BrowserWidgetTest
+from zope.formlib.interfaces import IWidgetInputErrorView
+
+class ITestContact(Interface):
+    name = TextLine()
+    email = TextLine()
+    
+class TestContact(object):
+    implements(ITestContact)
+
+class ObjectWidgetInputErrorView(object):
+    implements(IWidgetInputErrorView)
+
+    def __init__(self, error, request):
+        self.error = error
+        self.request = request
+
+    def snippet(self):
+        return repr(self.error)
+
+class ObjectWidgetTest(BrowserWidgetTest):
+    """Documents and tests the object widget.
+
+        >>> from zope.interface.verify import verifyClass
+        >>> verifyClass(IInputWidget, ObjectWidget)
+        True
+    """
+
+    _FieldFactory = Object
+    def _WidgetFactory(self, context, request, **kw):
+        kw.update({'factory': TestContact})
+        return ObjectWidget(context, request, **kw)
+
+    def setUpContent(self, desc=u'', title=u'Foo Title'):
+        provideAdapter(TextWidget, (ITextLine, IDefaultBrowserLayer),
+                       IInputWidget)
+        
+        class ITestContent(Interface):
+            foo = self._FieldFactory(
+                    ITestContact, 
+                    title=title,
+                    description=desc
+                    )
+        class TestObject(object):
+            implements(ITestContent)
+
+        self.content = TestObject()
+        self.field = ITestContent['foo']
+        self.request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
+        self.request.form['field.foo'] = u'Foo Value'
+        self._widget = self._WidgetFactory(self.field, self.request)
+
+    def setUp(self):
+        super(ObjectWidgetTest, self).setUp()
+        self.field = Object(ITestContact, __name__=u'foo')
+        provideAdapter(TextWidget,
+                       (ITextLine, IDefaultBrowserLayer),
+                       IInputWidget)
+
+    def test_applyChanges(self):
+        self.request.form['field.foo.name'] = u'Foo Name'
+        self.request.form['field.foo.email'] = u'foo at foo.test'
+        widget = self._WidgetFactory(self.field, self.request)
+
+        self.assertEqual(widget.applyChanges(self.content), True)
+        self.assertEqual(hasattr(self.content, 'foo'), True)
+        self.assertEqual(isinstance(self.content.foo, TestContact), True)
+        self.assertEqual(self.content.foo.name, u'Foo Name')
+        self.assertEqual(self.content.foo.email, u'foo at foo.test')
+
+    def test_error(self):
+        provideAdapter(
+            ObjectWidgetInputErrorView,
+            (MissingInputError, TestRequest),
+            IWidgetInputErrorView)
+
+        widget = self._WidgetFactory(self.field, self.request)
+        self.assertRaises(MissingInputError, widget.getInputValue)
+        error_html = widget.error()
+        if sys.version_info < (2, 5):
+            self.failUnless("email: <zope.formlib.interfaces.Mis" 
+                             in error_html)
+            self.failUnless("name: <zope.formlib.interfaces.Miss"
+                             in error_html)
+        else:
+            self.failUnless("email: MissingInputError(u'field.foo.email', u'', None)"
+                             in error_html)
+            self.failUnless("name: MissingInputError(u'field.foo.name', u'', None)"
+                             in error_html)
+
+    def test_applyChangesNoChange(self):
+        self.content.foo = TestContact()
+        self.content.foo.name = u'Foo Name'
+        self.content.foo.email = u'foo at foo.test'
+
+        self.request.form['field.foo.name'] = u'Foo Name'
+        self.request.form['field.foo.email'] = u'foo at foo.test'
+        widget = self._WidgetFactory(self.field, self.request)
+        widget.setRenderedValue(self.content.foo)
+
+        self.assertEqual(widget.applyChanges(self.content), False)
+        self.assertEqual(hasattr(self.content, 'foo'), True)
+        self.assertEqual(isinstance(self.content.foo, TestContact), True)
+        self.assertEqual(self.content.foo.name, u'Foo Name')
+        self.assertEqual(self.content.foo.email, u'foo at foo.test')
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(ObjectWidgetTest),
+        doctest.DocFileSuite('../objectwidget.txt',
+                             setUp=testing.setUp,
+                             tearDown=testing.tearDown),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_passwordwidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_passwordwidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_passwordwidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_passwordwidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,60 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Password Widget Tests
+
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import PasswordWidget
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+from zope.interface.verify import verifyClass
+
+class PasswordWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the password widget.
+
+        >>> verifyClass(IInputWidget, PasswordWidget)
+        True
+    """
+
+    _WidgetFactory = PasswordWidget
+
+    def testProperties(self):
+        self.assertEqual(self._widget.tag, 'input')
+        self.assertEqual(self._widget.type, 'password')
+        self.assertEqual(self._widget.cssClass, '')
+        self.assertEqual(self._widget.extra, '')
+        self.assertEqual(self._widget.default, '')
+        self.assertEqual(self._widget.displayWidth, 20)
+        self.assertEqual(self._widget.displayMaxWidth, '')
+
+    def testRender(self):
+        value = 'Foo Value'
+        self._widget.setRenderedValue(value)
+        check_list = ('type="password"', 'id="field.foo"',
+                      'name="field.foo"', 'value=""', 'size="20"')
+        self.verifyResult(self._widget(), check_list)
+
+    def testHidden(self):
+        self.assertRaises(NotImplementedError, self._widget.hidden)
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(PasswordWidgetTest),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_radiowidget.py (from rev 107392, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_radiowidget.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_radiowidget.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_radiowidget.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,125 @@
+##############################################################################
+#
+# 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 Tests
+
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+from zope.interface import Interface, implements
+from zope.interface.verify import verifyClass
+from zope.publisher.browser import TestRequest
+from zope.schema import Choice
+
+from zope.formlib.interfaces import IInputWidget
+from zope.formlib.widgets import RadioWidget
+from zope.formlib.tests.test_browserwidget import SimpleInputWidgetTest
+
+class RadioWidgetTest(SimpleInputWidgetTest):
+    """Documents and tests the radio widget.
+
+        >>> verifyClass(IInputWidget, RadioWidget)
+        True
+    """
+
+    _FieldFactory = Choice
+    _WidgetFactory = RadioWidget
+
+    def setUpContent(self, desc=u'', title=u'Foo Title'):
+        class ITestContent(Interface):
+            foo = self._FieldFactory(
+                    title=title,
+                    description=desc,
+                    values=(u'foo', u'bar')
+                    )
+        class TestObject(object):
+            implements(ITestContent)
+
+        self.content = TestObject()
+        field = ITestContent['foo']
+        field = field.bind(self.content)
+        request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
+        request.form['field.foo'] = u'Foo Value'
+        self._widget = self._WidgetFactory(field, field.vocabulary, request)
+
+    def testProperties(self):
+        self.assertEqual(self._widget.cssClass, "")
+        self.assertEqual(self._widget.extra, '')
+        self.assertEqual(self._widget.orientation, 'vertical')
+
+
+    def testRenderItem(self):
+        check_list = ('type="radio"', 'id="field.bar.0"',
+                      'name="field.bar"', 'value="foo"', 'Foo')
+        self.verifyResult(
+            self._widget.renderItem(0, 'Foo', 'foo', 'field.bar', None),
+            check_list)
+        check_list += ('checked="checked"',)
+        self.verifyResult(
+            self._widget.renderSelectedItem(
+                0, 'Foo', 'foo', 'field.bar', None),
+            check_list)
+
+
+    def testRenderItems(self):
+        check_list = ('type="radio"', 'id="field.foo.0"', 'name="field.foo"',
+                      'value="bar"', 'bar', 'value="foo"', 'foo',
+                      'checked="checked"')
+        self.verifyResult('\n'.join(self._widget.renderItems('bar')),
+                          check_list)
+
+
+    def testRender(self):
+        value = 'bar'
+        self._widget.setRenderedValue(value)
+        check_list = ('type="radio"', 'id="field.foo.0"',
+                      'name="field.foo"', 'value="bar"', 'bar',
+                      'value="foo"', 'foo', 'checked="checked"')
+        self.verifyResult(self._widget(), check_list)
+
+        check_list = ('type="hidden"', 'id="field.foo"',
+                      'name="field.foo"', 'value="bar"')
+        self.verifyResult(self._widget.hidden(), check_list)
+        check_list = ('style="color: red"',) + check_list
+        self._widget.extra = 'style="color: red"'
+        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),
+        doctest.DocTestSuite(),
+        ))
+
+if __name__=='__main__':
+    unittest.main(defaultTest='test_suite')

Copied: zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_source.py (from rev 107362, zope.app.form/branches/faassen-zaf/src/zope/app/form/browser/tests/test_source.py)
===================================================================
--- zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_source.py	                        (rev 0)
+++ zope.formlib/branches/faassen-zaf/src/zope/formlib/tests/test_source.py	2009-12-30 22:26:15 UTC (rev 107397)
@@ -0,0 +1,29 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Source Widget Tests
+
+$Id$
+"""
+from zope.component import testing
+
+def test_suite():
+    from zope.testing import doctest
+    return doctest.DocFileSuite(
+        '../source.txt',
+        setUp=testing.setUp, tearDown=testing.tearDown)
+
+if __name__ == '__main__':
+    import unittest
+    unittest.main(defaultTest='test_suite')
+



More information about the Zope3-Checkins mailing list