testFields.py 36.5 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
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
import transaction
34 35
import unittest

36
# Initialize ERP5Form Product to load monkey patches
37
from Testing import ZopeTestCase
Nicolas Delaby's avatar
Nicolas Delaby committed
38

39
from Acquisition import aq_base
40
from Products.Formulator.FieldRegistry import FieldRegistry
41
from Products.Formulator.Validator import ValidationError
42
from Products.Formulator.StandardFields import FloatField, StringField,\
43 44
DateTimeField, TextAreaField, CheckBoxField, ListField, LinesField, \
MultiListField
45
from Products.Formulator.MethodField import Method, BoundMethod
46
from Products.Formulator.TALESField import TALESMethod
47 48

from Products.ERP5Type.Core.Folder import Folder
49 50 51
from Products.ERP5Form.Form import ERP5Form
from Products.ERP5Form.Form import purgeFieldValueCache
from Products.ERP5Form.Form import getFieldValue
52
from Products.ERP5Form import Form
Nicolas Delaby's avatar
Nicolas Delaby committed
53
from Products.ERP5Form import ProxyField
54
from DateTime import DateTime
55

56 57
from Products.Formulator.Widget import NSMAP
ODG_XML_WRAPPING_XPATH = 'draw:text-box/text:p/text:span'
58

