testFields.py 30 KB
Newer Older
1
# -*- coding: utf-8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
#          Jerome Perrin <jerome@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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.
#
##############################################################################

30 31
# TODO: Some tests from this file can be merged into Formulator

32 33
import unittest

34 35 36 37 38 39
try:
    from zope.app.testing.placelesssetup import PlacelessSetup
except ImportError:
    # BACK: Zope 2.8. Remove when we no longer support it
    from zope.component.tests.placelesssetup import PlacelessSetup

40 41
# Make it possible to use Globals.get_request
class DummyRequest(dict):
42
  __allow_access_to_unprotected_subobjects__ = 1
43 44 45 46 47 48 49 50 51 52 53
  def set(self, k, v):
    self[k] = v

global request
request = DummyRequest()

def get_request():
  global request
  return request

# apply patch (before it's imported by other modules)
54
from Products.ERP5Type import Globals
55 56 57 58
Globals.get_request = get_request


# Initialize ERP5Form Product to load monkey patches
59 60
from Testing import ZopeTestCase
ZopeTestCase.installProduct('ERP5Form')
61 62
# Initialize ERP5Type Product to install interactors
ZopeTestCase.installProduct('ERP5Type')
63

64
from Acquisition import aq_base
65
from Products.Formulator.FieldRegistry import FieldRegistry
66
from Products.Formulator.Validator import ValidationError
67 68
from Products.Formulator.StandardFields import FloatField, StringField,\
DateTimeField, TextAreaField
69
from Products.Formulator.MethodField import Method, BoundMethod
70
from Products.Formulator.TALESField import TALESMethod
71 72

from Products.ERP5Type.Core.Folder import Folder
73 74 75
from Products.ERP5Form.Form import ERP5Form
from Products.ERP5Form.Form import purgeFieldValueCache
from Products.ERP5Form.Form import getFieldValue
76 77
from Products.ERP5Form import Form
from Products.ERP5Form import ProxyField
78
from DateTime import DateTime
79

80 81
from Products.Formulator.Widget import NSMAP
ODG_XML_WRAPPING_XPATH = 'draw:text-box/text:p/text:span'
82

83 84 85 86 87 88 89 90 91 92
class TestRenderViewAPI(unittest.TestCase):
  """For all fields and widgets, tests the signature of the render_view method.
  In particular, render_view must accept a 'REQUEST' parameter after 'value'.
  """

  def getTitle(self):
    return "{Field,Widget}.render_view"

  def test_signature(self):
    for field in FieldRegistry.get_field_classes().itervalues():
Yoshinori Okuji's avatar
Yoshinori Okuji committed
93
      self.assertEquals(('self', 'value', 'REQUEST', 'render_prefix'),
94 95 96
                        field.render_view.im_func.func_code.co_varnames)
      if field is not ProxyField.ProxyField:
        self.assertEquals(('self', 'field', 'value', 'REQUEST'),
97
          field.widget.render_view.im_func.func_code.co_varnames[:4], '%s %s' % (field.widget, field.widget.render_view.im_func.func_code.co_varnames[:4]))
98 99


100 101 102
class TestFloatField(unittest.TestCase):
  """Tests Float field
  """
103 104 105 106

  def getTitle(self):
    return "Float Field"

107 108 109
  def setUp(self):
    self.field = FloatField('test_field')
    self.widget = self.field.widget
110
    self.validator = self.field.validator
111

112
  def test_format_thousand_separator_point(self):
113 114
    self.field.values['input_style'] = '-1 234.5'
    self.assertEquals('1 000.0', self.widget.format_value(self.field, 1000))
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
  
  def test_format_thousand_separator_coma(self):
    self.field.values['input_style'] = '-1 234,5'
    self.assertEquals('1 000,0', self.widget.format_value(self.field, 1000))

  def test_format_thousand_separator_point_coma(self):
    self.field.values['input_style'] = '-1.234,5'
    self.assertEquals('1.000,0', self.widget.format_value(self.field, 1000))

  def test_format_thousand_separator_coma_point(self):
    self.field.values['input_style'] = '-1,234.5'
    self.assertEquals('1,000.0', self.widget.format_value(self.field, 1000))

  def test_format_thousand_separator_first_separator(self):
    # test for an edge case bug bug, ",100,000.0" was displayed (with leading coma)
    self.field.values['input_style'] = '-1,234.5'
    self.assertEquals('100,000.0', self.widget.format_value(self.field, 100000))
