## $Id: test_phonewidget.py 7494 2012-01-20 10:10:06Z henrik $
##
## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
## 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
##
"""
Tests for PhoneWidget.

Tests in here are an extended reformulation of the tests done in
z3.widget as doctests for the usphone widget.
"""
import unittest
from zope import schema
# XXX: could we possibly get rid of zope.app.testing?
from zope.app.testing.setup import placefulSetUp, placefulTearDown
from zope.component import provideAdapter
from zope.formlib.exception import WidgetInputErrorView
from zope.formlib.interfaces import (
    WidgetInputError, IInputWidget, IBrowserWidget, IWidgetInputError,
    IWidgetInputErrorView)
from zope.formlib.textwidgets import TextWidget
from zope.interface import verify, Interface, implements
from zope.publisher.browser import TestRequest
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
from zope.schema.interfaces import ITextLine
from waeup.sirp.widgets.phonewidget import PhoneWidget

# Dummy content
class ISampleContent(Interface):
    foo = schema.TextLine(
        title = u'Phone',
        description = u'Phone number',
        required = True)

    bar = schema.TextLine(
        title = u'Phone',
        description = u'Phone number (not required)',
        required = False,)

    baz = schema.TextLine(
        title = u'Phone',
        description = u'Required phone with a default',
        required = True,
        default=u'234--')

class SampleContent:
    implements(ISampleContent)