Nicolas Delaby's avatar
Nicolas Delaby committed
59
class TestRenderViewAPI(ERP5TypeTestCase):
60 61 62 63 64 65 66 67 68
  """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
69
      self.assertEquals(('self', 'value', 'REQUEST', 'render_prefix'),
70 71 72
                        field.render_view.im_func.func_code.co_varnames)
      if field is not ProxyField.ProxyField:
        self.assertEquals(('self', 'field', 'value', 'REQUEST'),
73
          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]))
74 75


Nicolas Delaby's avatar
Nicolas Delaby committed
76
class TestFloatField(ERP5TypeTestCase):
77 78
  """Tests Float field
  """
79 80 81 82

  def getTitle(self):
    return "Float Field"

Nicolas Delaby's avatar
Nicolas Delaby committed
83
  def afterSetUp(self):
84 85
    self.field = FloatField('test_field')
    self.widget = self.field.widget
86
    self.validator = self.field.validator
87

88
  def test_format_thousand_separator_point(self):
89 90
    self.field.values['input_style'] = '-1 234.5'
    self.assertEquals('1 000.0', self.widget.format_value(self.field, 1000))
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  
  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))
108 109
    self.assertEquals('-100,000.0', self.widget.format_value(self.field, -100000))

110 111 112 113 114 115 116
  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))
117 118
    # value is rounded
    self.assertEquals('13', self.widget.format_value(self.field, 12.9))
119

120
    purgeFieldValueCache() # call this before changing internal field values.
121 122 123 124
    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))
Jérome Perrin's avatar
Jérome Perrin committed
125 126 127 128
    self.assertEquals('1.00',
        self.widget.format_value(self.field, sum([0.1] * 10)))
    self.assertEquals('566.30',
        self.widget.format_value(self.field, 281.80 + 54.50 + 230.00))
129
  
130 131 132 133 134
  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))
135 136 137 138 139 140 141 142

  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))
143 144 145 146 147 148
  
  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"))
149

150 151 152 153 154 155
  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"))

156 157 158 159 160 161
  def test_render_big_numbers(self):
    self.field.values['precision'] = 2
    self.field.values['editable'] = 0
    self.assertEquals('10000000000000000000.00',
                      self.field.render(10000000000000000000))

162 163
  def test_validate_thousand_separator_point(self):
    self.field.values['input_style'] = '-1 234.5'
Nicolas Delaby's avatar
Nicolas Delaby committed
164
    self.portal.REQUEST.set('field_test_field', '1 000.0')
165
    self.assertEquals(1000,
Nicolas Delaby's avatar
Nicolas Delaby committed
166
        self.validator.validate(self.field, 'field_test_field', self.portal.REQUEST))
167 168 169
  
  def test_validate_thousand_separator_coma(self):
    self.field.values['input_style'] = '-1 234,5'
Nicolas Delaby's avatar
Nicolas Delaby committed
170
    self.portal.REQUEST.set('field_test_field', '1 000,0')
171
    self.assertEquals(1000,
Nicolas Delaby's avatar
Nicolas Delaby committed
172
        self.validator.validate(self.field, 'field_test_field', self.portal.REQUEST))
173 174 175

  def test_validate_thousand_separator_point_coma(self):
    self.field.values['input_style'] = '-1.234,5'
Nicolas Delaby's avatar
Nicolas Delaby committed
176
    self.portal.REQUEST.set('field_test_field', '1.000,0')
177
    self.assertEquals(1000,
Nicolas Delaby's avatar
Nicolas Delaby committed
178
        self.validator.validate(self.field, 'field_test_field', self.portal.REQUEST))
179 180 181

  def test_validate_thousand_separator_coma_point(self):
    self.field.values['input_style'] = '-1,234.5'
Nicolas Delaby's avatar
Nicolas Delaby committed
182
    self.portal.REQUEST.set('field_test_field', '1,000.0')
183
    self.assertEquals(1000,
Nicolas Delaby's avatar
Nicolas Delaby committed
184
        self.validator.validate(self.field, 'field_test_field', self.portal.REQUEST))
185 186 187

  def test_validate_percent_style(self):
    self.field.values['input_style'] = '-12.3%'
Nicolas Delaby's avatar
Nicolas Delaby committed
188
    self.portal.REQUEST.set('field_test_field', '10.0%')
189
    self.assertEquals(0.1,
Nicolas Delaby's avatar
Nicolas Delaby committed
190
        self.validator.validate(self.field, 'field_test_field', self.portal.REQUEST))
191 192

  def test_validate_not_float(self):
Nicolas Delaby's avatar
Nicolas Delaby committed
193
    self.portal.REQUEST.set('field_test_field', 'not_float')
194
    self.assertRaises(ValidationError,
Nicolas Delaby's avatar
Nicolas Delaby committed
195
        self.validator.validate, self.field, 'field_test_field', self.portal.REQUEST)
196 197 198

  def test_validate_two_comma(self):
    self.field.values['input_style'] = '-1.234,5'
Nicolas Delaby's avatar
Nicolas Delaby committed
199
    self.portal.REQUEST.set('field_test_field', '1,000,0')
200
    self.assertRaises(ValidationError,
Nicolas Delaby's avatar
Nicolas Delaby committed
201
        self.validator.validate, self.field, 'field_test_field', self.portal.REQUEST)
202 203 204

  def test_validate_two_dots(self):
    self.field.values['input_style'] = '-1,234.5'
Nicolas Delaby's avatar
Nicolas Delaby committed
205
    self.portal.REQUEST.set('field_test_field', '1.000.0')
206
    self.assertRaises(ValidationError,
Nicolas Delaby's avatar
Nicolas Delaby committed
207
        self.validator.validate, self.field, 'field_test_field', self.portal.REQUEST)
208

209 210 211 212 213 214 215 216 217 218 219 220
  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)

Nicolas Delaby's avatar
Nicolas Delaby committed
221
class TestStringField(ERP5TypeTestCase):
222 223
  """Tests string field
  """
224 225 226 227

  def getTitle(self):
    return "String Field"

Nicolas Delaby's avatar
Nicolas Delaby committed
228
  def afterSetUp(self):
229 230 231 232 233 234 235
    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>"))

236 237 238 239 240 241 242 243 244 245 246 247 248 249
  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)

Nicolas Delaby's avatar
Nicolas Delaby committed
250
class TestDateTimeField(ERP5TypeTestCase):
251 252 253 254 255 256
  """Tests DateTime field
  """

  def getTitle(self):
    return "DateTime Field"

Nicolas Delaby's avatar
Nicolas Delaby committed
257
  def afterSetUp(self):
258 259 260 261 262 263 264 265
    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)

Nicolas Delaby's avatar
Nicolas Delaby committed
266
class TestTextAreaField(ERP5TypeTestCase):
267 268 269 270 271 272
  """Tests TextArea field
  """

  def getTitle(self):
    return "TextArea Field"

Nicolas Delaby's avatar
Nicolas Delaby committed
273
  def afterSetUp(self):
274 275 276
    self.field = TextAreaField('test_field')
    self.widget = self.field.widget

277 278 279 280 281 282 283 284 285 286 287 288
  def test_render_view(self):
    self.field.values['default'] = 'My first Line\n&My Second Line\tfoo'
    self.assertEquals('<div  >My first Line<br/><br/>&amp;My Second Line\tfoo</div>',
                      self.field.render_view(value=['My first Line\n', '&My Second Line\tfoo']))
    editable_mode = self.portal.REQUEST.get('editable_mode', 1)
    self.portal.REQUEST.set('editable_mode', 0)
    try:
      self.assertEquals('<div  >My first Line<br/>&amp;My Second Line\tfoo</div>',
                        self.field.render(REQUEST=self.portal.REQUEST))
    finally:
      self.portal.REQUEST.set('editable_mode', editable_mode)

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
  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)
304

305 306 307 308 309 310 311 312 313 314 315 316 317
class TestLinesField(ERP5TypeTestCase):

  def getTitle(self):
    return "Lines Field"

  def afterSetUp(self):
    self.field = LinesField('test_field')
    self.widget = self.field.widget

  def test_render_view(self):
    self.assertEquals(self.field.render_view(value=['My first Line\n', '&My Second Line\tfoo']),
                      '<div  >My first Line<br />\n<br />\n&amp;My Second Line\tfoo</div>')

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
  def test_render_odt(self):
    self.field.values['default'] = ['A', 'B']
    self.assertEquals('{%(text)s}p' % NSMAP,
                      self.field.render_odt(as_string=False).tag)

  def test_render_odt_view(self):
    self.field.values['default'] = ['A', 'B']
    element = self.field.render_odt(as_string=False,
                                    REQUEST=self.portal.REQUEST)
    self.assertEquals('{%(text)s}p' % NSMAP, element.tag)
    # separated by text:line-break
    self.assertEquals('{%(text)s}line-break' % NSMAP, element[0].tag)
    self.assertEquals(['A', 'B'], [x for x in element.itertext()])


Nicolas Delaby's avatar
Nicolas Delaby committed
333
class TestCheckBoxField(ERP5TypeTestCase):
Nicolas Delaby's avatar
Nicolas Delaby committed
334 335 336 337 338 339
  """Tests TextArea field
  """

  def getTitle(self):
    return "CheckBox Field"

Nicolas Delaby's avatar
Nicolas Delaby committed
340
  def afterSetUp(self):
Nicolas Delaby's avatar
Nicolas Delaby committed
341 342 343 344 345
    self.field = CheckBoxField('test_field')
    self.widget = self.field.widget

  def test_render_odt(self):
    self.field.values['default'] = 1
346
    self.assertEquals('{%(form)s}checkbox' % NSMAP,
Nicolas Delaby's avatar
Nicolas Delaby committed
347 348 349 350
                      self.field.render_odt(as_string=False).tag)

  def test_render_odt_view(self):
    self.field.values['default'] = 1
Nicolas Delaby's avatar
Nicolas Delaby committed
351
    self.portal.REQUEST.set('editable_mode', 0)
352
    self.assertEquals('{%(text)s}p' % NSMAP,
Nicolas Delaby's avatar
Nicolas Delaby committed
353 354
                      self.field.render_odt(as_string=False, REQUEST=self.portal.REQUEST).tag)
    self.assertEquals('1', self.field.render_odt(as_string=False, REQUEST=self.portal.REQUEST).text)
Nicolas Delaby's avatar
Nicolas Delaby committed
355

356
class TestListField(ERP5TypeTestCase):
357 358 359 360 361 362
  """Tests List field
  """

  def getTitle(self):
    return "List Field"

363 364 365 366 367 368 369 370 371
  def getBusinessTemplateList(self):
    """
    Tuple of Business Templates we need to install
    """
    return (
      'erp5_base',
    )

  def afterSetUp(self):
372 373
    self.field = ListField('test_field')
    self.widget = self.field.widget
374 375 376 377 378
    self.createCategories()
    transaction.commit()
    self.tic()

  def createCategories(self):
379
    """Create some categories into gender