132 133
    self.assertEquals('-100,000.0', self.widget.format_value(self.field, -100000))

134 135 136 137 138 139 140
  def test_format_percent_style(self):
    self.field.values['input_style'] = '-12.3%'
    self.assertEquals('10.0%', self.widget.format_value(self.field, 0.1))

  def test_format_precision(self):
    self.field.values['precision'] = 0
    self.assertEquals('12', self.widget.format_value(self.field, 12.34))
141 142
    # value is rounded
    self.assertEquals('13', self.widget.format_value(self.field, 12.9))
143

144
    purgeFieldValueCache() # call this before changing internal field values.
145 146 147 148
    self.field.values['precision'] = 2
    self.assertEquals('0.01', self.widget.format_value(self.field, 0.011))
    # value is rounded
    self.assertEquals('0.01', self.widget.format_value(self.field, 0.009999))
149
  
150 151 152 153 154
  def test_render_view(self):
    self.field.values['input_style'] = '-1 234.5'
    self.field.values['precision'] = 2
    self.field.values['editable'] = 0
    self.assertEquals('1&nbsp;000.00', self.field.render(1000))
155 156 157 158 159 160 161 162

  def test_render_dict(self):
    self.field.values['input_style'] = '-1 234.5'
    self.field.values['precision'] = 4
    self.assertEquals(dict(query=0.12345,
                           format='0.0000',
                           type='float'),
                      self.field.render_dict(0.12345))
163 164 165 166 167 168
  
  def test_render_string_value(self):
    self.field.values['precision'] = 2
    self.field.values['editable'] = 0
    self.assertEquals('12.34', self.field.render("12.34"))
    self.assertEquals('not float', self.field.render("not float"))
169

170 171 172 173 174 175
  def test_percent_style_render_string_value(self):
    self.field.values['input_style'] = '-12.3%'
    self.field.values['editable'] = 0
    self.assertEquals('-12.34%', self.field.render("-0.1234"))
    self.assertEquals('not float', self.field.render("not float"))

176 177 178 179 180 181
  def test_render_big_numbers(self):
    self.field.values['precision'] = 2
    self.field.values['editable'] = 0
    self.assertEquals('10000000000000000000.00',
                      self.field.render(10000000000000000000))

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  def test_validate_thousand_separator_point(self):
    self.field.values['input_style'] = '-1 234.5'
    request.set('field_test_field', '1 000.0')
    self.assertEquals(1000,
        self.validator.validate(self.field, 'field_test_field', request))
  
  def test_validate_thousand_separator_coma(self):
    self.field.values['input_style'] = '-1 234,5'
    request.set('field_test_field', '1 000,0')
    self.assertEquals(1000,
        self.validator.validate(self.field, 'field_test_field', request))

  def test_validate_thousand_separator_point_coma(self):
    self.field.values['input_style'] = '-1.234,5'
    request.set('field_test_field', '1.000,0')
    self.assertEquals(1000,
        self.validator.validate(self.field, 'field_test_field', request))

  def test_validate_thousand_separator_coma_point(self):
    self.field.values['input_style'] = '-1,234.5'
    request.set('field_test_field', '1,000.0')
    self.assertEquals(1000,
        self.validator.validate(self.field, 'field_test_field', request))

  def test_validate_percent_style(self):
    self.field.values['input_style'] = '-12.3%'
    request.set('field_test_field', '10.0%')
    self.assertEquals(0.1,
        self.validator.validate(self.field, 'field_test_field', request))

  def test_validate_not_float(self):
    request.set('field_test_field', 'not_float')
    self.assertRaises(ValidationError,
        self.validator.validate, self.field, 'field_test_field', request)

  def test_validate_two_comma(self):
    self.field.values['input_style'] = '-1.234,5'
    request.set('field_test_field', '1,000,0')
    self.assertRaises(ValidationError,
        self.validator.validate, self.field, 'field_test_field', request)

  def test_validate_two_dots(self):
    self.field.values['input_style'] = '-1,234.5'
    request.set('field_test_field', '1.000.0')
    self.assertRaises(ValidationError,
        self.validator.validate, self.field, 'field_test_field', request)
