',
- ... mimetype='text/html')
-
-The details will now contain a bit more info.
-
- >>> object.getDetails()
- '{"thumb": "", "description": "Test", "anchors": ["anchor"], "title": "document"}'
-
-We can also get the details of an image object.
-
- >>> image = portal.invokeFactory('Image', id='image')
- >>> portal[image]
-
-
- >>> import os
- >>> imgdata = open(os.path.join(os.path.dirname(__file__), 'sample.png'))
- >>> portal[image].setImage(imgdata)
-
-The details will now also include the thumbnail url and the imagescales.
-
- >>> object = IJSONDetails(portal[image])
- >>> object.getDetails()
- '{"scales":..."thumb": "http://nohost/plone/resolveuid/.../@@images/image/thumb"...}'
-
-The first scale is always the original size:
-
- >>> object.getDetails()
- '{"scales": [{"size": [52, 43], "value": "", "title": "Original"}...
-
-JSONFolderListing
------------------
-
-The folder listing is used in the link and image drawers to show the contents
-of a folder. Let's see what items our current siteroot has.
-
- >>> from zope.component import getUtility
- >>> from Products.TinyMCE.interfaces.utility import ITinyMCE
- >>> utility = getUtility(ITinyMCE)
- >>> linkable_portal_types = utility.linkable.split('\n')
- >>> linkable_portal_types.extend(utility.containsobjects.split('\n'))
- >>> from Products.TinyMCE.adapters.interfaces.JSONFolderListing import \
- ... IJSONFolderListing
- >>> object = IJSONFolderListing(portal)
- >>> object.getListing(filter_portal_types=linkable_portal_types,\
- ... rooted=False,\
- ... document_base_url='http://nohost/plone',\
- ... upload_type='File',\
- ... )
- '{"parent_url": "", "path": [...],..."items": [...]}'
-
-Let's create some more content to get breadcrumbs.
-
- >>> folder = portal.invokeFactory('Folder', id='folder')
- >>> folder_object = portal[folder]
- >>> document = folder_object.invokeFactory('Document', id='document')
-
-When we call the getListing method on the document we should get the listing of
-its parent.
-
- >>> object = IJSONFolderListing(folder_object.get(document))
- >>> object.getListing(filter_portal_types=[],\
- ... rooted='False',\
- ... document_base_url='http://nohost/plone/folder',\
- ... )
- '{"parent_url": "http://nohost/plone"...}'
-
-We can also select rooted so we don't get all the breadcrumbs.
-
- >>> object.getListing(filter_portal_types=[],\
- ... rooted='True',\
- ... document_base_url='http://nohost/plone/folder',\
- ... )
- '{..."path": [{"url": "http://nohost/plone/folder"...}'
-
-JSONSearch
-----------
-
-The json search is used the look for content types within the portal. Let's see
-if we can find some items containing the searchterm 'Events'
-
- >>> linkable_portal_types = utility.linkable.split('\n')
- >>> linkable_portal_types.extend(utility.containsobjects.split('\n'))
- >>> from Products.TinyMCE.adapters.interfaces.JSONSearch import IJSONSearch
- >>> object = IJSONSearch(portal)
- >>> object.getSearchResults(filter_portal_types=linkable_portal_types,\
- ... searchtext='Events',\
- ... )
- '{..."title": "Events", "url": "http://nohost/plone/events"...}'
-
-Save
-----
-
-This class is used to save a document using json. Let's try and set some value.
-
- >>> from Products.TinyMCE.adapters.interfaces.Save import ISave
- >>> object = ISave(folder_object[document])
- >>> object.save(fieldname='text',\
- ... text='
test
',\
- ... )
- 'saved'
-
-The document should now contain our new text.
-
- >>> folder_object[document].getText()
- '
test
'
-
-Upload
-------
-
diff --git a/Products/TinyMCE/tests/base.py b/Products/TinyMCE/tests/base.py
new file mode 100644
index 00000000..ac200b72
--- /dev/null
+++ b/Products/TinyMCE/tests/base.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+import unittest2 as unittest
+
+from plone.app.testing import FunctionalTesting
+from plone.app.testing import IntegrationTesting
+from plone.app.testing import PLONE_FIXTURE
+from plone.app.testing import PloneSandboxLayer
+from plone.app.testing import login
+from plone.app.testing import setRoles
+from plone.app.testing import TEST_USER_ID
+from plone.app.testing import TEST_USER_NAME
+from plone.testing import z2
+
+
+class TinyMCELayer(PloneSandboxLayer):
+ defaultBases = (PLONE_FIXTURE,)
+
+ def setUpZope(self, app, configurationContext):
+ """Set up Zope."""
+ import Products.TinyMCE
+ self.loadZCML(package=Products.TinyMCE)
+ z2.installProduct(app, 'Products.TinyMCE')
+
+ def setUpPloneSite(self, portal):
+ """Set up Plone."""
+ # Install into Plone site using portal_setup
+ self.applyProfile(portal, 'Products.TinyMCE:TinyMCE')
+
+ def tearDownZope(self, app):
+ """Tear down Zope."""
+ z2.uninstallProduct(app, 'Products.TinyMCE')
+
+
+FIXTURE = TinyMCELayer()
+INTEGRATION_TESTING = IntegrationTesting(
+ bases=(FIXTURE,), name="TinyMCELayer:Integration")
+FUNCTIONAL_TESTING = FunctionalTesting(
+ bases=(FIXTURE,), name="TinyMCELayer:Functional")
+
+
+class BaseTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.portal = self.layer['portal']
+ self.app = self.layer['app']
+ setRoles(self.portal, TEST_USER_ID, ['Manager'])
+ login(self.portal, TEST_USER_NAME)
+
+
+class IntegrationTestCase(BaseTestCase):
+ """Base class for integration tests."""
+
+ layer = INTEGRATION_TESTING
+
+
+class FunctionalTestCase(BaseTestCase):
+ """Base class for functional tests."""
+
+ layer = FUNCTIONAL_TESTING
diff --git a/Products/TinyMCE/tests/browser.txt b/Products/TinyMCE/tests/browser.txt
deleted file mode 100644
index b5157d3f..00000000
--- a/Products/TinyMCE/tests/browser.txt
+++ /dev/null
@@ -1,153 +0,0 @@
-=======
-Browser
-=======
-
-The browser section adds browserviews used by TinyMCE.
-
-Let's start by logging in as a manager.
-
- >>> self.setRoles(['Manager'])
-
-url
----
-
-We start by creating a normal Document.
-
- >>> document = portal.invokeFactory('Document', id='document')
- >>> portal[document]
-
-
-Then we get the url by specifying the uid of the document.
-
- >>> from Products.Five.testbrowser import Browser
- >>> browser = Browser()
- >>> self.app.acl_users.userFolderAddUser('root', 'secret', ['Manager'], [])
- >>> browser.addHeader('Authorization', 'Basic root:secret')
- >>> browser.open('http://nohost/plone/portal_tinymce/@@tinymce-getpathbyuid?uid=%s' % (portal[document].UID()))
- >>> browser.contents
- 'http://nohost/plone/document'
-
-If we don't specify the uid we get an empty string.
-
- >>> browser.open('http://nohost/plone/portal_tinymce/@@tinymce-getpathbyuid')
- >>> browser.contents
- ''
-
-If we specify a non existing uid we should get an empty string.
-
- >>> browser.open('http://nohost/plone/portal_tinymce/@@tinymce-getpathbyuid?uid=abc')
- >>> browser.contents
- ''
-
-style
------
-
-This browserview will return all the stylesheets used. Let's call the
-browser view.
-
- >>> browser.open('http://nohost/plone/portal_tinymce/@@tinymce-getstyle')
- >>> browser.contents
- '...'
-
-controlpanel
-------------
-
-Open the TinyMCE control panel.
-
- >>> browser.open('http://nohost/plone/portal_tinymce/@@tinymce-controlpanel')
-
-jsonlinkablefolderlisting
--------------------------
-
-We can call the linkable folder listing browserview on the site root to get a
-list of linkable items.
-
- >>> browser.open('http://nohost/plone/@@tinymce-jsonlinkablefolderlisting?rooted=False&document_base_url=http://nohost/plone/')
- >>> browser.contents
- '..."id": "document"...'
-
-jsonimagefolderlisting
-----------------------
-
-Let's create an image first.
-
- >>> image = portal.invokeFactory('Image', id='image')
- >>> portal[image]
-
-
-Now we can get a listing of the images and check if our image is there.
-
- >>> browser.open('http://nohost/plone/@@tinymce-jsonimagefolderlisting?rooted=False&document_base_url=http://nohost/plone/')
- >>> browser.contents
- '..."id": "image"...'
-
-jsonlinkablesearch
-------------------
-
-If we want to search for a linkable item we can call the json linkable search
-browser view and specify a searchtext. Let's find our document.
-
- >>> browser.open('http://nohost/plone/@@tinymce-jsonlinkablesearch?searchtext=Document')
- >>> browser.contents
- '..."id": "document"...'
-
-jsonimagesearch
----------------
-
-The images have a similar search method. Let's find our image.
-
- >>> browser.open('http://nohost/plone/@@tinymce-jsonimagesearch?searchtext=Image')
- >>> browser.contents
- '..."id": "image"...'
-
-jsondetails
------------
-
-When we call the json details view on a document we will get the details of
-the specific item.
-
- >>> browser.open('http://nohost/plone/document/@@tinymce-jsondetails')
- >>> browser.contents
- '...document...'
-
-save
-----
-
-Let's call the save method to store some content in the document we created.
-
- >>> browser.open('http://nohost/plone/document/@@tinymce-save?text=test&fieldname=text')
- >>> portal[document].getText()
- 'test'
-
-upload
-------
-
-TODO
-
-configuration
--------------
-
- >>> document_jsonconfig_url = ('http://nohost/plone/document/'
- ... '@@tinymce-jsonconfiguration?fieldname=text')
- >>> browser.open(document_jsonconfig_url)
- >>> browser.contents
- '...buttons...'
-
-If we configure directivity to 'auto', the directivity is set depending
-on the content language.
-
- >>> from Products.TinyMCE.utility import form_adapter
- >>> utility = form_adapter(portal)
- >>> utility.directionality = 'auto'
- >>> doc = portal[document]
- >>> doc.Language()
- 'en'
- >>> browser.open(document_jsonconfig_url)
- >>> browser.contents
- '..."directionality": "ltr"...'
- >>> doc.setLanguage('ar')
- >>> browser.open(document_jsonconfig_url)
- >>> browser.contents
- '..."directionality": "rtl"...'
-
-
diff --git a/Products/TinyMCE/tests/exportimport.txt b/Products/TinyMCE/tests/exportimport.txt
deleted file mode 100644
index 53f60723..00000000
--- a/Products/TinyMCE/tests/exportimport.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-=============
-Export/Import
-=============
-
-Import
-------
-
-This module takes care of exporting and importing settings of TinyMCE. Let's
-create a dummy import context. And a dummy file.
-
- >>> from Products.GenericSetup.tests.common import DummyImportContext
- >>> xml = """\
- ...
- ... """
- >>> context = DummyImportContext(portal, purge=True)
- >>> context._files = {'tinymce.xml': xml}
-
-Now import the file.
-
- >>> from Products.TinyMCE.exportimport import importTinyMCESettings
- >>> importTinyMCESettings(context)
-
-Our specified plugin should now be stored in the utility.
-
- >>> from Products.TinyMCE.interfaces.utility import ITinyMCE
- >>> from zope.component import getUtility
- >>> tinymce_utility = getUtility(ITinyMCE)
- >>> 'testplugin' in tinymce_utility.customplugins
- True
-
-Export
-------
-
-Let's create a dummy export context.
-
- >>> from Products.GenericSetup.tests.common import DummyExportContext
- >>> context = DummyExportContext(portal)
-
-And export the current settings.
-
- >>> from Products.TinyMCE.exportimport import exportTinyMCESettings
- >>> exportTinyMCESettings(context)
-
-Check if tinymce.xml is exported.
-
- >>> context._wrote[0][0]
- 'tinymce.xml'
-
-Check the contents of the export.
-
- >>> context._wrote[0][1]
- '...testplugin...'
diff --git a/Products/TinyMCE/tests/setuphandlers.txt b/Products/TinyMCE/tests/setuphandlers.txt
deleted file mode 100644
index 61a5541a..00000000
--- a/Products/TinyMCE/tests/setuphandlers.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-=============
-Setuphandlers
-=============
-
-Remove editor
--------------
-
-First let's remove the editor
-
- >>> from Products.TinyMCE.setuphandlers import remove_editor
- >>> remove_editor(portal)
-
-Check if it is removed.
-
- >>> from Products.CMFCore.interfaces import IPropertiesTool
- >>> from zope.component import getUtility
- >>> portal_props = getUtility(IPropertiesTool)
- >>> site_props=getattr(portal_props,'site_properties', None)
- >>> attrname='available_editors'
- >>> editors=list(site_props.getProperty(attrname))
- >>> 'TinyMCE' in editors
- False
-
-Add editor
-----------
-
-And now add the editor.
-
- >>> from Products.TinyMCE.setuphandlers import add_editor
- >>> add_editor(portal)
-
-And check if it is added.
-
- >>> editors=list(site_props.getProperty(attrname))
- >>> 'TinyMCE' in editors
- True
-
-
-Unregister utility
-------------------
-
-Unregister the utility.
-
- >>> from Products.TinyMCE.setuphandlers import unregisterUtility
- >>> unregisterUtility(portal)
-
-And check if the utility is removed.
-
- >>> from Products.TinyMCE.interfaces.utility import ITinyMCE
- >>> from zope.component import queryUtility
- >>> queryUtility(ITinyMCE, default='Not found')
- 'Not found'
diff --git a/Products/TinyMCE/tests/test_adapters.py b/Products/TinyMCE/tests/test_adapters.py
new file mode 100644
index 00000000..39417d06
--- /dev/null
+++ b/Products/TinyMCE/tests/test_adapters.py
@@ -0,0 +1,107 @@
+"""
+This functionality is added to plone content types using adapters.
+"""
+import os
+
+from zope.component import getUtility
+
+from Products.TinyMCE.adapters.interfaces.JSONDetails import IJSONDetails
+from Products.TinyMCE.adapters.interfaces.Save import ISave
+from Products.TinyMCE.adapters.interfaces.JSONSearch import IJSONSearch
+from Products.TinyMCE.adapters.interfaces.JSONFolderListing import IJSONFolderListing
+from Products.TinyMCE.interfaces.utility import ITinyMCE
+from Products.TinyMCE.tests.base import FunctionalTestCase
+
+
+class AdaptersTestCase(FunctionalTestCase):
+
+ def setUp(self):
+ super(AdaptersTestCase, self).setUp()
+ self.utility = getUtility(ITinyMCE)
+ folder = self.portal.invokeFactory('Folder', id='folder')
+ self.document = self.portal.invokeFactory('Document', id='document')
+ self.folder_object = self.portal[folder]
+
+ def test_json_details_document(self):
+ # This class is used to get detail from a certain object. Let's create a Document.
+ self.assertEqual(repr(self.portal[self.document]), '')
+
+ # The basic details should return the following.
+ obj = IJSONDetails(self.portal[self.document])
+ self.assertEqual(obj.getDetails(), '{"thumb": "", "description": "", "anchors": [], "title": "document"}')
+
+ # Let's set some more details like description and body text.
+ self.portal[self.document].setDescription('Test')
+ self.portal[self.document].setText(u'
', mimetype='text/html')
+
+ # The details will now contain a bit more info.
+ self.assertEqual(obj.getDetails(), '{"thumb": "", "description": "Test", "anchors": ["anchor"], "title": "document"}')
+
+ def test_json_details_image(self):
+ # We can also get the details of an image object.
+ image = self.portal.invokeFactory('Image', id='image')
+ self.assertEqual(repr(self.portal[image]), '')
+
+ imgdata = open(os.path.join(os.path.dirname(__file__), 'sample.png'))
+ self.portal[image].setImage(imgdata)
+
+ # The details will now also include the thumbnail url and the imagescales.
+ obj = IJSONDetails(self.portal[image])
+ self.assertRegexpMatches(obj.getDetails(), r'\{"scales": \[\{"size": \[52, 43], "value": "", "title": "Original"}.+], "thumb": "http://nohost/plone/resolveuid/.*/@@images/image/thumb".+')
+
+ def test_json_folder_listing(self):
+ # The folder listing is used in the link and image drawers to show the contents
+ # of a folder. Let's see what items our current siteroot has.
+
+ linkableportal_types = self.utility.linkable.split('\n')
+ linkableportal_types.extend(self.utility.containsobjects.split('\n'))
+ object = IJSONFolderListing(self.portal)
+ self.assertRegexpMatches(object.getListing(
+ filter_portal_types=linkableportal_types,
+ rooted=False,
+ document_base_url='http://nohost/plone',
+ upload_type='File',
+ ), '\{"parent_url": "", "path": \[.+],.+"items": \[.+]}')
+
+ # Let's create some more content to get breadcrumbs.
+ document = self.folder_object.invokeFactory('Document', id='document')
+
+ # When we call the getListing method on the document we should get the listing of
+ # its parent.
+ obj = IJSONFolderListing(self.folder_object.get(document))
+ self.assertRegexpMatches(obj.getListing(
+ filter_portal_types=[],
+ rooted='False',
+ document_base_url='http://nohost/plone/folder',
+ ), '\{"parent_url": "http://nohost/plone".+}')
+
+ # We can also select rooted so we don't get all the breadcrumbs.
+ self.assertRegexpMatches(obj.getListing(
+ filter_portal_types=[],
+ rooted='True',
+ document_base_url='http://nohost/plone/folder',
+ ), '\{.*"path": \[{"url": "http://nohost/plone/folder".*}')
+
+ def test_json_search(self):
+ # Create an Event
+ self.portal.invokeFactory('Event', id='events', title='Events')
+
+ # The json search is used the look for content types within the self.portal. Let's see
+ # if we can find some items containing the searchterm 'Events'
+ linkable_portal_types = self.utility.linkable.split('\n')
+ linkable_portal_types.extend(self.utility.containsobjects.split('\n'))
+
+ obj = IJSONSearch(self.portal)
+ self.assertRegexpMatches(
+ obj.getSearchResults(filter_portal_types=linkable_portal_types,
+ searchtext='Events',
+ ), '\{.*"title": "Events", "url": "http://nohost/plone/events".*}')
+
+ def test_json_save(self):
+ # This class is used to save a document using json. Let's try and set some value.
+ document = self.folder_object.invokeFactory('Document', id='document')
+ obj = ISave(self.folder_object[document])
+ obj.save(fieldname='text', text='
test
')
+
+ # The document should now contain our new text.
+ self.assertEqual(self.folder_object[self.document].getText(), '
test
')
diff --git a/Products/TinyMCE/tests/test_browser.py b/Products/TinyMCE/tests/test_browser.py
new file mode 100644
index 00000000..d4b7508b
--- /dev/null
+++ b/Products/TinyMCE/tests/test_browser.py
@@ -0,0 +1,88 @@
+from Products.TinyMCE.utility import form_adapter
+from Products.TinyMCE.tests.base import FunctionalTestCase
+
+
+class BrowserTestCase(FunctionalTestCase):
+
+ def setUp(self):
+ super(BrowserTestCase, self).setUp()
+ self.image = self.portal.invokeFactory('Image', id='image')
+ self.document = self.portal.invokeFactory('Document', id='document')
+
+ def test_url(self):
+ # We get the url by specifying the uid of the document.
+ self.portal.REQUEST['uid'] = self.portal[self.document].UID()
+ output = self.portal.restrictedTraverse('/plone/portal_tinymce/@@tinymce-getpathbyuid')()
+ self.assertEqual(output, 'http://nohost/plone/document')
+
+ # If we don't specify the uid we get an empty string.
+ self.portal.REQUEST['uid'] = None
+ output = self.portal.restrictedTraverse('/plone/portal_tinymce/@@tinymce-getpathbyuid')()
+ self.assertEqual(output, '')
+
+ self.portal.REQUEST['uid'] = 'asd'
+ # If we specify a non existing uid we should get an empty string.
+ output = self.portal.restrictedTraverse('/plone/portal_tinymce/@@tinymce-getpathbyuid')()
+ self.assertEqual(output, '')
+
+ def test_getstyle(self):
+ # This browserview will return all the stylesheets used. Let's call the
+ # browser view.
+ output = self.portal.restrictedTraverse('/plone/portal_tinymce/@@tinymce-getstyle')()
+ self.assertRegexpMatches(output, '')
+
+ def test_controlpanel(self):
+ # Open the TinyMCE control panel.
+ self.portal.restrictedTraverse('/plone/portal_tinymce/@@tinymce-controlpanel')()
+
+ def test_jsonlinkablefolderlisting(self):
+ # We can call the linkable folder listing browserview on the site root to get a
+ # list of linkable items.
+ output = self.portal.restrictedTraverse('/plone/@@tinymce-jsonlinkablefolderlisting')(False, 'http://nohost/plone/')
+ self.assertIn('"id": "document"', output)
+
+ def test_jsonimagefolderlisting(self):
+ # Now we can get a listing of the images and check if our image is there.e/'})
+ output = self.portal.restrictedTraverse('/plone/@@tinymce-jsonimagefolderlisting')(False, 'http://nohost/plone/')
+ self.assertIn('"id": "image"', output)
+
+ def test_jsonlinkablesearch(self):
+ # If we want to search for a linkable item we can call the json linkable search
+ # browser view and specify a searchtext. Let's find our document.
+ output = self.portal.restrictedTraverse('/plone/@@tinymce-jsonlinkablesearch')('Document')
+ self.assertIn('"id": "document"', output)
+
+ def test_jsonimagesearch(self):
+ # The images have a similar search method. Let's find our image.
+ output = self.portal.restrictedTraverse('/plone/@@tinymce-jsonimagesearch')('Image')
+ self.assertIn('"id": "image"', output)
+
+ def test_jsondetails(self):
+ # When we call the json details view on a document we will get the details of
+ # the specific item.
+ output = self.portal.restrictedTraverse('/plone/document/@@tinymce-jsondetails')()
+ self.assertIn('document', output)
+
+ def test_save(self):
+ # Let's call the save method to store some content in the document we created.
+ self.portal.restrictedTraverse('/plone/document/@@tinymce-save')('test', 'text')
+ self.assertEqual(self.portal[self.document].getText(), 'test')
+
+ def test_configuration(self):
+ document_jsonconfig_url = '/plone/document/@@tinymce-jsonconfiguration'
+ output = self.portal.restrictedTraverse(document_jsonconfig_url)('text')
+ self.assertIn('buttons', output)
+
+ # If we configure directivity to 'auto', the directivity is set depending
+ # on the content language.
+ utility = form_adapter(self.portal)
+ utility.directionality = 'auto'
+ doc = self.portal[self.document]
+ self.assertEqual(doc.Language(), 'en')
+ output = self.portal.restrictedTraverse(document_jsonconfig_url)('text')
+ self.assertIn('"directionality": "ltr"', output)
+ doc.setLanguage('ar')
+ output = self.portal.restrictedTraverse(document_jsonconfig_url)('text')
+ self.assertIn('"directionality": "rtl"', output)
+
+ # TODO: upload
diff --git a/Products/TinyMCE/tests/test_exportimport.py b/Products/TinyMCE/tests/test_exportimport.py
new file mode 100644
index 00000000..1893ea19
--- /dev/null
+++ b/Products/TinyMCE/tests/test_exportimport.py
@@ -0,0 +1,55 @@
+from zope.component import getUtility
+from Products.GenericSetup.tests.common import DummyImportContext
+from Products.GenericSetup.tests.common import DummyExportContext
+
+from Products.TinyMCE.interfaces.utility import ITinyMCE
+from Products.TinyMCE.exportimport import importTinyMCESettings
+from Products.TinyMCE.exportimport import exportTinyMCESettings
+from Products.TinyMCE.tests.base import FunctionalTestCase
+
+
+class ImportExportTestCase(FunctionalTestCase):
+
+ def test_import_export(self):
+ # This module takes care of exporting and importing settings of TinyMCE. Let's
+ # create a dummy import context. And a dummy file.
+
+ xml = """\
+
+ """
+
+ context = DummyImportContext(self.portal, purge=True)
+ context._files = {'tinymce.xml': xml}
+
+ # Now import the file.
+ importTinyMCESettings(context)
+
+ # Our specified plugin should now be stored in the utility.
+
+ tinymce_utility = getUtility(ITinyMCE)
+ self.assertIn('testplugin', tinymce_utility.customplugins)
+
+ # Let's create a dummy export context.
+ context = DummyExportContext(self.portal)
+
+ # And export the current settings.
+ exportTinyMCESettings(context)
+
+ # Check if tinymce.xml is exported.
+ self.assertEqual(context._wrote[0][0], 'tinymce.xml')
+
+ # Check the contents of the export.
+ self.assertIn('testplugin', context._wrote[0][1])
diff --git a/Products/TinyMCE/tests/test_setuphandlers.py b/Products/TinyMCE/tests/test_setuphandlers.py
new file mode 100644
index 00000000..f163f691
--- /dev/null
+++ b/Products/TinyMCE/tests/test_setuphandlers.py
@@ -0,0 +1,37 @@
+from zope.component import queryUtility
+from zope.component import getUtility
+from Products.CMFCore.interfaces import IPropertiesTool
+
+from Products.TinyMCE.interfaces.utility import ITinyMCE
+from Products.TinyMCE.tests.base import FunctionalTestCase
+from Products.TinyMCE.setuphandlers import remove_editor
+from Products.TinyMCE.setuphandlers import add_editor
+from Products.TinyMCE.setuphandlers import unregisterUtility
+
+
+class SetupHandlersTestCase(FunctionalTestCase):
+
+ def test_remove_editor(self):
+ # First let's remove the editor
+ remove_editor(self.portal)
+
+ # Check if it is removed.
+
+ portal_props = getUtility(IPropertiesTool)
+ site_props = getattr(portal_props, 'site_properties', None)
+ attrname = 'available_editors'
+ editors = list(site_props.getProperty(attrname))
+ self.assertNotIn('TinyMCE', editors)
+
+ # And now add the editor.
+ add_editor(self.portal)
+
+ # And check if it is added.
+ editors = list(site_props.getProperty(attrname))
+ self.assertIn('TinyMCE', editors)
+
+ def test_unregister_utility(self):
+ unregisterUtility(self.portal)
+
+ # And check if the utility is removed.
+ self.assertEqual(queryUtility(ITinyMCE, default='Not found'), 'Not found')
diff --git a/Products/TinyMCE/tests/test_transforms.py b/Products/TinyMCE/tests/test_transforms.py
new file mode 100644
index 00000000..22d69c4d
--- /dev/null
+++ b/Products/TinyMCE/tests/test_transforms.py
@@ -0,0 +1,65 @@
+from zope.component import getUtility
+
+from Products.CMFPlone.tests import dummy
+from Products.TinyMCE.interfaces.utility import ITinyMCE
+from Products.TinyMCE.tests.base import FunctionalTestCase
+
+
+class TransformsTestCase(FunctionalTestCase):
+
+ # Tests integration with plone.outputfilters, which provides 2 filters that are
+ # applied while rendering rich text:
+
+ # * Resolving UID links to absolute URIs.
+ # * Adding captions to images with the "captioned" CSS class.
+
+ # These filters used to be implemented in Products.TinyMCE, so for backwards
+ # compatibility they can be enabled via settings in the TinyMCE control panel.
+
+ def test_filters(self):
+ # Let's turn these filters on first.
+ tinymce_utility = getUtility(ITinyMCE)
+ tinymce_utility.link_using_uids = True
+ tinymce_utility.allow_captioned_images = True
+
+ # To test the settings, we'll need an image.
+ self.portal.invokeFactory('Image', id='image.jpg', title="Image", file=dummy.Image())
+ image = getattr(self.portal, 'image.jpg')
+ image.setDescription('My caption')
+ image.reindexObject()
+ UID = image.UID()
+
+ # Let's transform some text/html to text/x-html-safe, which should trigger the
+ # filters.
+ text = """
+
+
+
+
+
+
This is line 1
+ This is line 2
+
+""" % UID
+ transformed_text = self.portal.portal_transforms.convertTo('text/x-html-safe', text, mimetype='text/html', context=self.portal)
+
+ # The UID reference should be converted to an absolute url, and a caption should be added.
+ self.assertEqual(unicode(transformed_text).replace(' ', ''), u"""
+
+
+
My caption
+
+
+
+
My caption
+
+
This is line 1
+ This is line 2
+""".replace(' ', ''))
+ # Now turn off the settings.
+ tinymce_utility.link_using_uids = False
+ tinymce_utility.allow_captioned_images = False
+
+ # Now the filters should not be applied.
+ transformed_text_2 = self.portal.portal_transforms.convertTo('text/x-html-safe', text, mimetype='text/html', context=self.portal)
+ self.assertNotEqual(unicode(transformed_text_2).replace(' ', ''), unicode(transformed_text).replace(' ', ''))
diff --git a/Products/TinyMCE/tests/test_upgrades.py b/Products/TinyMCE/tests/test_upgrades.py
new file mode 100644
index 00000000..e4c53729
--- /dev/null
+++ b/Products/TinyMCE/tests/test_upgrades.py
@@ -0,0 +1,21 @@
+from zope.component import getUtility
+from Products.CMFCore.utils import getToolByName
+
+from Products.TinyMCE.upgrades import upgrade_10_to_11
+from Products.TinyMCE.interfaces.utility import ITinyMCE
+from Products.TinyMCE.tests.base import FunctionalTestCase
+
+
+class UpgradesTestCase(FunctionalTestCase):
+
+ def test_upgrade_profile(self):
+ # First set the entity encoding to a different value.
+ tinymce = getUtility(ITinyMCE)
+ tinymce.entity_encoding = u"named"
+
+ # Then run the upgrade from 1.0. to 1.1.
+ portal_setup = getToolByName(self.portal, 'portal_setup')
+ upgrade_10_to_11(portal_setup)
+
+ # And check the outcome
+ self.assertEqual(tinymce.entity_encoding, u'raw')
diff --git a/Products/TinyMCE/tests/test_utility.py b/Products/TinyMCE/tests/test_utility.py
new file mode 100644
index 00000000..2c825b9a
--- /dev/null
+++ b/Products/TinyMCE/tests/test_utility.py
@@ -0,0 +1,170 @@
+import os
+
+import transaction
+from Products.CMFCore.utils import getToolByName
+from plone.testing.z2 import Browser
+
+from Products.TinyMCE.tests.base import IntegrationTestCase
+from Products.TinyMCE.utility import form_adapter
+
+
+class UtilityTestCase(IntegrationTestCase):
+
+ def setUp(self):
+ super(UtilityTestCase, self).setUp()
+ self.utility = form_adapter(self.portal)
+
+ def test_tinymce_configuration(self):
+ # Let's get the configuration of TinyMCE and see if it returns a json structure.
+ self.assertTrue(self.utility.getConfiguration(self.portal))
+
+ # Now let's change some variables and see it still works.
+ self.utility.toolbar_cut = True
+ self.utility.toolbar_copy = True
+ self.utility.toolbar_paste = True
+ self.utility.toolbar_pastetext = True
+ self.utility.toolbar_pasteword = True
+ self.utility.toolbar_undo = True
+ self.utility.toolbar_redo = True
+ self.utility.toolbar_search = True
+ self.utility.toolbar_replace = True
+ self.utility.toolbar_underline = True
+ self.utility.toolbar_strikethrough = True
+ self.utility.toolbar_sub = True
+ self.utility.toolbar_sup = True
+ self.utility.toolbar_forecolor = True
+ self.utility.toolbar_backcolor = True
+ self.utility.toolbar_media = True
+ self.utility.toolbar_charmap = True
+ self.utility.toolbar_hr = True
+ self.utility.toolbar_advhr = True
+ self.utility.toolbar_insertdate = True
+ self.utility.toolbar_inserttime = True
+ self.utility.toolbar_emotions = True
+ self.utility.toolbar_nonbreaking = True
+ self.utility.toolbar_pagebreak = True
+ self.utility.toolbar_print = True
+ self.utility.toolbar_preview = True
+ self.utility.toolbar_spellchecker = True
+ self.utility.toolbar_removeformat = True
+ self.utility.toolbar_cleanup = True
+ self.utility.toolbar_visualaid = True
+ self.utility.toolbar_visualchars = True
+ self.utility.toolbar_attribs = True
+ self.utility.resizing = False
+
+ # The result should at least contain the new buttons we added.
+ self.assertRegexpMatches(self.utility.getConfiguration(self.portal), '\{.+attribs.+}')
+
+ # Let's change some more settings.
+ self.utility.toolbar_external = True
+ self.utility.autoresize = True
+ self.utility.editor_width = u'100'
+ self.utility.editor_height = u'abc'
+ self.utility.toolbar_width = u'abc'
+ self.utility.contextmenu = False
+ self.utility.content_css = u'test.css'
+ self.utility.link_using_uids = True
+ self.utility.allow_captioned_images = True
+ self.utility.rooted = True
+
+ props = getToolByName(self, 'portal_properties')
+ livesearch = props.site_properties.getProperty('enable_livesearch', False)
+ livesearch = False
+ livesearch # pep8
+
+ # The result should contain the settings specified.
+ self.assertRegexpMatches(self.utility.getConfiguration(self.portal), '\{.+external.+}')
+
+ # Let's call the portal_factory of a document and make sure the configuration
+ # doesn't contain the save button:
+ browser = Browser(self.app)
+ self.app.acl_users.userFolderAddUser('root', 'secret', ['Manager'], [])
+ transaction.commit()
+ browser.addHeader('Authorization', 'Basic root:secret')
+ browser.open('http://nohost/plone/createObject?type_name=Document')
+ self.assertNotIn(""save":", browser.contents)
+
+ # Do some more toolbar tests, specifically testing the spellchecker button.
+ # First, we make sure that no spellchecker is loaded when the toolbar button is
+ # hidden.
+ self.utility.toolbar_spellchecker = False
+ transaction.commit()
+ browser.open('http://nohost/plone/createObject?type_name=Document')
+
+ # AtD shouldn't be there:
+ self.assertNotIn(""AtD"", browser.contents)
+
+ # Neither should iespell:
+ self.assertNotIn(""iespell"", browser.contents)
+
+ # Now, we enable the button and set AtD as the checker:
+ self.utility.toolbar_spellchecker = True
+ self.utility.libraries_spellchecker_choice = u'AtD'
+ transaction.commit()
+ browser.open('http://nohost/plone/createObject?type_name=Document')
+ self.assertIn(""AtD"", browser.contents)
+
+ # Now, we set iespell as the checker:
+ self.utility.libraries_spellchecker_choice = u'iespell'
+ transaction.commit()
+ browser.open('http://nohost/plone/createObject?type_name=Document')
+ self.assertIn(""iespell"", browser.contents)
+
+ # When we have browser as the checker, neither iespell nor AtD should load:
+ self.utility.libraries_spellchecker_choice = u'browser'
+ transaction.commit()
+ browser.open('http://nohost/plone/createObject?type_name=Document')
+ self.assertNotIn(""iespell"", browser.contents)
+ self.assertNotIn(""AtD"", browser.contents)
+
+ def test_tinymce_configurable_image_dimensions(self):
+ # The image scale dimensions being provided to the image chooser should be
+ # gathered from the chosen AT content type. Let's begin by creating an
+ # AT image:
+ id = self.portal.invokeFactory('News Item', id='test')
+ self.assertEqual(repr(self.portal[id]), '')
+
+ # Now the values being provided through JSON should be gotten from the ATNewsItem
+ # schema:
+ primary_field = self.portal.test.schema['image']
+ scales = self.utility.getImageScales(primary_field)
+ self.assertEqual(scales,
+ [{'size': [0, 0], 'title': 'Original', 'value': ''},
+ {'size': [16, 16], 'title': 'Listing', 'value': 'image_listing'},
+ {'size': [32, 32], 'title': 'Icon', 'value': 'image_icon'},
+ {'size': [64, 64], 'title': 'Tile', 'value': 'image_tile'},
+ {'size': [128, 128], 'title': 'Thumb', 'value': 'image_thumb'},
+ {'size': [200, 200], 'title': 'Mini', 'value': 'image_mini'},
+ {'size': [400, 400], 'title': 'Preview', 'value': 'image_preview'},
+ {'size': [768, 768], 'title': 'Large', 'value': 'image_large'}]
+ )
+
+ # If no primary field is given, we should get the scale dimensions from ATImage:
+ scales = self.utility.getImageScales()
+ self.assertEqual(scales,
+ [{'size': [0, 0], 'title': 'Original', 'value': ''},
+ {'size': [16, 16], 'title': 'Listing', 'value': 'image_listing'},
+ {'size': [32, 32], 'title': 'Icon', 'value': 'image_icon'},
+ {'size': [64, 64], 'title': 'Tile', 'value': 'image_tile'},
+ {'size': [128, 128], 'title': 'Thumb', 'value': 'image_thumb'},
+ {'size': [200, 200], 'title': 'Mini', 'value': 'image_mini'},
+ {'size': [400, 400], 'title': 'Preview', 'value': 'image_preview'},
+ {'size': [768, 768], 'title': 'Large', 'value': 'image_large'}]
+ )
+
+ # We need to specify a context, if we want the dimension of the original image
+ imgdata = open(os.path.join(os.path.dirname(__file__), 'sample.png'))
+ self.portal[id].setImage(imgdata)
+
+ scales = self.utility.getImageScales(context=self.portal[id])
+ self.assertEqual(scales,
+ [{'size': [52, 43], 'title': 'Original', 'value': ''},
+ {'size': [16, 16], 'title': 'Listing', 'value': 'image_listing'},
+ {'size': [32, 32], 'title': 'Icon', 'value': 'image_icon'},
+ {'size': [64, 64], 'title': 'Tile', 'value': 'image_tile'},
+ {'size': [128, 128], 'title': 'Thumb', 'value': 'image_thumb'},
+ {'size': [200, 200], 'title': 'Mini', 'value': 'image_mini'},
+ {'size': [400, 400], 'title': 'Preview', 'value': 'image_preview'},
+ {'size': [768, 768], 'title': 'Large', 'value': 'image_large'}]
+ )
diff --git a/Products/TinyMCE/tests/test_wysiwyg_support.py b/Products/TinyMCE/tests/test_wysiwyg_support.py
new file mode 100644
index 00000000..588f7f34
--- /dev/null
+++ b/Products/TinyMCE/tests/test_wysiwyg_support.py
@@ -0,0 +1,120 @@
+import transaction
+from plone.testing.z2 import Browser
+
+from Products.TinyMCE.tests.base import IntegrationTestCase
+from plone.app.testing import TEST_USER_NAME
+from plone.app.testing import TEST_USER_PASSWORD
+
+
+class WysiwygSupportTestCase(IntegrationTestCase):
+
+ def setUp(self):
+ super(WysiwygSupportTestCase, self).setUp()
+ # Unhide exceptions.
+
+ def test_personalized(self):
+ self.portal.error_log._ignored_exceptions = ()
+ browser = Browser(self.app)
+ portal_url = self.portal.absolute_url()
+
+ # Get an account and login via the login form.
+ browser.open(portal_url + '/login_form')
+ browser.getControl(name='__ac_name').value = TEST_USER_NAME
+ browser.getControl(name='__ac_password').value = TEST_USER_PASSWORD
+ browser.getControl(name='submit').click()
+
+ # Create a blank page for test edits. (front-page is too noisy.)
+ testpage = self.portal.absolute_url() + '/' + self.portal.invokeFactory('Document', 'testpage') + '/edit'
+ self.assertEqual(testpage, 'http://nohost/plone/testpage/edit')
+ transaction.commit()
+
+ # Set up personalize_form
+ personalizer = portal_url + '/personalize_form'
+ self.assertEqual(personalizer, 'http://nohost/plone/personalize_form')
+ browser.open(personalizer)
+ browser.getControl(name='email').value = 'test@example.org'
+ browser.getControl(name='form.button.Save').click()
+ self.assertIn('
Your personal settings have been saved.
', browser.contents)
+
+ #Test different editors
+ #----------------------
+ #Which editor is used is set globally and can be overridden in the personal preferences.
+ #This results in the following matrix:
+ #===== ===== ======
+ #Global Personal Expected
+ #===== ===== ======
+ #TinyMCE Use site's default TinyMCE
+ #TinyMCE None Basic textarea
+ #TinyMCE TinyMCE TinyMCE
+ # Use site's default Basic textarea
+ # None Basic textarea
+ # TinyMCE TinyMCE
+ #===== ===== ======
+
+ # Set the editor globally to TinyMCE:
+ self.portal.portal_properties.site_properties.default_editor = 'TinyMCE'
+ transaction.commit()
+
+ # If the user sets 'Use site's default'...
+ browser.open(personalizer)
+ browser.getControl(name='wysiwyg_editor').value = ['']
+ browser.getControl(name='form.button.Save').click()
+ self.assertIn('
Your personal settings have been saved.
', browser.contents)
+
+ # we should get TinyMCE:
+ browser.open(testpage)
+ self.assertIn('TinyMCEConfig', browser.contents)
+
+ # If the user sets 'None'...
+ browser.open(personalizer)
+ browser.getControl(name='wysiwyg_editor').value = ['None']
+ browser.getControl(name='form.button.Save').click()
+ self.assertIn('
Your personal settings have been saved.
', browser.contents)
+
+ # We should get just a textarea:
+ browser.open(testpage)
+ self.assertIn(' ...
-
-
-If the user sets 'TinyMCE'...
- >>> browser.open(personalizer)
- >>> browser.getControl(name='wysiwyg_editor').value = ['TinyMCE']
- >>> browser.getControl(name='form.button.Save').click()
- >>> print(browser.contents)
- Your personal settings have been saved. ...
-
-we should get TinyMCE:
- >>> browser.open(testpage)
- >>> print(browser.contents)
-
- tinymce.dom.Event.add(window, 'load', function(e) { var config = new TinyMCEConfig('text'); config.init(); }); ...
diff --git a/buildout.cfg b/buildout.cfg
index 012b0b4e..6d5edd22 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -3,7 +3,9 @@
extends = http://svn.plone.org/svn/collective/buildout/plonetest/test-4.x.cfg
versions = versions
package-name = Products.TinyMCE
-#test-eggs = PIL
+test-eggs =
+ plone.app.testing
+ unittest2
parts += omelette
@@ -13,8 +15,7 @@ extensions =
develop = .
[instance]
-#eggs+=PIL
-eggs += Products.TinyMCE
+eggs += Products.TinyMCE [test]
[versions]
Products.PrintingMailHost = 0.7
diff --git a/setup.py b/setup.py
index c8c8b95b..e02e6be6 100644
--- a/setup.py
+++ b/setup.py
@@ -35,7 +35,7 @@
'plone.app.imaging>=1.0.2',
'plone.outputfilters',
] + requires,
- extras_require = {
- 'test': ['Products.PloneTestCase'],
+ extras_require={
+ 'test': ['plone.app.testing', 'unittest2'],
},
)