380 381 382
    """
    category_tool = self.portal.portal_categories
    if len(category_tool.gender.contentValues()) == 0 :
Jérome Perrin's avatar
Jérome Perrin committed
383 384 385 386 387 388 389 390
      category_tool.gender.newContent(portal_type='Category',
                                      id='male',
                                      title='Male',
                                      int_index=1)
      category_tool.gender.newContent(portal_type='Category',
                                      id='female',
                                      title='Female',
                                      int_index=2)
391 392

  def test_render_odt(self):
393 394 395 396 397 398 399 400 401 402 403 404
    items = [('My first Line', '1'), ('My Second Line', '2')]
    self.field.values['items'] = items
    self.field.values['default'] = '2'
    element = self.field.render_odt(as_string=False)
    self.assertEquals('{%(text)s}p' % NSMAP, element.tag)
    self.assertEquals('My Second Line', element.text)

    # values not in items are displayed with ???
    self.field.values['default'] = '3'
    element = self.field.render_odt(as_string=False)
    self.assertEquals('??? (3)', element.text)

405

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
  def test_listField_value_order(self):
    '''This test check the list field value order
    '''
    # create a form with a list_field that use gender category
    portal_skins = self.getSkinsTool()
    skin_folder = portal_skins._getOb('custom')
    skin_folder.manage_addProduct['ERP5Form'].addERP5Form(
        'Base_viewTestFieldValueOrder',
        'View')
    form = skin_folder._getOb('Base_viewTestFieldValueOrder', None)

    # The field is a proxyfield on Base_viewFieldLibrary.my_category that
    # category should be sort on int_index and translated_id
    form.manage_addField('my_gender', 'Test List Field',
        'ProxyField')
    field = getattr(form, 'my_gender')
    field.manage_edit_xmlrpc(dict(
    form_id='Base_viewFieldLibrary', field_id='my_category'))

    category_item_list = field.get_value('items')
    self.assertEquals(category_item_list,
Jérome Perrin's avatar
Jérome Perrin committed
427
        [['', ''], ['Male', 'male'], ['Female', 'female']])
428 429 430 431

    # try on a person to select on gender and check if the result is the same
    person_module = self.portal.getDefaultModule('Person')
    person = person_module.newContent(portal_type='Person')
Jérome Perrin's avatar
Jérome Perrin committed
432 433
    person.setGender('female')
    self.assertEquals(person.getGender(), 'female')
434
    self.assertEquals(person.Person_view.my_gender.get_value('items'),
Jérome Perrin's avatar
Jérome Perrin committed
435
        [['', ''], ['Male', 'male'], ['Female', 'female']])
436

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

class TestMultiListField(ERP5TypeTestCase):

  def afterSetUp(self):
    self.field = MultiListField('test_field')
    self.widget = self.field.widget
    self.field.values['items'] = [('A', 'a',), ('B', 'b')]
    self.field.values['default'] = ['a', 'b']

  def test_render_view(self):
    self.assertEquals('A<br />\nB', self.field.render_view(value=['a', 'b']))

  def test_render_odt(self):
    element = self.field.render_odt(as_string=False)
    self.assertEquals('{%(text)s}p' % NSMAP, element.tag)
    # separated by text:line-break
    self.assertEquals('{%(text)s}line-break' % NSMAP, element[0].tag)
    self.assertEquals(['A', 'B'], [x for x in element.itertext()])

  def test_render_odt_view(self):
    element = self.field.render_odt_view(as_string=False,
                                        value=['a', 'b'],
                                        REQUEST=self.portal.REQUEST)
    self.assertEquals('{%(text)s}p' % NSMAP, element.tag)
    # separated by text:line-break
    self.assertEquals('{%(text)s}line-break' % NSMAP, element[0].tag)
    self.assertEquals(['A', 'B'], [x for x in element.itertext()])

    # values not in items are displayed with ???
    element = self.field.render_odt_view(as_string=False,
                                        value=['other'],
                                        REQUEST=self.portal.REQUEST)
    self.assertEquals('{%(text)s}p' % NSMAP, element.tag)
    self.assertEquals('??? (other)', element.text)

472
class TestProxyField(ERP5TypeTestCase):
473 474 475 476

  def getTitle(self):
    return "Proxy Field"

Nicolas Delaby's avatar
Nicolas Delaby committed
477
  def afterSetUp(self):
478
    self.container = Folder('container').__of__(self.portal)
479 480 481 482
    self.container._setObject('Base_viewProxyFieldLibrary',
                               ERP5Form('Base_viewProxyFieldLibrary', 'Proxys'))
    self.container._setObject('Base_view',
                               ERP5Form('Base_view', 'View'))
483 484 485 486 487 488
    try:
        from Products.CMFCore.tests.base.utils import _setUpDefaultTraversable
        _setUpDefaultTraversable()
    except ImportError:
        pass # On Zope 2.8, remove when we no longer support it

489 490 491 492 493 494

  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
495 496

  def test_get_template_field(self):
497 498 499 500
    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')
501
    self.assertEquals(None, proxy_field.getTemplateField())
502 503
    self.assertEquals(None, proxy_field.get_value('enable'))
    self.assertEquals(None, proxy_field.get_value('default'))
504

505 506 507 508 509
    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):
510 511
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
512 513
    self.assertEquals('Title', original_field.get_value('title'))

514 515
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Not Title', 'ProxyField')
516 517 518 519 520
    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'))

521
  def test_simple_not_surcharge(self):
522 523
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
524 525
    self.assertEquals('Title', original_field.get_value('title'))

526 527
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Proxy Title', 'ProxyField')
528 529 530 531 532 533 534 535
    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'))

536 537 538
  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
539 540 541 542
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
    proxy_field = self.addField(self.container.Base_view,
                                'my_id', 'ID', 'ProxyField')
543 544 545 546 547
    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'))

548
  def test_field_tales_context(self):
549 550
    # in the TALES context, "field" will be the proxyfield, not the original
    # field.
551 552
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
553 554 555
    original_field.manage_tales_xmlrpc(dict(title='field/getId'))
    self.assertEquals('my_title', original_field.get_value('title'))

556 557
    proxy_field = self.addField(self.container.Base_view,
                                'my_reference', 'Not Title', 'ProxyField')
558 559 560 561 562
    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'))

563 564 565
  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).
566 567
    original_field = self.addField(self.container.Base_viewProxyFieldLibrary,
                                   'my_title', 'Title', 'StringField')
568 569 570 571
    original_field.manage_tales_xmlrpc(dict(title='form/getId'))
    self.assertEquals('Base_viewProxyFieldLibrary',
                       original_field.get_value('title'))

572 573
    proxy_field = self.addField(self.container.Base_view,
                                'my_title', 'Title', 'ProxyField')
574 575 576
    proxy_field.manage_edit_xmlrpc(dict(form_id='Base_viewProxyFieldLibrary',
                                        field_id='my_title',))
    self.assertEquals('Base_view', proxy_field.get_value('title'))
577

578 579 580 581 582 583 584 585 586 587
  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'))
588
    proxy_field.manage_tales_xmlrpc(dict(field_id='request/field_id'))
589 590 591 592 593 594 595 596 597

    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'))

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
  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())
    
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  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
635 636 637 638 639 640 641 642
  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
643
                                 'my_another_string', '', 'ProxyField')
Yusei Tahara's avatar
Yusei Tahara committed
644 645
    # Use same id to the template field.
    proxy_field1 = self.addField(self.container.Base_view,
Yusei Tahara's avatar
Yusei Tahara committed
646
                                 'my_string', '', 'ProxyField')
Yusei Tahara's avatar
Yusei Tahara committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
    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'))

666 667 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 703 704 705 706 707 708 709 710 711 712 713 714
  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()

715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
  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)
744

745
class TestFieldValueCache(ERP5TypeTestCase):
746 747
  """Tests field value caching system
  """
748 749 750 751

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

Nicolas Delaby's avatar
Nicolas Delaby committed
752
  def afterSetUp(self):
753
    self.root = self.portal
754
    self.root.form = ERP5Form('form', 'Form')
755 756
    self.root.getProperty = lambda key, d=None: \
      dict(on_memory_field='123').get(key, d)
757 758 759 760

    form = self.root.form
    form.field = StringField('test_field')
    form.field._p_oid = makeDummyOid()
761
    # method field
762 763
    form.field.values['external_validator'] = Method('this_is_a_method')
    # on-memory field (not in zodb)
764 765 766 767 768 769
    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
770 771 772 773 774 775 776 777 778 779
    # 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')
780 781 782 783 784 785
    # 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()
786 787

  def test_method_field(self):
788
    field = self.root.form.field
789
    value, cacheable = getFieldValue(field, field, 'external_validator')
790 791 792
    self.assertEqual(False, value.value is field.values['external_validator'])
    self.assertEqual(True, type(value.value) is Method)

793 794 795 796 797 798 799 800 801 802
  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)
803 804 805 806
    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'))
807 808 809 810 811 812 813 814 815 816 817 818 819 820
    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))

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
  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))
856

857 858 859 860 861
def makeDummyOid():
  import time, random
  return '%s%s' % (time.time(), random.random())


862 863
def test_suite():
  suite = unittest.TestSuite()
864
  suite.addTest(unittest.makeSuite(TestRenderViewAPI))
865 866
  suite.addTest(unittest.makeSuite(TestFloatField))
  suite.addTest(unittest.makeSuite(TestStringField))
867 868
  suite.addTest(unittest.makeSuite(TestDateTimeField))
  suite.addTest(unittest.makeSuite(TestTextAreaField))
869
  suite.addTest(unittest.makeSuite(TestLinesField))
Nicolas Delaby's avatar
Nicolas Delaby committed
870
  suite.addTest(unittest.makeSuite(TestCheckBoxField))
871
  suite.addTest(unittest.makeSuite(TestListField))
872
  suite.addTest(unittest.makeSuite(TestMultiListField))
873
  suite.addTest(unittest.makeSuite(TestProxyField))
874
  suite.addTest(unittest.makeSuite(TestFieldValueCache))
875
  return suite