228

229 230 231 232 233 234 235 236 237 238 239 240
  def test_render_odt(self):
    self.field.values['input_style'] = '-1 234.5'
    self.field.values['default'] = 1000
    self.assertEquals('1 000.0', self.field.render_odt(as_string=False).text)

  def test_render_odg(self):
    self.field.values['input_style'] = '-1 234.5'
    self.field.values['default'] = 1000
    test_value = self.field.render_odg(as_string=False)\
      .xpath('%s/text()' % ODG_XML_WRAPPING_XPATH, namespaces=NSMAP)[0]
    self.assertEquals('1 000.0', test_value)

241 242 243
class TestStringField(unittest.TestCase):
  """Tests string field
  """
244 245 246 247

  def getTitle(self):
    return "String Field"

248 249 250 251 252 253 254 255
  def setUp(self):
    self.field = StringField('test_field')
    self.widget = self.field.widget

  def test_escape_html(self):
    self.field.values['editable'] = 0
    self.assertEquals('&lt;script&gt;', self.field.render("<script>"))

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
  def test_render_odt(self):
    self.field.values['default'] = 'Hello World! <&> &lt;&mp;&gt;'
    self.assertEquals('Hello World! <&> &lt;&mp;&gt;', self.field.render_odt(as_string=False).text)
    self.assertEquals('Hello World!', self.field.render_odt(value='Hello World!', as_string=False).text)

  def test_render_odg(self):
    self.field.values['default'] = 'Hello World! <&> &lt;&mp;&gt;'
    test_value = self.field.render_odg(as_string=False)\
      .xpath('%s/text()' % ODG_XML_WRAPPING_XPATH, namespaces=NSMAP)[0]
    self.assertEquals('Hello World! <&> &lt;&mp;&gt;', test_value)
    test_value = self.field.render_odg(value='Hello World!', as_string=False)\
      .xpath('%s/text()' % ODG_XML_WRAPPING_XPATH, namespaces=NSMAP)[0]
    self.assertEquals('Hello World!', test_value)

class TestDateTimeField(unittest.TestCase):
  """Tests DateTime field
  """

  def getTitle(self):
    return "DateTime Field"

  def setUp(self):
    self.field = DateTimeField('test_field')
    self.widget = self.field.widget

  def test_render_odt(self):
    self.field.values['default'] = DateTime('2010/01/01 00:00:01 UTC')
    self.assertEquals('2010/01/01   00:00',
            self.field.render_odt(as_string=False).text)

class TestTextAreaField(unittest.TestCase):
  """Tests TextArea field
  """

  def getTitle(self):
    return "TextArea Field"

  def setUp(self):
    self.field = TextAreaField('test_field')
    self.widget = self.field.widget

  def test_render_odt(self):
    self.field.values['default'] = 'My first Line\nMy Second Line\tfoo'
    self.assertEquals('text:line-break', 
        self.field.render_odt(as_string=False)[0].xpath('name()'))
    self.assertEquals('text:tab',
        self.field.render_odt(as_string=False)[1].xpath('name()'))

  def test_render_odg(self):
    self.field.values['default'] = 'My first Line\nMy Second Line\tfoo'
    test_value = self.field.render_odg(as_string=False)\
      .xpath('%s/text:line-break' % ODG_XML_WRAPPING_XPATH, namespaces=NSMAP)
    self.assertTrue(test_value)
    test_value = self.field.render_odg(as_string=False)\
      .xpath('%s/text:tab' % ODG_XML_WRAPPING_XPATH, namespaces=NSMAP)
    self.assertTrue(test_value)
