##
## test_dictwidget.py
## Login : <uli@pu.smp.net>
## Started on  Sun Jul 18 15:47:58 2010 Uli Fouquet
## $Id$
## 
## Copyright (C) 2010 Uli Fouquet
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
## 
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## 
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
"""Dict Field Widget tests.
"""
import unittest
from zope.interface import Interface, implements
from zope.schema import Tuple, List, TextLine, Dict
from zope.schema.interfaces import ITextLine
from zope.component import provideAdapter
from zope.publisher.browser import TestRequest
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
import doctest

from zope.formlib.widgets import TextWidget, ObjectWidget, DisplayWidget
from zope.formlib.widgets import TupleSequenceWidget, ListSequenceWidget
from zope.formlib.widgets import SequenceDisplayWidget
from zope.formlib.widgets import SequenceWidget
from zope.formlib.interfaces import IDisplayWidget
from zope.formlib.interfaces import IInputWidget, MissingInputError
from zope.formlib.interfaces import IWidgetInputError, WidgetInputError
from zope.formlib.interfaces import IWidgetInputErrorView
from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.exception import WidgetInputErrorView

from zope.formlib.tests.support import VerifyResults
from zope.formlib.tests.test_browserwidget import BrowserWidgetTest

from zope.traversing.testing import setUp as traversingSetUp
from zope.testing.cleanup import tearDown as traversingTearDown

from waeup.sirp.jambtables.dictwidget import DictWidget, DictDisplayWidget

class DictWidgetTestHelper(object):

    def setUpContent(self, desc=u'', title=u'Foo Title'):
        class ITestContent(Interface):
            foo = self._FieldFactory(
                    title=title,
                    description=desc,
                    )
        class TestObject(object):
            implements(ITestContent)

        self.content = TestObject()
        self.field = ITestContent['foo'].bind(self.content)
        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):
        traversingSetUp()
        self.setUpContent()

    def tearDown(self):
        traversingTearDown()
        
    def _FieldFactory(self, **kw):
        kw.update({
            '__name__': u'foo', 
            'key_type': TextLine(__name__=u'bar',title=u'bar'),
            'value_type': TextLine(__name__=u'baz',title=u'baz')})
        return Dict(**kw)


