##############################################################################
#
# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
#          Yoshinori Okuji <yo@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.
#
##############################################################################

import os, sys
if __name__ == '__main__':
  execfile(os.path.join(sys.path[0], 'framework.py'))

from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from AccessControl.SecurityManagement import newSecurityManager
from zLOG import LOG
from Products.ERP5Type.tests.Sequence import SequenceList
from Testing import ZopeTestCase
from Products.ERP5Type.Utils import get_request
from Products.ERP5Type.tests.utils import createZODBPythonScript
from ZPublisher.HTTPRequest import FileUpload
from StringIO import StringIO
from Products.ERP5Form.Selection import Selection
from Products.ERP5Form.Form import ERP5Form


class DummyFieldStorage:
  """A dummy FieldStorage to be wrapped in a FileUpload object.
  """
  def __init__(self):
    self.file = StringIO()
    self.filename = '<dummy field storage>'
    self.headers = {}

class TestListBox(ERP5TypeTestCase):
  """
    Test the API of ListBox. The user-visible aspect is tested
    by functional testing.
  """
  quiet = 1
  run_all_test = 1

  def getBusinessTemplateList(self):
    # Use the same framework as the functional testing for convenience.
    # This adds some specific portal types and skins.
    return ('erp5_ui_test',)

  def getTitle(self):
    return "ListBox"

  def afterSetUp(self):
    self.login()

  def login(self):
    uf = self.getPortal().acl_users
    uf._doAddUser('seb', '', ['Manager'], [])
    user = uf.getUserById('seb').__of__(uf)
    newSecurityManager(None, user)

  def stepTic(self,**kw):
    self.tic()

  def stepCreateObjects(self, sequence = None, sequence_list = None, **kw):
    # Make sure that the status is clean.
    portal = self.getPortal()
    portal.ListBoxZuite_reset()

    message = portal.foo_module.FooModule_createObjects()
    self.failUnless('Created Successfully' in message)

  def stepModifyListBoxForStat(self, sequence = None, sequence_list = None, **kw):
    portal = self.getPortal()
    listbox = portal.FooModule_viewFooList.listbox
    message = listbox.ListBox_setPropertyList(
      field_stat_columns = 'id|FooModule_statId\ntitle|FooModule_statTitle',
      field_stat_method = 'portal_catalog')
    self.failUnless('Set Successfully' in message)

  def stepRenderList(self, sequence = None, sequence_list = None, **kw):
    portal = self.getPortal()
    listbox = portal.FooModule_viewFooList.listbox
    request = get_request()
    request['here'] = portal.foo_module
    listboxline_list = listbox.get_value('default', render_format = 'list',
                                         REQUEST = request)
    sequence.edit(listboxline_list = listboxline_list)

  def stepCheckListBoxLineListWithStat(self, sequence = None, sequence_list = None, **kw):
    line_list = sequence.get('listboxline_list')
    self.assertEqual(len(line_list), 12)

    title_line = line_list[0]
    self.failUnless(title_line.isTitleLine())
    self.assertEqual(len(title_line.getColumnItemList()), 3)
    result = (('id', 'ID'), ('title', 'Title'), ('getQuantity', 'Quantity'))
    for i, (key, value) in enumerate(title_line.getColumnItemList()):
      self.assertEqual(key, result[i][0])
      self.assertEqual(value, result[i][1])

    for n, data_line in enumerate(line_list[1:-1]):
      self.failUnless(data_line.isDataLine())
      self.assertEqual(len(data_line.getColumnItemList()), 3)
      result = (('id', str(n)), ('title', 'Title %d' % n), ('getQuantity', str(10.0 - n)))
      for i, (key, value) in enumerate(data_line.getColumnItemList()):
        self.assertEqual(key, result[i][0])
        self.assertEqual(str(value).strip(), result[i][1])

    stat_line = line_list[-1]
    self.failUnless(stat_line.isStatLine())
    self.assertEqual(len(stat_line.getColumnItemList()), 3)
    result = (('id', 'foo_module'), ('title', 'Foos'), ('getQuantity', 'None'))
    for i, (key, value) in enumerate(stat_line.getColumnItemList()):
      self.assertEqual(key, result[i][0])
      self.assertEqual(str(value).strip(), result[i][1])

  def test_01_CheckListBoxLinesWithStat(self, quiet=quiet, run=run_all_test):
    if not run: return
    if not quiet:
      message = 'Test ListBoxLines With Statistics'
      ZopeTestCase._print('\n%s ' % message)
      LOG('Testing... ', 0, message)
    sequence_list = SequenceList()
    sequence_string = '\
                       CreateObjects \
                       Tic \
                       ModifyListBoxForStat \
                       RenderList \
                       CheckListBoxLineListWithStat \
                       '
    sequence_list.addSequenceString(sequence_string)
    sequence_list.play(self, quiet=quiet)

  def test_02_DefaultSort(self, quiet=quiet, run=run_all_test):
    """Defaults sort parameters must be passed to the list method, under the
    'sort_on' key.
    """
    portal = self.getPortal()
    portal.ListBoxZuite_reset()

    # We create a script to use as a list method, in this script, we will check
    # the sort_on parameter.
    list_method_id = 'ListBox_checkSortOnListMethod'
    createZODBPythonScript(
        portal.portal_skins.custom,
        list_method_id,
        'selection=None, sort_on=None, **kw',
r"""
if sort_on != [('title', 'ASC'), ('uid', 'ASC')]:
  raise AssertionError('sort_on is %r' % sort_on)
return []
""")
 
    # set the listbox to use this as list method
    listbox = portal.FooModule_viewFooList.listbox
    listbox.ListBox_setPropertyList(
      field_list_method = list_method_id,
      field_count_method = '',
      field_sort = 'title | ASC\n'
                   'uid | ASC',)
    
    # render the listbox, checks are done by list method itself
    request = get_request()
    request['here'] = portal.foo_module
    listbox.get_value('default', render_format='list', REQUEST=request)

  def test_03_DefaultParameters(self, quiet=quiet, run=run_all_test):
    """Defaults parameters are passed as keyword arguments to the list method
    """
    portal = self.getPortal()
    portal.ListBoxZuite_reset()

    # We create a script to use as a list method, in this script, we will check
    # the default parameter.
    list_method_id = 'ListBox_checkDefaultParametersListMethod'
    createZODBPythonScript(
        portal.portal_skins.custom,
        list_method_id,
        'selection=None, dummy_default_param=None, **kw',
"""
if dummy_default_param != 'dummy value':
  raise AssertionError('recieved wrong arguments: %s instead of "dummy value"'
                        % dummy_default_param )
return []
""")
 
    # set the listbox to use this as list method
    listbox = portal.FooModule_viewFooList.listbox
    listbox.ListBox_setPropertyList(
      field_list_method = list_method_id,
      field_count_method = '',
      field_default_params = 'dummy_default_param | dummy value',)
    
    # render the listbox, checks are done by list method itself
    request = get_request()
    request['here'] = portal.foo_module
    listbox.get_value('default', render_format='list', REQUEST=request)

  def test_04_UnicodeParameters(self, quiet=0, run=run_all_test):
    """Unicode properties are handled. 
    """
    portal = self.getPortal()
    portal.ListBoxZuite_reset()
    
    # We create a script to use as a list method
    list_method_id = 'ListBox_ParametersListMethod'
    createZODBPythonScript(
        portal.portal_skins.custom,
        list_method_id,
        'selection=None, **kw',
        """return [context.asContext(alternate_title = u'\xe9lisa')]""")
 
    # set the listbox to use this as list method
    listbox = portal.FooModule_viewFooList.listbox
    listbox.ListBox_setPropertyList(
      field_list_method = list_method_id,
      field_count_method = '',
      field_columns = ['alternate_title | Alternate Title',],)
    
    request = get_request()
    request['here'] = portal.foo_module
    try:
      listbox.get_value('default', render_format='list', REQUEST=request)
    except UnicodeError, e:
      self.fail('Rendering failed: %s' % e)

  def test_05_EditSelectionWithFileUpload(self, quiet=quiet, run=run_all_test):
    """Listbox edits selection with request parameters. Special care must be
    taken for FileUpload objects that cannot be pickled, thus cannot be stored
    in the ZODB.
    """
    portal = self.getPortal()
    portal.ListBoxZuite_reset()
    listbox = portal.FooModule_viewFooList.listbox
    # XXX isn't Selection automatically created ?
    portal.portal_selections.setSelectionFor(
          listbox.get_value('selection_name'), Selection())

    request = get_request()
    request['here'] = portal.foo_module
    request.form['my_file_upload'] = FileUpload(DummyFieldStorage())
    listbox.get_value('default', render_format='list', REQUEST=request)
    try:
      get_transaction().commit()
    except TypeError, e:
      self.fail('Unable to commit transaction: %s' % e)

  def test_06_LineFields(self, quiet=0, run=run_all_test):
    """
       Line Fields are able to render a list parameter in the form
       of lines. The same behaviour is expected for Line Fields used
       in ListBox objects.
    """
    portal = self.getPortal()
    portal.ListBoxZuite_reset()

    # Reset listbox properties
    listbox = portal.FooModule_viewFooList.listbox
    listbox.ListBox_setPropertyList(
      field_list_method = 'portal_catalog',
      field_columns = ['subject_list | Subjects',],
      field_editable_columns = ['subject_list | Subjects',],
    )

    # Create an new empty object with a list property
    foo_module = portal.foo_module
    word = 'averycomplexwordwhichhaslittlechancetoexistinhtml'
    o = foo_module.newContent(subject_list = [word])

    # Make sure that word is the subject list
    self.assertEqual(word in o.getSubjectList(), True)

    # Reindex
    o.immediateReindexObject()

    # Render the module in html
    request = get_request()
    request['here'] = portal.foo_module
    rendered_listbox = listbox.render(REQUEST=request)

    # Make sure that word is there
    self.assertEqual(rendered_listbox.find(word) > 0, True)

  def test_ObjectSupport(self):
    # make sure listbox supports rendering of simple objects
    # the only requirement is that objects have a `uid` attribute which is a
    # string starting by new_ (a convention to prevent indexing of objects).
    portal = self.getPortal()
    list_method_id = 'DummyListMethodId'
    portal.ListBoxZuite_reset()
    form = portal.FooModule_viewFooList
    listbox = form.listbox
    listbox.ListBox_setPropertyList(
      field_list_method = list_method_id,
      field_count_method = '',
      field_editable_columns = ['title | title'],
      field_columns = ['title | Title',],)
    form.manage_addField('listbox_title', 'Title', 'StringField')
    
    createZODBPythonScript(
        portal.portal_skins.custom,
        list_method_id,
        'selection=None, **kw',
        "from Products.PythonScripts.standard import Object\n"
        "return [Object(uid='new_', title='Object Title')]")
    
    request = get_request()
    request['here'] = portal.foo_module
    line_list = [l for l in listbox.get_value('default',
                               render_format='list',
                               REQUEST=request) if l.isDataLine()]
    self.assertEquals(1, len(line_list))
    self.assertEquals('Object Title', line_list[0].getColumnProperty('title'))
    html = listbox.render(REQUEST=request)
    self.failUnless('Object Title' in html, html)

  def test_ProxyFieldRenderFormatLines(self):
    # tests that listbox default value in render_format=list mode is
    # compatible with proxy field.
    portal = self.getPortal()
    portal.ListBoxZuite_reset()
    form = portal.FooModule_viewFooList
    listbox = form.listbox
    listbox.ListBox_setPropertyList(
      field_list_method='contentValues',
      field_columns=['listbox_value | Title',],)
    
    # create a form, to store our proxy field inside
    portal._setObject('Test_view',
                      ERP5Form('Test_view', 'View'))
    portal.Test_view.manage_addField('listbox', 'listbox', 'ProxyField')
    proxy_field = portal.Test_view.listbox
    proxy_field.manage_edit_xmlrpc(dict(
            form_id=form.getId(), field_id='listbox',
            columns=[('proxy_value', 'Proxy')]))

    # this proxy field will not delegate its "columns" value
    proxy_field._surcharged_edit(dict(columns=[('proxy_value', 'Proxy')]),
                                ['columns'])
    
    request = get_request()
    request['here'] = portal.foo_module
    line_list = proxy_field.get_value('default',
                      render_format='list', REQUEST=request)
    self.failUnless(isinstance(line_list, list))

    title_line = line_list[0]
    self.failUnless(title_line.isTitleLine())

    # title of columns is the value overloaded by the proxy field.
    self.assertEquals([('proxy_value', 'Proxy')],
                      title_line.getColumnItemList())


if __name__ == '__main__':
  framework()
else:
  import unittest
  def test_suite():
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(TestListBox))
    return suite