312

313
class TestProxyField(PlacelessSetup, unittest.TestCase):
314 315 316 317

  def getTitle(self):
    return "Proxy Field"

318
  def setUp(self):
319
    super(TestProxyField, self).setUp()
320 321 322 323 324 325 326
    self.container = Folder('container').__of__(Folder('root'))
    self.container._setObject('Base_viewProxyFieldLibrary',
                               ERP5Form('Base_viewProxyFieldLibrary', 'Proxys'))
    self.container._setObject('Base_view',
                               ERP5Form('Base_view', 'View'))
    global request
    request = DummyRequest()
327
    self.container.REQUEST = request
328 329 330 331 332 333 334
    try:
        from Products.CMFCore.tests.base.utils import _setUpDefaultTraversable
        _setUpDefaultTraversable()
    except ImportError:
        pass # On Zope 2.8, remove when we no longer support it

  # if tearDown is ever added, don't forget to call PlacelessSetup.tearDown()
335 336 337 338 339 340

  def addField(self, form, id, title, field_type):
    form.manage_addField(id, title, field_type)
    field = getattr(form, id)
    field._p_oid = makeDummyOid()
    return field
341 342

  def test_get_template_field(self):
343 344 345 346
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Not Title', 'ProxyField')
347
    self.assertEquals(None, proxy_field.getTemplateField())
348 349
    self.assertEquals(None, proxy_field.get_value('enable'))
    self.assertEquals(None, proxy_field.get_value('default'))
350

351 352 353 354 355
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    self.assertEquals(original_field, proxy_field.getTemplateField())

  def test_simple_surcharge(self):
356 357
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
358 359
    self.assertEquals('Title', original_field.get_value('title'))

360 361
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Not Title', 'ProxyField')
362 363 364 365 366
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    self.assert_(proxy_field.is_delegated('title'))
    self.assertEquals('Title', proxy_field.get_value('title'))

367
  def test_simple_not_surcharge(self):
368 369
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
370 371
    self.assertEquals('Title', original_field.get_value('title'))

372 373
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Proxy Title', 'ProxyField')
374 375 376 377 378 379 380 381
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    # XXX no API for this ?
    proxy_field._surcharged_edit(dict(title='Proxy Title'), ['title'])

    self.failIf(proxy_field.is_delegated('title'))
    self.assertEquals('Proxy Title', proxy_field.get_value('title'))

382 383 384
  def test_get_value_default(self):
    # If the proxy field is named 'my_id', it will get 'id'
    # property on the context, regardless of the id of the proxified field
385 386 387 388
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
    proxy_field = self.addField(self.container.Base_view,
                                'my_id', 'ID', 'ProxyField')
389 390 391 392 393
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    self.assertEquals('container', self.container.getId())
    self.assertEquals('container', proxy_field.get_value('default'))

394
  def test_field_tales_context(self):
395 396
    # in the TALES context, "field" will be the proxyfield, not the original
    # field.
397 398
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
399 400 401
    original_field.manage_tales_xmlrpc(dict(title='field/getId'))
    self.assertEquals('my_title', original_field.get_value('title'))

402 403
    proxy_field = self.addField(self.container.Base_view,
                                'my_reference', 'Not Title', 'ProxyField')
404 405 406 407 408
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    # 'my_reference' is the ID of the proxy field
    self.assertEquals('my_reference', proxy_field.get_value('title'))

409 410 411
  def test_form_tales_context(self):
    # in the TALES context, "form" will be the form containing the proxyfield,
    # not the original form (ie. the field library).
412 413
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
414 415 416 417
    original_field.manage_tales_xmlrpc(dict(title='form/getId'))
    self.assertEquals('Base_viewProxyFieldLibrary',
                       original_field.get_value('title'))