class DictWidgetTest(DictWidgetTestHelper, BrowserWidgetTest):
    """Documents and tests the dict widget.

        >>> from zope.interface.verify import verifyClass
        >>> verifyClass(IInputWidget, DictWidget)
        True
    """

    _WidgetFactory = DictWidget

    def testRender(self):
        pass

    def setUp(self):
        super(DictWidgetTest, self).setUp()
        provideAdapter(TextWidget,
                       (ITextLine, IDefaultBrowserLayer),
                       IInputWidget, '')
        provideAdapter(WidgetInputErrorView,
                       (IWidgetInputError, IDefaultBrowserLayer),
                       IWidgetInputErrorView, '')

    def test_haveNoData(self):
        self.failIf(self._widget.hasInput())

    def test_hasInput(self):
        self._widget.request.form['field.foo.count'] = u'0'
        self.failUnless(self._widget.hasInput())

    def test_customWidgetFactory(self):
        """Verify that the widget can be constructed via the CustomWidgetFactory
        (Issue #293)
        """
        key_type = TextLine(__name__=u'bar')
        value_type = TextLine(__name__=u'baz')
        self.field = Dict( __name__=u'foo',
                           value_type=value_type,
                           key_type=key_type)
        request = TestRequest()

        # set up the custom widget factory and verify that it works
        sw = CustomWidgetFactory(DictWidget)
        widget = sw(self.field, request)
        assert widget.subwidget is None
        assert widget.context.value_type is value_type

        # set up a variant that specifies the subwidget to use and verify it
        class PollOption(object) : pass
        ow = CustomWidgetFactory(ObjectWidget, PollOption)
        sw = CustomWidgetFactory(DictWidget, subwidget=ow)
        widget = sw(self.field, request)
        assert widget.subwidget is ow
        assert widget.context.value_type is value_type
        assert widget.context.key_type is key_type

    def test_subwidget(self):
        """This test verifies that the specified subwidget is not ignored.
        (Issue #293)
        """
        self.field = Dict(__name__=u'foo',
                          key_type=TextLine(__name__=u'bar'),
                          value_type=TextLine(__name__=u'baz'),
                          )
        request = TestRequest()

        class PollOption(object) : pass
        ow = CustomWidgetFactory(ObjectWidget, PollOption)
        widget = DictWidget(
            self.field, request, subwidget=ow)
        assert widget.subwidget is ow

    def test_dict(self):
        self.field = Dict(
            __name__=u'foo',
            key_type=TextLine(__name__=u'bar'),
            value_type=TextLine(__name__=u'baz'),
            )
        request = TestRequest()
        widget = DictWidget(
            self.field, request)
        self.failIf(widget.hasInput())
        self.assertRaises(MissingInputError, widget.getInputValue)

        request = TestRequest(form={'field.foo.add': u'Add bar',
                                    'field.foo.count': u'0'})
        widget = DictWidget(
            self.field, request)
        self.assert_(widget.hasInput())
        self.assertRaises(WidgetInputError, widget.getInputValue)

        request = TestRequest(form={'field.foo.key.0.bar': u'Hello',
                                    'field.foo.value.0.baz': u'world!',
                                    'field.foo.count': u'1'})
        widget = DictWidget(
            self.field, request)
        self.assert_(widget.hasInput())
        self.assertEquals(widget.getInputValue(), {u'Hello': u'world!'})

    def test_new(self):
        request = TestRequest()
        widget = TupleSequenceWidget(
            self.field, self.field.value_type, request)
        self.failIf(widget.hasInput())
        self.assertRaises(MissingInputError, widget.getInputValue)
        check_list = ('input', 'name="field.foo.add"')
        self.verifyResult(widget(), check_list)

    def test_add(self):
        request = TestRequest(form={'field.foo.add': u'Add bar',
                                    'field.foo.count': u'0'})
        widget = DictWidget(
            self.field, request)
        self.assert_(widget.hasInput())
        self.assertRaises(WidgetInputError, widget.getInputValue)
        check_list = (
            'checkbox', 'field.foo.remove_0', 'input', 'field.foo.key.0.bar',
            'submit', 'submit', 'field.foo.add'
        )
        self.verifyResult(widget(), check_list, inorder=True)

    def test_request(self):
        request = TestRequest(form={'field.foo.key.0.bar': u'Hello',
                                    'field.foo.value.0.baz': u'world!',
                                    'field.foo.count': u'1'})
        #widget = DictWidget(
        #    self.field, self.field.value_type, request)
        widget = DictWidget(
            self.field, request)
        self.assert_(widget.hasInput())
        self.assertEquals(widget.getInputValue(), {u'Hello': 'world!'})

    def test_existing(self):
        request = TestRequest()
        widget = DictWidget(
            self.field, request)
        widget.setRenderedValue({'existing':'value'})
        self.failIf(widget.hasInput())
        self.assertRaises(MissingInputError, widget.getInputValue)
        check_list = (
            'checkbox', 'field.foo.remove_0', 'input', 'field.foo.key.0.bar',
                'existing',
            'submit', 'submit', 'field.foo.add',
            'field.foo.count" value="1"',
        )
        self.verifyResult(widget(), check_list, inorder=True)
        widget.setRenderedValue({u'existing':u'entry',
                                 u'second':u'value'})
        self.failIf(widget.hasInput())
        self.assertRaises(MissingInputError, widget.getInputValue)
        check_list = (
            'checkbox', 'field.foo.remove_0', 'input', 'field.foo.key.0.bar',
                'second',
            'checkbox', 'field.foo.remove_1', 'input', 'field.foo.key.1.bar',
                'existing',
            'submit', 'submit', 'field.foo.add',
            'field.foo.count" value="2"',
        )
        self.verifyResult(widget(), check_list, inorder=True)

    def test_remove(self):
        request = TestRequest(form={
            'field.foo.remove_0': u'1',
            'field.foo.0.bar': u'existing',
            'field.foo.1.bar': u'second',
            'field.foo.remove': u'Remove selected items',
            'field.foo.count': u'2'})
        request = TestRequest(form={
            'field.foo.remove_0': u'1',
            'field.foo.key.0.bar': u'first',
            'field.foo.value.0.baz': u'entry',
            'field.foo.key.1.bar': u'second',
            'field.foo.value.1.baz': u'entry',
            'field.foo.remove': u'Remove selected items',
            'field.foo.count': u'2'})
        #widget = TupleSequenceWidget(
        #    self.field, self.field.value_type, request)
        widget = DictWidget(
            self.field, request)
        #widget.setRenderedValue(((u'existing', u'one'), (u'second', u'entry')))
        #widget.setRenderedValue({u'existing': u'one', u'other': u'entry'})
        widget.setRenderedValue({u'other': u'entry', u'existing': u'one'})
        #a = widget.getInputValue()
        #print "AAAAAAAAAAAAAAAA: ", a
        self.assertEquals(widget.getInputValue(), {u'second': u'entry'})
        #self.assertEquals(a, {u'second': u'entry'})
        output = widget()
        #print "WIDGET_OUTPUT: ", output
        check_list = (
            'checkbox', 'field.foo.remove_0',
            'input', 'field.foo.key.0.bar', 'other',
            'input', 'field.foo.value.0.baz', 'entry',
            'checkbox', 'field.foo.remove_1',
            'input', 'field.foo.key.1.bar', 'existing',
            'input', 'field.foo.value.1.baz', 'one',
            'submit', 'submit', 'field.foo.add',
            'field.foo.count" value="2"',
        )
        #self.verifyResult(widget(), check_list, inorder=True)
        self.verifyResult(output, check_list, inorder=True)

    def test_min(self):
        request = TestRequest()
        self.field.min_length = 2
        #widget = TupleSequenceWidget(
        #    self.field, self.field.value_type, request)
        widget = DictWidget(
            self.field, request)
        widget.setRenderedValue({u'existing': u'value', None:None})
        self.assertRaises(MissingInputError, widget.getInputValue)
        check_list = (
            'input', 'field.foo.key.0.bar', 'value=""',
            'input', 'field.foo.key.1.bar', 'existing',
            'submit', 'field.foo.add'
        )
        s = widget()
        #print "S: "
        #print s
        self.verifyResult(s, check_list, inorder=True)
        self.assertEquals(s.find('checkbox'), -1)

    def test_max(self):
        request = TestRequest()
        self.field.max_length = 1
        widget = DictWidget(
            self.field, request)
        widget.setRenderedValue({u'existing':u'value'})
        self.assertRaises(MissingInputError, widget.getInputValue)
        s = widget()
        self.assertEquals(s.find('field.foo.add'), -1)

    def test_anonymousfield(self):
        self.field = Dict(__name__=u'foo',
                          value_type=TextLine(),
                          key_type=TextLine())
        request = TestRequest()
        widget = DictWidget(
            self.field, request)
        widget.setRenderedValue({u'existing':u'value'})
        s = widget()
        check_list = (
            'input', '"field.foo.key.0."', 'existing',
            'submit', 'submit', 'field.foo.add'
        )
        s = widget()
        self.verifyResult(s, check_list, inorder=True)

    def test_usererror(self):
        self.field = Dict(__name__=u'foo',
                          key_type=TextLine(__name__='bar'),
                          value_type=TextLine(__name__='baz'),
                          )
        request = TestRequest(form={
            'field.foo.key.0.bar': u'',
            'field.foo.value.0.baz': u'value',
            'field.foo.key.1.bar': u'nonempty',
            'field.foo.value.1.baz': u'entry',
            'field.foo.count': u'2'})
        widget = DictWidget(
            self.field, request)
        # Rendering a widget should not raise errors!
        widget()

        data = widget._generateSequence()
        self.assertEquals(data, [(None, u'value'),
                                 (u'nonempty', u'entry')])

    def doctest_widgeterrors(self):
        """Test that errors on subwidgets appear

            >>> #field = Tuple(__name__=u'foo',
            >>> #              value_type=TextLine(__name__='bar'))
            >>> field = Dict(__name__=u'foo',
            ...              key_type=TextLine(__name__='bar',title=u'bar'),
            ...              value_type=TextLine(__name__='baz',title=u'baz'))
            >>> request = TestRequest(form={
            ...     'field.foo.key.0.bar': u'',
            ...     'field.foo.value.0.baz': u'some',
            ...     'field.foo.key.1.bar': u'nonempty',
            ...     'field.foo.value.1.baz': u'entry',
            ...     'field.foo.count': u'2'})
            >>> #widget = TupleSequenceWidget(field, field.value_type, request)
            >>> widget = DictWidget(field, request)

         If we render the widget, we see no errors:

            >>> print widget()
            <BLANKLINE>
            ...
            <tr>
              <td>
                 <input class="editcheck" type="checkbox"
                        name="field.foo.remove_0" />
              </td>
              <td>
                 <input class="textType" id="field.foo.key.0.bar"
                        name="field.foo.key.0.bar"
                        size="20" type="text" value=""  />
              </td>
              ...
            </tr>
            ...

         However, if we call getInputValue or hasValidInput, the
         errors on the widgets are preserved and displayed:

            >>> widget.hasValidInput()
            False

            >>> print widget()
            <BLANKLINE>
            ...
            <tr>
              <td>
                 <input class="editcheck" type="checkbox"
                        name="field.foo.remove_0" />
              </td>
              <td>
                 <span class="error">Required input is missing.</span>
                 <input class="textType" id="field.foo.key.0.bar"
                        name="field.foo.key.0.bar"
                        size="20" type="text" value=""  />
              </td>
              ...
            </tr>
            ...
        """