class PhoneWidgetTests(unittest.TestCase):
    # Tests for phone widget

    def setUp(self):
        placefulSetUp()

        # register TextLine views (so that subwidgets can be rendered)
        provideAdapter(
            TextWidget, (ITextLine, IDefaultBrowserLayer), IInputWidget)
        # errors in forms
        provideAdapter(
            WidgetInputErrorView, (IWidgetInputError, IDefaultBrowserLayer),
            IWidgetInputErrorView)

        # setup some sample content to bind to (only neccessary for
        # some of the tests)
        self.content = SampleContent()
        self.field = ISampleContent['foo']
        self.field = self.field.bind(self.content)
        self.field_nonreq = ISampleContent['bar']
        self.field_nonreq = self.field_nonreq.bind(self.content)
        self.field_w_default = ISampleContent['baz']
        self.field_w_default = self.field_w_default.bind(self.content)

        # create an empty request. We need one to create a widget
        self.request = TestRequest()
        self.widget = PhoneWidget(self.field, self.request)
        self.widget_nonreq = PhoneWidget(self.field_nonreq, self.request)
        return

    def tearDown(self):
        placefulTearDown()
        return

    def test_ifaces(self):
        # make sure we fullfill interface contracts
        verify.verifyClass(IBrowserWidget, PhoneWidget)
        verify.verifyClass(IInputWidget, PhoneWidget)
        verify.verifyObject(IBrowserWidget, self.widget)
        verify.verifyObject(IInputWidget, self.widget)
        return

    def test_attribs_set(self):
        # make sure the public attributes are set correctly
        self.assertEqual(self.widget.name, 'field.foo')
        self.assertEqual(self.widget.label, u'Phone')
        self.assertEqual(self.widget.hint, u'Phone number')
        self.assertEqual(self.widget.visible, True)
        self.assertEqual(self.widget.required, True)
        self.assertEqual(self.widget_nonreq.required, False)
        return

    def test_subwidgets_exist(self):
        # make sure we have three sub widgets
        sub_widget1 = self.widget.widgets['country']
        sub_widget2 = self.widget.widgets['area']
        sub_widget3 = self.widget.widgets['extension']
        self.assertTrue(isinstance(sub_widget1, TextWidget))
        self.assertTrue(isinstance(sub_widget2, TextWidget))
        self.assertTrue(isinstance(sub_widget3, TextWidget))
        return

    def test_set_rendered_value(self):
        # make sure we render values correctly
        self.widget.setRenderedValue(u'123-456-1234567')
        self.assertEqual(
            self.widget(),
            (u'+&nbsp;<input class="span2 textType" id="field.foo.country"'
             u' name="field.foo.country" size="20" type="text" value="123" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span2 textType" id="field.foo.area"'
             u' name="field.foo.area" size="20" type="text" value="456" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span3 textType" id="field.foo.extension"'
             u' name="field.foo.extension" size="20" type="text"'
             u' value="1234567"  />\n'))
        return

    def test_set_prefix_default(self):
        # prefix determines the name of widget and its subwidgets.
        self.assertEqual(self.widget.name, 'field.foo')
        self.assertEqual(self.widget.widgets['country'].name,
                         'field.foo.country')
        self.assertEqual(self.widget.widgets['area'].name,
                         'field.foo.area')
        self.assertEqual(self.widget.widgets['extension'].name,
                         'field.foo.extension')
        return

    def test_set_prefix_changed(self):
        # when we set the prefix, widget names change
        self.widget.setPrefix('test.')
        self.assertEqual(self.widget.name, 'test.foo')
        self.assertEqual(self.widget.widgets['country'].name,
                         'test.foo.country')
        self.assertEqual(self.widget.widgets['area'].name,
                         'test.foo.area')
        self.assertEqual(self.widget.widgets['extension'].name,
                         'test.foo.extension')
        return

    def test_set_prefix_changed_no_dot(self):
        # when we try to set a name without trailing dot, one will be
        # inserted automatically
        self.widget.setPrefix('test')
        self.assertEqual(self.widget.name, 'test.foo')
        self.assertEqual(self.widget.widgets['country'].name,
                         'test.foo.country')
        self.assertEqual(self.widget.widgets['area'].name,
                         'test.foo.area')
        self.assertEqual(self.widget.widgets['extension'].name,
                         'test.foo.extension')
        return

    def test_get_input_value(self):
        # we get a string when we want the input value
        request = TestRequest(
            form={
                'field.foo.country': '123',
                'field.foo.area': '456',
                'field.foo.extension': '12345'
                })
        widget = PhoneWidget(self.field, request)
        value = widget.getInputValue()
        self.assertEqual(value, '123-456-12345')
        return

    def test_get_input_value_invalid(self):
        # we get errors when valid data is sent
        request = TestRequest(
            form={
                'field.foo.country': 'not-a-number',
                'field.foo.area': '456',
                'field.foo.extension': '12345',
                })
        widget = PhoneWidget(self.field, request)
        self.assertRaises(
            WidgetInputError,
            widget.getInputValue)
        return

    def test_apply_changes(self):
        # we can apply passed-in numbers to content objects. The
        # widget is smart enough to detect whether the values really
        # changed.
        class Content(object):
            field = None
        content = Content()
        request = TestRequest(form={
                'field.foo.country': '123',
                'field.foo.area': '456',
                'field.foo.extension': '7890'})
        widget = PhoneWidget(self.field, request)
        result1 = widget.applyChanges(content)
        result2 = widget.applyChanges(content)
        self.assertEqual(result1, True)
        self.assertEqual(content.foo, u'123-456-7890')
        self.assertEqual(result2, False)
        return

    def test_has_input(self):
        # we can check whether there is input without actually validating.
        request = TestRequest(form={
                'field.foo.country': '123'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasInput()
        self.assertEqual(result, False)

        request = TestRequest(form={
                'field.foo.area': '123'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasInput()
        self.assertEqual(result, False)

        request = TestRequest(form={
                'field.foo.extension': '123567'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasInput()
        self.assertEqual(result, False)

        request = TestRequest(form={
                'field.foo.country': '123',
                'field.foo.area': '456',
                'field.foo.extension': '7890'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasInput()
        self.assertEqual(result, True)

        self.assertEqual(self.widget.hasInput(), False)
        return

    def test_has_valid_input(self):
        # we can check for input also including validation
        request = TestRequest(form={
                'field.foo.country': '123'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasValidInput()
        self.assertEqual(result, False)

        request = TestRequest(form={
                'field.foo.area': '123'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasValidInput()
        self.assertEqual(result, False)

        request = TestRequest(form={
                'field.foo.extension': '123567'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasValidInput()
        self.assertEqual(result, False)

        request = TestRequest(form={
                'field.foo.country': '123',
                'field.foo.area': '456',
                'field.foo.extension': '7890'})
        widget = PhoneWidget(self.field, request)
        result = widget.hasValidInput()
        self.assertEqual(result, True)

        self.assertEqual(self.widget.hasValidInput(), False)
        return

    def test_hidden(self):
        # we can render output as hidden field(s)
        self.widget.setRenderedValue(u'123-456-1234567')
        self.assertEqual(
            self.widget.hidden(),
            (u'<input class="span2 hiddenType" id="field.foo.country"'
             u' name="field.foo.country" type="hidden" value="123" '
             u' />'
             u'\n'
             u'<input class="span2 hiddenType" id="field.foo.area"'
             u' name="field.foo.area" type="hidden" value="456" '
             u' />'
             u'\n'
             u'<input class="span3 hiddenType" id="field.foo.extension"'
             u' name="field.foo.extension" type="hidden"'
             u' value="1234567"  />'))
        return

    def test_error_extension(self):
        # the widget can handle input errors in extension subwidget

        # extension field too short
        request = TestRequest(form={
                'field.foo.country': '123',
                'field.foo.area': '456',
                'field.foo.extension': '0'})
        widget = PhoneWidget(self.field, request)
        try:
            widget.getInputValue()
            self.fail('No error raised')
        except WidgetInputError, e:
            pass
        self.assertEqual(
            e.__repr__(),
            ("WidgetInputError("
             "'extension', u'Direct Line', ConstraintNotSatisfied(u'0'))"))
        self.assertEqual(
            widget.error(),
            u'<span class="error">Constraint not satisfied</span>')
        return


    def test_error_area(self):
        # the widget can handle input errors in area code subwidget

        # area code contains illegal chars
        request = TestRequest(form={
                'field.foo.country': '123',
                'field.foo.area': 'no-number',
                'field.foo.extension': '12345'})
        widget = PhoneWidget(self.field, request)
        try:
            widget.getInputValue()
            self.fail('No error raised')
        except WidgetInputError, e:
            pass
        self.assertEqual(
            e.__repr__(),
            ("WidgetInputError("
             "'area', u'Area Code', ConstraintNotSatisfied(u'no-number'))"))
        self.assertEqual(
            widget.error(),
            u'<span class="error">Constraint not satisfied</span>')
        return

    def test_error_country(self):
        # the widget can handle input errors in country code subwidget

        # invalid country code
        request = TestRequest(form={
                'field.foo.country': 'XXX',
                'field.foo.area': '456',
                'field.foo.extension': '12345'})
        widget = PhoneWidget(self.field, request)
        try:
            widget.getInputValue()
            self.fail('No error raised')
        except WidgetInputError, e:
            pass
        self.assertEqual(
            e.__repr__(),
            ("WidgetInputError("
             "'country', u'Country Code', ConstraintNotSatisfied(u'XXX'))"))
        self.assertEqual(
            widget.error(),
            u'<span class="error">Constraint not satisfied</span>')
        return

    def test_render_wo_data(self):
        # when no data was submitted, the content data will be displayed
        request = TestRequest(form={})
        self.content.foo = u'11-222-3333'
        widget = PhoneWidget(self.field, request)
        self.assertEqual(
            widget(),
            (u'+&nbsp;<input class="span2 textType" id="field.foo.country"'
             u' name="field.foo.country" size="20" type="text" value="11" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span2 textType" id="field.foo.area"'
             u' name="field.foo.area" size="20" type="text" value="222" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span3 textType" id="field.foo.extension"'
             u' name="field.foo.extension" size="20" type="text"'
             u' value="3333"  />\n'))
        return

    def test_render_with_data(self):
        # when data was in the request, it will override any content value
        request = TestRequest(form={
                'field.foo.country': '11',
                'field.foo.area': '222',
                'field.foo.extension': '3333'})
        self.content.foo = u'22-333-4444'
        widget = PhoneWidget(self.field, request)
        self.assertEqual(
            widget(),
            (u'+&nbsp;<input class="span2 textType" id="field.foo.country"'
             u' name="field.foo.country" size="20" type="text" value="11" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span2 textType" id="field.foo.area"'
             u' name="field.foo.area" size="20" type="text" value="222" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span3 textType" id="field.foo.extension"'
             u' name="field.foo.extension" size="20" type="text"'
             u' value="3333"  />\n'))
        return

    def test_render_changed_prefix(self):
        # when the prefix is changed, the subwidgets will reflect that
        # properly. This tests a regression: if setPrefix was called
        # on the phone widget, the subwidgets were already assuming
        # that there is no data in the request
        request = TestRequest(form={
                'field.foo.country': '11',
                'field.foo.area': '222',
                'field.foo.extension': '3333'})
        self.content.foo = u'22-333-4444'
        widget = PhoneWidget(self.field, request)
        # when setting a wrong prefix (not in the form), subwidgets
        # will display the content values assuming (correctly) there
        # is no data sent for them in the request.
        widget.setPrefix('not-existent.')
        self.assertTrue('value="4444"' in widget())
        # when setting the right prefix, subwidgets will display the
        # sent form values and not the content values.
        widget.setPrefix('field.')
        self.assertTrue('value="3333"' in widget())
        return

    def test_render_changed_prefix(self):
        # when the prefix is changed, the subwidgets will reflect that
        # properly. This tests a regression: if setPrefix was called
        # on the phone widget, the subwidgets were already assuming
        # that there is no data in the request
        request = TestRequest(form={
                'field.foo.country': '11',
                'field.foo.area': '222',
                'field.foo.extension': '3333'})
        self.content.foo = u'22-333-4444'
        widget = PhoneWidget(self.field, request)
        # when setting a wrong prefix (not in the form), subwidgets
        # will display the content values assuming (correctly) there
        # is no data sent for them in the request.
        widget.setPrefix('not-existent.')
        self.assertTrue('value="4444"' in widget())
        # when setting the right prefix, subwidgets will display the
        # sent form values and not the content values.
        widget.setPrefix('field.')
        self.assertTrue('value="3333"' in widget())
        return

    def test_non_required_no_input(self):
        # if the bound field requires no input phone widget will cope
        # with that.
        class Content(object):
            field = None

        content = Content()
        request = TestRequest(form={
                'field.bar.country': '',
                'field.bar.area': '',
                'field.bar.extension': '',
                })
        widget = PhoneWidget(self.field_nonreq, request)
        result1 = widget.applyChanges(content)
        result2 = widget.getInputValue()
        self.assertEqual(result1, True)
        # without input we get None
        self.assertTrue(content.bar is None)
        self.assertTrue(result2 is None)
        return

    def test_set_rendered_with_empty_context_value(self):
        # if we set empty string as rendered value, empty fields are
        # displayed.
        self.widget.setRenderedValue(u'')
        self.assertEqual(
            self.widget(),
            (u'+&nbsp;<input class="span2 textType" id="field.foo.country"'
             u' name="field.foo.country" size="20" type="text" value="" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span2 textType" id="field.foo.area"'
             u' name="field.foo.area" size="20" type="text" value="" '
             u' />'
             u'&nbsp;-\n'
             u'<input class="span3 textType" id="field.foo.extension"'
             u' name="field.foo.extension" size="20" type="text"'
             u' value=""  />\n'))
        return