418 419
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Title', 'ProxyField')
420 421 422
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    self.assertEquals('Base_view', proxy_field.get_value('title'))
423

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
  def test_get_value_cache_on_TALES_target(self):
    # If the proxy field defines its target using TALES, then no caching should
    # happen.
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
    other_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_other_field', 'Other', 'StringField')
    proxy_field = self.addField(self.container.Base_view,
                                'my_id', 'ID', 'ProxyField')
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary'))
    proxy_field.manage_tales_xmlrpc(dict(field_id='request/field_id'))

    self.container.REQUEST.set('field_id', 'my_title')
    self.assertEquals(original_field, proxy_field.getTemplateField())
    self.assertEquals('Title', proxy_field.get_value('title'))

    self.container.REQUEST.set('field_id', 'my_other_field')
    self.assertEquals(other_field, proxy_field.getTemplateField())
    self.assertEquals('Other', proxy_field.get_value('title'))

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
  def test_proxy_to_date_time_field(self):
    # date time fields are specific, because they use a 'sub_form', we must
    # make sure this works as expected
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_date', 'Date', 'DateTimeField')
    original_field.manage_edit_xmlrpc(dict(required=0))
    proxy_field = self.addField(self.container.Base_view,
                                'my_date', 'Date', 'ProxyField')
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_date',))
    self.assertTrue(hasattr(proxy_field, 'sub_form'))
    self.assertTrue(aq_base(proxy_field.sub_form) is
                      aq_base(original_field.sub_form))
    # we can render
    proxy_field.render()
    # and validate
    self.container.Base_view.validate_all_to_request(dict())
    
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
  def test_manage_edit_surcharged_xmlrpc(self):
    # manage_edit_surcharged_xmlrpc is a method to edit proxyfields
    # programmatically
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_string', 'String', 'StringField')
    proxy_field = self.addField(self.container.Base_view,
                                'my_String', '', 'ProxyField')
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_date',))

    proxy_field.manage_edit_surcharged_xmlrpc(dict(title='Title'))
    self.assertFalse(proxy_field.is_delegated('title'))
    self.assertEquals('Title', proxy_field.get_value('title'))

    # beware that all values that are not passed in the mapping will be
    # delegated again, regardless of the old state.
    proxy_field.manage_edit_surcharged_xmlrpc(dict())
    self.assertTrue(proxy_field.is_delegated('title'))

Yusei Tahara's avatar
Yusei Tahara committed
481 482 483 484 485 486 487 488
  def test_same_field_id_in_proxy_field_and_template_field(self):
    """
    Test a case that if proxy field id is same as template field id.
    """
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_string', 'String', 'StringField')
    # Use different id to the template field.
    proxy_field2 = self.addField(self.container.Base_view,
Yusei Tahara's avatar
Yusei Tahara committed
489
                                 'my_another_string', '', 'ProxyField')
Yusei Tahara's avatar
Yusei Tahara committed
490 491
    # Use same id to the template field.
    proxy_field1 = self.addField(self.container.Base_view,
Yusei Tahara's avatar
Yusei Tahara committed
492
                                 'my_string', '', 'ProxyField')