class SequenceDisplayWidgetTest(
    VerifyResults, DictWidgetTestHelper, unittest.TestCase):

    def _WidgetFactory(self, *args, **kw):
        #w = SequenceDisplayWidget(*args, **kw)
        w = DictDisplayWidget(*args, **kw)
        w.cssClass = "testwidget"
        return w

    def setUp(self):
        self.setUpContent()
        self.request = TestRequest()
        #self.widget = self._WidgetFactory(
        #    self.field, self.field.value_type, self.request)
        self.widget = self._WidgetFactory(
            self.field, self.request)
        provideAdapter(DisplayWidget, (ITextLine, IDefaultBrowserLayer),
                       IDisplayWidget)
        
    def test_render_empty(self):
        self.content.foo = ()
        self.assertEquals(self.widget(), '(no values)')

    def test_render_missing(self):
        self.content.foo = self.field.missing_value
        self.assertEquals(self.widget(), '(no value available)')

    def test_render_single(self):
        self.content.foo = {u'one':u'value'}
        check_list = ['<ol', 'class=', 'testwidget',
                      '<li', 'one: value', '</li', '</ol']
        self.verifyResult(self.widget(), check_list, inorder=True)

    def test_render_multiple(self):
        self.content.foo = {u'one': u'entry',
                            u'entry': u'two',
                            u'three': u'a value',
                            u'fourth': u'entry'}
        check_list = ['<ol', 'class=', 'testwidget',
                      '<li', 'entry: two', '</li',
                      '<li', 'three: a value', '</li',
                      '<li', 'fourth: entry', '</li',
                      '<li', 'one: entry', '</li',
                      '</ol']
        self.verifyResult(self.widget(), check_list, inorder=True)

    def test_render_alternate_cssClass(self):
        #self.content.foo = (u'one value',)
        self.content.foo = {u'one': u'value'}
        check_list = ['<ol', 'class=', 'altclass',
                      '<li', 'one: value', '</li', '</ol']
        self.widget.cssClass = 'altclass'
        self.verifyResult(self.widget(), check_list, inorder=True)

    def test_honors_subwidget(self):
        self.widget = self._WidgetFactory(
            self.field, self.request,
            subwidget=UppercaseDisplayWidget)
        self.content.foo = {u'first value': u'second value'}
        check_list = ['<ol', 'class=', 'testwidget',
                      '<li', 'FIRST VALUE: SECOND VALUE', '</li',
                      '</ol']
        self.verifyResult(self.widget(), check_list, inorder=True)


class UppercaseDisplayWidget(DisplayWidget):

    def __call__(self):
        return super(UppercaseDisplayWidget, self).__call__().upper()


def setUp(test):
    traversingSetUp()
    provideAdapter(TextWidget,
                   (ITextLine, IDefaultBrowserLayer),
                   IInputWidget)
    provideAdapter(WidgetInputErrorView,
                   (IWidgetInputError, IDefaultBrowserLayer),
                   IWidgetInputErrorView)

def tearDown(test):
    traversingTearDown()


def test_suite():
    return unittest.TestSuite((
        unittest.makeSuite(DictWidgetTest),
        doctest.DocTestSuite(setUp=setUp, tearDown=tearDown,
                             optionflags=doctest.ELLIPSIS
                             |doctest.NORMALIZE_WHITESPACE
                             |doctest.REPORT_NDIFF),
        unittest.makeSuite(SequenceDisplayWidgetTest),
        ))

if __name__=='__main__':
    unittest.main(defaultTest='test_suite')
