################################################################################ # # Copyright (c) 2002-2005, Benjamin Saller <bcsaller@ideasuite.com>, and # the respective authors. All rights reserved. # For a list of Archetypes contributors see docs/CREDITS.txt. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names of its contributors may be used # to endorse or promote products derived from this software without specific # prior written permission. # # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ################################################################################ from unittest import TestSuite, makeSuite import os import PIL from zope.annotation.interfaces import IAttributeAnnotatable from zope.interface import implements, alsoProvides from zope.component import getSiteManager from zope.publisher.browser import TestRequest from zope.schema.interfaces import IVocabularyFactory from zope.schema.vocabulary import SimpleVocabulary from Products.Archetypes.tests.atsitetestcase import ATSiteTestCase from Products.Archetypes.tests.atsitetestcase import portal_name from Products.Archetypes.tests.utils import mkDummyInContext from Products.Archetypes.tests.utils import PACKAGE_HOME from Products.Archetypes.atapi import Schema, DisplayList, IntDisplayList, BaseContentMixin, TextField from Products.Archetypes.interfaces import IFieldDefaultProvider from Products.Archetypes.interfaces.vocabulary import IVocabulary from Products.Archetypes import Field as at_field from Products import PortalTransforms from OFS.Image import File, Image from DateTime import DateTime test_fields = [ ('ObjectField', 'objectfield'), ('StringField', 'stringfield'), ('FileField', 'filefield'), ('TextField', 'textfield'), ('DateTimeField', 'datetimefield'), ('LinesField', 'linesfield'), ('IntegerField', 'integerfield'), ('FloatField', 'floatfield'), ('FloatField', 'floatfield2'), ('FixedPointField', 'fixedpointfield1'), ('FixedPointField', 'fixedpointfield2'), ('BooleanField', 'booleanfield'), ('ImageField', 'imagefield'), ] field_instances = [] for type, name in test_fields: field_instances.append(getattr(at_field, type)(name)) txt_file = open(os.path.join(PACKAGE_HOME, 'input', 'rest1.rst')) txt_content = txt_file.read() img_file = open(os.path.join(PACKAGE_HOME, 'input', 'tool.gif'), 'rb') img_content = img_file.read() animated_gif_file = open(os.path.join(PACKAGE_HOME, 'input', 'animated.gif'), 'rb') animated_gif_content = animated_gif_file.read() pdf_file = open(os.path.join(PACKAGE_HOME, 'input', 'webdav.pdf'), 'rb') pdf_content = pdf_file.read() field_values = { 'objectfield': 'objectfield', 'stringfield': 'stringfield', 'filefield_file': txt_file, 'textfield': 'textfield', 'datetimefield': '', 'datetimefield_year': '2003', 'datetimefield_month': '01', 'datetimefield_day': '01', 'datetimefield_hour': '03', 'datetimefield_minute': '04', 'linesfield': 'bla\nbla', 'integerfield': '1', 'floatfield': '1.5', 'floatfield2': '1,2', 'fixedpointfield1': '1.5', 'fixedpointfield2': '1,5', 'booleanfield': '1', 'imagefield_file': img_file, } expected_values = { 'objectfield': 'objectfield', 'stringfield': 'stringfield', 'filefield': txt_content, 'textfield': 'textfield', 'datetimefield': DateTime(2003, 01, 01, 03, 04), 'linesfield': ('bla', 'bla'), 'integerfield': 1, 'floatfield': 1.5, 'floatfield2': 1.2, 'fixedpointfield1': '1.50', 'fixedpointfield2': '1.50', 'booleanfield': 1, 'imagefield': '<img src="%s/dummy/imagefield" alt="Spam" title="Spam" height="16" width="16" />' % portal_name } empty_values = { 'objectfield': None, 'stringfield': '', 'filefield': None, 'textfield': '', 'datetimefield': '2007-00-00', 'linesfield': (), 'integerfield': None, 'floatfield': None, 'floatfield2': None, 'fixedpointfield1': None, 'fixedpointfield2': None, 'booleanfield': None, } schema = Schema(tuple(field_instances)) sampleDisplayList = DisplayList([('e1', 'e1'), ('element2', 'element2')]) class sampleInterfaceVocabulary: implements(IVocabulary) def getDisplayList(self, instance): return sampleDisplayList class Dummy(BaseContentMixin): def Title(self): # required for ImageField return 'Spam' def aMethod(self): return sampleDisplayList def default_val(self): return "World" class DummyVocabulary(object): implements(IVocabularyFactory) def __call__(self, context): return SimpleVocabulary.fromItems([("title1", "value1"), ("t2", "v2")]) DummyVocabFactory = DummyVocabulary() class DummyIntVocabulary(object): implements(IVocabularyFactory) def __call__(self, context): return SimpleVocabulary.fromItems([("title1", 1), ("t2", 2)]) DummyIntVocabFactory = DummyIntVocabulary() class FakeRequest: def __init__(self): self.other = {} self.form = {} class ProcessingTest(ATSiteTestCase): def afterSetUp(self): self.setRoles(['Manager']) ATSiteTestCase.afterSetUp(self) self._dummy = mkDummyInContext(Dummy, oid='dummy', context=self.portal, schema=schema) txt_file.seek(0) img_file.seek(0) pdf_file.seek(0) def makeDummy(self): return self._dummy def test_processing(self): dummy = self.makeDummy() request = FakeRequest() request.form.update(field_values) dummy.REQUEST = request dummy.processForm(data=1) for k, v in expected_values.items(): got = dummy.getField(k).get(dummy) if isinstance(got, File): got = str(got) self.assertEqual(got, v, 'got: %r, expected: %r, field "%s"' % (got, v, k)) def test_processing_fieldset(self): dummy = self.makeDummy() request = FakeRequest() request.form.update(field_values) request.form['fieldset'] = 'default' dummy.REQUEST = request dummy.processForm() for k, v in expected_values.items(): got = dummy.getField(k).get(dummy) if isinstance(got, (File, Image)): got = str(got) self.assertEqual(got, v, 'got: %r, expected: %r, field "%s"' % (got, v, k)) def test_image_tag(self): dummy = self.makeDummy() request = FakeRequest() request.form.update(field_values) request.form['fieldset'] = 'default' dummy.REQUEST = request dummy.processForm() image_field = dummy.getField('imagefield') self.assertEqual(image_field.tag(dummy), '<img src="%s/dummy/imagefield" alt="Spam" title="Spam" height="16" width="16" />' % portal_name) self.assertEqual(image_field.tag(dummy, alt=''), '<img src="%s/dummy/imagefield" alt="" title="Spam" height="16" width="16" />' % portal_name) self.assertEqual(image_field.tag(dummy, alt='', title=''), '<img src="%s/dummy/imagefield" alt="" title="" height="16" width="16" />' % portal_name) def test_gif_format_preserved_when_scaling(self): dummy = self.makeDummy() image_field = dummy.getField('imagefield') scaled_image_file, img_format = image_field.scale(img_content, 5, 5) self.assertEqual("gif", img_format) image = PIL.Image.open(scaled_image_file) self.assertEqual("GIF", image.format) def test_dont_scale_animated_gif_when_original_is_smaller_than_scale_size(self): dummy = self.makeDummy() image_field = dummy.getField('imagefield') scaled_image_file, img_format = image_field.scale(animated_gif_content, 100, 100) self.assertEqual("gif", img_format) image = PIL.Image.open(scaled_image_file) self.assertEqual("GIF", image.format) image.seek(image.tell() + 1) def test_get_size(self): dummy = self.makeDummy() request = FakeRequest() request.form.update(field_values) request.form['fieldset'] = 'default' dummy.REQUEST = request dummy.processForm() size = 0 for k, v in expected_values.items(): field = dummy.getField(k) s = field.get_size(dummy) size += s self.assertTrue(s, 'got: %s, field: %s' % (s, k)) self.assertEqual(size, dummy.get_size()) def test_validation(self): dummy = self.makeDummy() request = FakeRequest() request.form.update(field_values) request.form['fieldset'] = 'default' dummy.REQUEST = request errors = {} dummy.validate(REQUEST=request, errors=errors) self.assertFalse(errors, errors) def test_validation_visible_fields(self): """ we assume that every field is visible """ dummy = self.makeDummy() request = TestRequest() alsoProvides(request, IAttributeAnnotatable) my_values = field_values.copy() my_values['fixedpointfield2'] = 'an_error' request.form.update(my_values) request.form['fieldset'] = 'default' errors = {} dummy.validate(errors=errors, REQUEST=request) self.assertTrue(errors, errors) def test_validation_invisible_fields(self): dummy = self.makeDummy() request = FakeRequest() my_values = field_values.copy() my_values['fixedpointfield2'] = 'an_error' request.form.update(my_values) request.form['fieldset'] = 'default' for field in dummy.Schema().filterFields(__name__='fixedpointfield2'): field.widget.visible['edit'] = 'invisible' errors = {} dummy.validate(errors=errors, REQUEST=request) self.assertFalse(errors, errors) def test_validation_hidden_fields(self): dummy = self.makeDummy() request = FakeRequest() my_values = field_values.copy() my_values['fixedpointfield2'] = 'an_error' request.form.update(my_values) request.form['fieldset'] = 'default' for field in dummy.Schema().filterFields(__name__='fixedpointfield2'): field.widget.visible['edit'] = 'hidden' errors = {} dummy.validate(errors=errors, REQUEST=request) self.assertFalse(errors, errors) def test_double_validation(self): """ If a field already has an error and it is validated again, we cut the validation short and return the original error. Here we test that we do not lose the original error in the process. We do that by adding the fieldsets twice in the request, which can happen if you have some whacky javascript that tries to clone too many inputs. """ dummy = self.makeDummy() request = TestRequest() alsoProvides(request, IAttributeAnnotatable) my_values = field_values.copy() my_values['fixedpointfield2'] = 'an_error' request.form.update(my_values) request.form['fieldset'] = 'default' request.form['fieldsets'] = ['default', 'default'] errors = {} dummy.validate(errors=errors, REQUEST=request) self.assertTrue(errors, errors) # The validation error looks a bit weird because of the # [[plone]] domain that is added by the translation testing # machinery. self.assertEqual(errors['fixedpointfield2'], (u"[[plone][Validation failed(isDecimal): " "'an_error' [[plone][is not a decimal number.]]]]")) def test_required(self): request = FakeRequest() request.form.update(empty_values) request.form['fieldset'] = 'default' self._test_required(request) def test_required_empty_request(self): request = FakeRequest() request.form = {} request.form['fieldset'] = 'default' self._test_required(request) def _test_required(self, request): dummy = self.makeDummy() f_names = [] schema = dummy.Schema() for f in schema.fields(): name = f.getName() f.required = 1 f_names.append(name) errors = {} dummy.validate(REQUEST=request, errors=errors) self.assertTrue(errors, "Errors dictionary is empty.") err_fields = errors.keys() failures = [] for f_name in f_names: if f_name not in err_fields: failures.append(f_name) self.assertFalse(failures, "%s failed to report error." % failures) def test_static_vocabulary(self): dummy = self.makeDummy() field = dummy.Schema().fields()[0] # Default self.assertEqual(field.Vocabulary(), DisplayList()) # DisplayList field.vocabulary = sampleDisplayList() self.assertEqual(field.Vocabulary(), sampleDisplayList) # List field.vocabulary = ['e1', 'element2'] self.assertEqual(field.Vocabulary(), sampleDisplayList) # 2-Tuples field.vocabulary = [('e1', 'e1'), ('element2', 'element2')] self.assertEqual(field.Vocabulary(), sampleDisplayList) def test_dynamic_vocabulary(self): dummy = self.makeDummy() field = dummy.Schema().fields()[0] # Default self.assertEqual(field.Vocabulary(dummy), DisplayList()) # Method field.vocabulary = 'aMethod' self.assertEqual(field.Vocabulary(dummy), sampleDisplayList) # DisplayList field.vocabulary = sampleDisplayList() self.assertEqual(field.Vocabulary(dummy), sampleDisplayList) # List field.vocabulary = ['e1', 'element2'] self.assertEqual(field.Vocabulary(dummy), sampleDisplayList) # 2-Tuples field.vocabulary = [('e1', 'e1'), ('element2', 'element2')] self.assertEqual(field.Vocabulary(dummy), sampleDisplayList) # Interface field.vocabulary = sampleInterfaceVocabulary() self.assertEqual(field.Vocabulary(dummy), sampleDisplayList) def test_factory_vocabulary(self): dummy = self.makeDummy() field = dummy.Schema().fields()[0] # Default self.assertEqual(field.Vocabulary(dummy), DisplayList()) expected = DisplayList([('value1', 'title1'), ('v2', 't2')]) # # Vocabulary factory field.vocabulary = () field.vocabulary_factory = 'archetypes.tests.dummyvocab' getSiteManager().registerUtility(component=DummyVocabFactory, name='archetypes.tests.dummyvocab') self.assertEqual(field.Vocabulary(dummy), expected) getSiteManager().unregisterUtility(component=DummyVocabFactory, name='archetypes.tests.dummyvocab') def test_factory_vocabulary_int(self): dummy = self.makeDummy() request = FakeRequest() field = dummy.Schema().fields()[0] # Default self.assertEqual(field.Vocabulary(dummy), IntDisplayList()) expected = IntDisplayList([(1, 'title1'), (2, 't2')]) # # Vocabulary factory field.vocabulary = () field.vocabulary_factory = 'archetypes.tests.dummyintvocab' getSiteManager().registerUtility(component=DummyIntVocabFactory, name='archetypes.tests.dummyintvocab') self.assertEqual(field.Vocabulary(), expected) getSiteManager().unregisterUtility(component=DummyIntVocabFactory, name='archetypes.tests.dummyintvocab') def test_allowable_content_types_ok(self): dummy = self.makeDummy() request = TestRequest() request.form.update(field_values) request.form['fieldset'] = 'default' dummy.REQUEST = request errors = {} dummy.validate(REQUEST=request, errors=errors) self.assertFalse(errors, errors) def test_allowable_content_types_ofs_image_field(self): dummy = self.makeDummy() request = TestRequest() request.form.update(field_values) image = dummy.getField('imagefield') image.set(dummy, img_file) image = image.getAccessor(dummy)() # we need to set the filename to blank otherwise the mimetypes_registry # will pick up the correct mimetype from the filename and we need to # test a situation where the image field is of type Image from # Archetypes fields and the OFS image uploaded within it has no # filename set image.filename = "" image_file = image.data request.form.update({'imagefield_file': image_file}) request.form['fieldset'] = 'default' dummy.REQUEST = request errors = {} dummy.validate(REQUEST=request, errors=errors) self.assertFalse(errors, errors) def test_allowable_content_types_fail(self): dummy = self.makeDummy() request = TestRequest() request.form.update(field_values) request.form.update({'imagefield_file': pdf_file}) request.form['fieldset'] = 'default' dummy.REQUEST = request errors = {} dummy.validate(REQUEST=request, errors=errors) self.assertTrue(errors, errors) def test_defaults(self): dummy = self.makeDummy() field = dummy.Schema().fields()[0] # Default self.assertEqual(field.getDefault(dummy), None) # Value field.default = "Hello" self.assertEqual(field.getDefault(dummy), 'Hello') # Method field.default = None field.default_method = 'default_val' self.assertEqual(field.getDefault(dummy), 'World') # Adapter field.default_method = None class DefaultFor(object): implements(IFieldDefaultProvider) def __init__(self, context): self.context = context def __call__(self): return "Adapted" getSiteManager().registerAdapter(factory=DefaultFor, required=(Dummy,), name=field.__name__) self.assertEqual(field.getDefault(dummy), 'Adapted') getSiteManager().unregisterAdapter(factory=DefaultFor, required=(Dummy,), name=field.__name__) def test_encoding(self): # http://dev.plone.org/plone/ticket/7597 dummy = self.makeDummy() field = dummy.Schema().fields()[3] # textfield field.set(self.portal, 'some_text_with_weird_encoding', encoding='latin') encoding = field.getRaw(self.portal, raw=1).original_encoding self.assertEqual(encoding, 'latin') def test_mimetype(self): dummy = self.makeDummy() field = TextField('test', default_content_type='text/html') dummy.test = '' mimetype = field.getContentType(dummy) self.assertEqual('text/html', mimetype) class DownloadTest(ATSiteTestCase): def afterSetUp(self): # Set up a content object with a field that has a word # document in it ATSiteTestCase.afterSetUp(self) self.dummy = mkDummyInContext( Dummy, oid='dummy', context=self.portal, schema=schema) self.field = self.dummy.getField('textfield') ptpath = PortalTransforms.__path__[0] self.wordfile = open('%s/tests/input/test.doc' % ptpath) self.field.getMutator(self.dummy)(self.wordfile.read()) self.request = self.app.REQUEST self.response = self.request.response def test_download_from_textfield(self): # make sure field data doesn't get transformed when using the # download method value = self.field.download(self.dummy, no_output=True) self.assertFalse(isinstance(value, str)) # XXX This test produces an UnicodeEncodeError in default Archetypes def DISABLED_test_download_filename_encoding(self): # When downloading, the filename is converted to ASCII: self.field.setFilename(self.dummy, '\xc3\xbcberzeugen') self.field.download(self.dummy, no_output=True) self.assertEqual(self.response.headers['content-disposition'], 'attachment; filename="uberzeugen"') def test_suite(): suite = TestSuite() suite.addTest(makeSuite(ProcessingTest)) suite.addTest(makeSuite(DownloadTest)) return suite