Yusei Tahara's avatar
Yusei Tahara committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
    proxy_field2.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                         field_id='my_string',))
    proxy_field1.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                         field_id='my_string',))

    def make_dummy_getter(value):
      def method():
        return value
      return method

    self.container.getAnotherString = make_dummy_getter('WAAA')
    self.container.getString = make_dummy_getter('123')

    # First, call field which the id is different to the template field's.
    self.assertEqual('WAAA', proxy_field2.get_value('default'))

    # Next, call field which the id is same to the template field's.
    self.assertEqual('123', proxy_field1.get_value('default'))

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
  def test_dicts_cleared_on_edit(self):
    """
    Test that values and tales dicts are cleared when property is switched to
    not surcharged.
    """
    # create a field
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'OrigTitle', 'StringField')
    field = self.addField(self.container.Base_view,
                                   'my_dict_test', '', 'ProxyField')
    field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                         field_id='my_title',))
    def surcharge_edit():
      #surcharge from edit
      field._surcharged_edit(dict(title='TestTitle'), ['title'])
      self.assertTrue('title' in field.delegated_list)
      self.assertEquals(field.values['title'], 'TestTitle')
      self.assertTrue('title' not in field.tales)

    def delegate_edit():
      # delegate the field from edit view
      field._surcharged_edit(dict(title='TestTitle'), [])
      self.assertTrue('title' not in field.delegated_list)
      self.assertTrue('title' not in field.values)
      self.assertTrue('title' not in field.tales)

    def surcharge_tales():
      #surcharge from tales
      field._surcharged_tales(dict(title='string:TestTitle'), ['title'])
      self.assertTrue('title' in field.delegated_list)
      self.assertTrue(field.values['title'], 'OrigTitle')
      self.assertEquals(field.tales['title'], 'string:TestTitle')

    def delegate_tales():
      # delegate the field from tales view
      field._surcharged_tales(dict(title='string:TestTitle'), [])
      self.assertTrue('title' not in field.delegated_list)
      self.assertTrue('title' not in field.values)
      self.assertTrue('title' not in field.tales)
    
    surcharge_edit()
    delegate_edit()
    surcharge_edit()
    delegate_tales()
    surcharge_tales()
    delegate_edit()
    surcharge_tales()
    delegate_tales()

561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
  def test_proxify_error_message(self):
    """
    Test that error messages can be delegated and surcharged.
    """
    # create a field
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'OrigTitle', 'StringField')
    field = self.addField(self.container.Base_view,
                                   'my_dict_test', '', 'ProxyField')
    field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                         field_id='my_title',))
    self.assertEquals(original_field.get_error_names(),
        field.get_error_names())
    test_error = 'too_long' # arbitrary chosen among StringField error names
    test_message = 'Some Unprobable Error'
    test_message2 = 'Some Even More Unprobable Error'
    original_field.message_values[test_error] = test_message
    field.message_values[test_error] = test_message2
    # delegated (by default)
    self.assertEquals(original_field.get_error_message(test_error),
      test_message)
    self.assertTrue(field.is_message_delegated(test_error))
    self.assertEquals(field.get_error_message(test_error), test_message)
    # surcharged
    field.delegated_message_list = [test_error]
    self.assertEquals(original_field.get_error_message(test_error),
      test_message)
    self.assertFalse(field.is_message_delegated(test_error))
    self.assertEquals(field.get_error_message(test_error), test_message2)
590

591 592 593
class TestFieldValueCache(unittest.TestCase):
  """Tests field value caching system
  """
594 595 596 597

  def getTitle(self):
    return "Field Value Cache"

598
  def setUp(self):
599
    self.root = Folder('root')
600
    self.root = self.root.__of__(self.root)
601
    self.root.form = ERP5Form('form', 'Form')
602 603
    self.root.getProperty = lambda key, d=None: \
      dict(on_memory_field='123').get(key, d)
604 605 606 607

    form = self.root.form
    form.field = StringField('test_field')
    form.field._p_oid = makeDummyOid()
608
    # method field
609 610
    form.field.values['external_validator'] = Method('this_is_a_method')
    # on-memory field (not in zodb)
611 612 613 614 615 616
    form.my_on_memory_field = StringField('my_on_memory_field')
    form.my_on_memory_field._p_oid = None
    form.my_on_memory_tales_field = StringField('my_on_memory_tales_field')
    form.my_on_memory_tales_field.manage_tales_xmlrpc({
      'default': 'python: repr(here)'})
    form.my_on_memory_field._p_oid = None
617 618 619 620 621 622 623 624 625 626
    # proxy field
    form.proxy_field = ProxyField.ProxyField('test_proxy_field')
    form.proxy_field._p_oid = makeDummyOid()
    form.proxy_field.values['form_id'] = 'form'
    form.proxy_field.values['field_id'] = 'field'
    # proxy field with tales
    form.proxy_field_tales = ProxyField.ProxyField('test_proxy_field')
    form.proxy_field_tales._p_oid = makeDummyOid()
    form.proxy_field_tales.tales['form_id'] = TALESMethod('string:form')
    form.proxy_field_tales.tales['field_id'] = TALESMethod('string:field')
627 628 629 630 631 632
    # datetime field (input style is list)
    form.datetime_field = DateTimeField('datetime_field')
    form.datetime_field._p_oid = makeDummyOid()
    form.datetime_field._edit(dict(input_style='list'))
    for i in form.datetime_field.sub_form.fields.values():
      i._p_oid = makeDummyOid()
633 634

  def test_method_field(self):
635
    field = self.root.form.field
636
    value, cacheable = getFieldValue(field, field, 'external_validator')
637 638 639
    self.assertEqual(False, value.value is field.values['external_validator'])
    self.assertEqual(True, type(value.value) is Method)

640 641 642 643 644 645 646 647 648 649
  def test_using_cache_or_not(self):
    # check standard field in zodb
    # make sure that this will use cache.
    cache_size = len(Form._field_value_cache)
    self.root.form.field.get_value('title')
    self.assertEqual(True, cache_size < len(Form._field_value_cache))

    # check on-memory field
    # make sure that this will not use cache.
    cache_size = len(Form._field_value_cache)
650 651 652 653
    self.assertEqual(repr(self.root),
      self.root.form.my_on_memory_tales_field.get_value('default'))
    self.assertEqual('123',
      self.root.form.my_on_memory_field.get_value('default'))
654 655 656 657 658 659 660 661 662 663 664 665 666 667
    self.assertEqual(True, cache_size == len(Form._field_value_cache))

    # check proxy field
    # make sure that this will use cache.
    cache_size = len(ProxyField._field_value_cache)
    self.root.form.proxy_field.get_value('title')
    self.assertEqual(True, cache_size < len(ProxyField._field_value_cache))

    # check proxy field with tales
    # make sure that this will not use cache.
    cache_size = len(ProxyField._field_value_cache)
    self.root.form.proxy_field_tales.get_value('title')
    self.assertEqual(True, cache_size == len(ProxyField._field_value_cache))

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
  def test_datetime_field(self):
    purgeFieldValueCache()
    
    # make sure that boundmethod must not be cached.
    year_field = self.root.form.datetime_field.sub_form.get_field('year', include_disabled=1)
    self.assertEqual(True, type(year_field.overrides['items']) is BoundMethod)

    cache_size = len(Form._field_value_cache)
    year_field.get_value('items')

    # See Formulator/StandardFields.py(line:174)
    # there are two get_value, start_datetime and end_datetime
    cache_size += 2

    # make sure that boundmethod is not cached(cache size does not change)
    self.assertEqual(True, ('Form.get_value',
                            self.root.form.datetime_field._p_oid,
                            self.root.form.datetime_field._p_oid,
                            'start_datetime'
                            ) in Form._field_value_cache)
    self.assertEqual(True, ('Form.get_value',
                            self.root.form.datetime_field._p_oid,
                            self.root.form.datetime_field._p_oid,
                            'end_datetime'
                            ) in Form._field_value_cache)
    self.assertEqual(False, ('Form.get_value',
                            year_field._p_oid,
                            year_field._p_oid,
                            'items'
                            ) in Form._field_value_cache)
    self.assertEqual(cache_size, len(Form._field_value_cache))

    year_field.get_value('size')
    year_field.get_value('default')
    self.assertEqual(cache_size+2, len(Form._field_value_cache))
703

704 705 706 707 708
def makeDummyOid():
  import time, random
  return '%s%s' % (time.time(), random.random())


709 710
def test_suite():
  suite = unittest.TestSuite()
711
  suite.addTest(unittest.makeSuite(TestRenderViewAPI))
712 713
  suite.addTest(unittest.makeSuite(TestFloatField))
  suite.addTest(unittest.makeSuite(TestStringField))
714 715
  suite.addTest(unittest.makeSuite(TestDateTimeField))
  suite.addTest(unittest.makeSuite(TestTextAreaField))
716
  suite.addTest(unittest.makeSuite(TestProxyField))
717
  suite.addTest(unittest.makeSuite(TestFieldValueCache))
718
  return suite