Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/base.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/base.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/base.py	(revision 1493)
@@ -0,0 +1,111 @@
+"""Test setup for integration and functional tests.
+
+When we import PloneTestCase and then call setupPloneSite(), all of
+Plone's products are loaded, and a Plone site will be created. This
+happens at module level, which makes it faster to run each test, but
+slows down test runner startup.
+"""
+import re
+from AccessControl.SecurityManagement import newSecurityManager
+
+from Products.Five import zcml
+from Products.Five import fiveconfigure
+
+from Testing import ZopeTestCase as ztc
+
+from Products.PloneTestCase import PloneTestCase as ptc
+from Products.PloneTestCase.layer import onsetup
+
+from Products.CMFCore.utils import getToolByName
+from config import PROJECT_NAME
+
+# When ZopeTestCase configures Zope, it will *not* auto-load products
+# in Products/. Instead, we have to use a statement such as:
+#   ztc.installProduct('SimpleAttachment')
+# This does *not* apply to products in eggs and Python packages (i.e.
+# not in the Products.*) namespace. For that, see below.
+# All of Plone's products are already set up by PloneTestCase.
+
+@onsetup
+def setup_product():
+    """Set up the package and its dependencies.
+
+    The @onsetup decorator causes the execution of this body to be
+    deferred until the setup of the Plone site testing layer. We could
+    have created our own layer, but this is the easiest way for Plone
+    integration tests.
+    """
+
+    # Load the ZCML configuration for the example.tests package.
+    # This can of course use <include /> to include other packages.
+
+    fiveconfigure.debug_mode = True
+    import quintagroup.seoptimizer
+    zcml.load_config('configure.zcml', quintagroup.seoptimizer)
+    zcml.load_config('overrides.zcml', quintagroup.seoptimizer)
+    fiveconfigure.debug_mode = False
+
+    # We need to tell the testing framework that these products
+    # should be available. This can't happen until after we have loaded
+    # the ZCML. Thus, we do it here. Note the use of installPackage()
+    # instead of installProduct().
+    # This is *only* necessary for packages outside the Products.*
+    # namespace which are also declared as Zope 2 products, using
+    # <five:registerPackage /> in ZCML.
+
+    # We may also need to load dependencies, e.g.:
+    #   ztc.installPackage('borg.localrole')
+
+    ztc.installPackage(PROJECT_NAME)
+
+# The order here is important: We first call the (deferred) function
+# which installs the products we need for this product. Then, we let
+# PloneTestCase set up this product on installation.
+
+setup_product()
+ptc.setupPloneSite(products=[PROJECT_NAME])
+
+
+class TestCase(ptc.PloneTestCase):
+    """We use this base class for all the tests in this package. If
+    necessary, we can put common utility or setup code in here. This
+    applies to unit test cases.
+    """
+
+class FunctionalTestCase(ptc.FunctionalTestCase):
+    """We use this class for functional integration tests that use
+    doctest syntax. Again, we can put basic common utility or setup
+    code in here.
+    """
+
+    #def afterSetUp(self):
+        #roles = ('Member', 'Contributor')
+        #self.portal.portal_membership.addMember('contributor',
+                                                #'secret',
+                                                #roles, [])
+
+#class TestErase(TestCase):
+    ## we use here nested layer for not to make an impact on
+    ## the rest test cases, this test case check uninstall procedure
+    ## thus it has to uninstall package which will be required to
+    ## be installed for other test cases
+    #class layer(PloneSiteLayer):
+        #@classmethod
+        #def setUp(cls):
+            #app = ztc.app()
+            #portal = app.plone
+
+            ## elevate permissions
+            #user = portal.getWrappedOwner()
+            #newSecurityManager(None, user)
+
+            #tool = getToolByName(portal, 'portal_quickinstaller')
+            #if tool.isProductInstalled(PROJECT_NAME):
+                #tool.uninstallProducts([PROJECT_NAME,])
+
+            ## drop elevated perms
+            #noSecurityManager()
+
+            #transaction.commit()
+            #ztc.close(app)
+
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/config.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/config.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/config.py	(revision 1493)
@@ -0,0 +1,32 @@
+from Products.CMFCore.permissions import ManagePortal
+
+from quintagroup.seoptimizer.config import *
+
+PROJECT_NAME = 'quintagroup.seoptimizer'
+
+PROPS = {'stop_words':STOP_WORDS, 'fields':FIELDS, 'additional_keywords': []}
+
+CUSTOM_METATAGS = [{'meta_name'    : 'metatag1',
+                    'meta_content' : 'metatag1value'},
+                   {'meta_name'    : 'metatag2',
+                    'meta_content' : 'metatag2value'},
+                   {'meta_name'    : 'metatag3',
+                    'meta_content' : ''}
+                  ]
+VIEW_METATAGS = ['DC.creator', 'DC.format', 'DC.date.modified', 'DC.date.created', 'DC.type',
+                   'DC.distribution', 'description', 'keywords', 'robots', 'distribution']
+
+GLOBAL_CUSTOM_METATAGS = {'default_custom_metatags':'metatag1|global_metatag1value\nmetatag4|global_metatag4value'}
+
+CONFIGLETS = ({'id':'qSEOptimizer',
+    'name':'Search Engine Optimizer',
+    'action':'string:${portal_url}/seo-controlpanel',
+    'condition':'',
+    'category':'Products',
+    'visible':1,
+    'appId':'qSEOptimizer',
+    'permission':ManagePortal},)
+
+qSEO_CONTENT = ['File','Document','News Item']
+qSEO_FOLDER  = []
+qSEO_TYPES   = qSEO_CONTENT + qSEO_FOLDER
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizer.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizer.py	(revision 1475)
+++ 	(revision )
@@ -1,594 +1,0 @@
-import re
-import string
-import urllib
-from cStringIO import StringIO
-from zope.component import getMultiAdapter
-from Products.Five import zcml, fiveconfigure
-from Testing.ZopeTestCase import installPackage, hasPackage
-from Products.PloneTestCase import PloneTestCase
-from Products.CMFCore.utils import getToolByName
-from Products.CMFCore.permissions import ManagePortal
-from Products.CMFQuickInstallerTool.InstalledProduct import InstalledProduct
-from AccessControl.SecurityManagement import newSecurityManager, noSecurityManager
-
-import quintagroup.seoptimizer
-from quintagroup.seoptimizer.config import *
-
-zcml.load_site()
-zcml.load_config('overrides.zcml', quintagroup.seoptimizer)
-zcml.load_config('configure.zcml', quintagroup.seoptimizer)
-PRODUCT = 'quintagroup.seoptimizer'
-installPackage(PRODUCT)
-props = {'stop_words':STOP_WORDS, 'fields':FIELDS, 'additional_keywords': []}
-
-custom_metatags = [{'meta_name'    : 'metatag1',
-                    'meta_content' : 'metatag1value'},
-                   {'meta_name'    : 'metatag2',
-                    'meta_content' : 'metatag2value'},
-                   {'meta_name'    : 'metatag3',
-                    'meta_content' : ''}
-                  ]
-view_metatags = ['DC.creator', 'DC.format', 'DC.date.modified', 'DC.date.created', 'DC.type',
-                   'DC.distribution', 'description', 'keywords', 'robots', 'distribution']
-
-global_custom_metatags = {'default_custom_metatags':'metatag1|global_metatag1value\nmetatag4|global_metatag4value'}
-
-configlets = ({'id':'qSEOptimizer',
-    'name':'Search Engine Optimizer',
-    'action':'string:${portal_url}/seo-controlpanel',
-    'condition':'',
-    'category':'Products',
-    'visible':1,
-    'appId':'qSEOptimizer',
-    'permission':ManagePortal},)
-
-qSEO_CONTENT = ['File','Document','News Item']
-qSEO_FOLDER  = []
-qSEO_TYPES   = qSEO_CONTENT + qSEO_FOLDER
-
-
-PloneTestCase.setupPloneSite()
-
-class TestBeforeInstall(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.basic_auth = 'mgr:mgrpw'
-        self.portal_path = '/%s' % self.portal.absolute_url(1)
-
-    def testAccessPortalRootAnonymous(self):
-        response = self.publish(self.portal_path)
-        self.assertEqual(response.getStatus(), 200)
-
-    def testAccessPortalRootAuthenticated(self):
-        response = self.publish(self.portal_path, self.basic_auth)
-        self.assertEqual(response.getStatus(), 200)
-
-
-class TestInstallation(PloneTestCase.PloneTestCase):
-
-    def afterSetUp(self):
-        self.properties = getToolByName(self.portal, 'portal_properties')
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
-
-    def testAddingPropertySheet(self):
-        """ Test adding property sheet to portal_properties tool """
-        self.failUnless(hasattr(self.properties.aq_base, PROPERTY_SHEET))
-
-    def testAddingPropertyFields(self):
-        """ Test adding property field to portal_properties.maps_properties sheet """
-        map_sheet = self.properties[PROPERTY_SHEET]
-        for key, value in props.items():
-            self.failUnless(map_sheet.hasProperty(key) and list(map_sheet.getProperty(key)) == value)
-
-    def test_configlet_install(self):
-        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
-        self.assert_(PRODUCT in [a.getId() for a in configTool.listActions()], 'Configlet not found')
-
-    def test_actions_install(self):
-        portal_types = getToolByName(self.portal, 'portal_types')
-        for ptype in portal_types.objectValues():
-            try:
-                #for Plone-2.5 and higher
-                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
-                action = acts and acts[0] or None
-            except AttributeError:
-                action = ptype.getActionById('seo_properties', default=None )
-
-            if ptype.getId() in qSEO_TYPES:
-                self.assert_(action, 'Action for %s not found' % ptype.getId())
-            else:
-                self.assert_(not action, 'Action found for %s' % ptype.getId())
-
-    def test_skins_install(self):
-        skinstool=getToolByName(self.portal, 'portal_skins')
-
-        for skin in skinstool.getSkinSelections():
-            path = skinstool.getSkinPath(skin)
-            path = map( string.strip, string.split( path,',' ) )
-            self.assert_(PRODUCT in path, 'qSEOptimizer layer not found in %s' %skin)
-
-    def test_versionedskin_install(self):
-        skinstool=getToolByName(self.portal, 'portal_skins')
-        mtool = getToolByName(self.portal, 'portal_migration')
-        plone_version = mtool.getFileSystemVersion()
-        if plone_version < "3":
-            for skin in skinstool.getSkinSelections():
-                path = skinstool.getSkinPath(skin)
-                path = map( string.strip, string.split( path,',' ) )
-                self.assert_(PRODUCT+'/%s' % plone_version in path, 'qSEOptimizer versioned layer not found in %s' %skin)
-
-    def test_actions_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
-        portal_types = getToolByName(self.portal, 'portal_types')
-        for ptype in portal_types.objectValues():
-            try:
-                #for Plone-2.5 and higher
-                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
-                action = acts and acts[0] or None
-            except AttributeError:
-                action = ptype.getActionById('seo_properties', default=None )
-
-            self.assert_(not action, 'Action for %s found after uninstallation' % ptype.getId())
-
-    def test_skins_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
-        skinstool=getToolByName(self.portal, 'portal_skins')
-
-        for skin in skinstool.getSkinSelections():
-            path = skinstool.getSkinPath(skin)
-            path = map( string.strip, string.split( path,',' ) )
-            self.assert_(not PRODUCT in path, 'qSEOptimizer layer found in %s after uninstallation' %skin)
-
-    def test_versionedskin_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
-        skinstool=getToolByName(self.portal, 'portal_skins')
-        mtool = getToolByName(self.portal, 'portal_migration')
-        plone_version = mtool.getFileSystemVersion()
-
-        for skin in skinstool.getSkinSelections():
-            path = skinstool.getSkinPath(skin)
-            path = map( string.strip, string.split( path,',' ) )
-            self.assert_(not PRODUCT+'/%s' % plone_version in path, 'qSEOptimizer versioned layer found in %s after uninstallation' %skin)
-
-    def test_configlet_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
-
-        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
-        self.assert_(not PRODUCT in [a.getId() for a in configTool.listActions()], 'Configlet found after uninstallation')
-
-
-class TestResponse(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
-        self.sp = self.portal.portal_properties.seo_properties
-        self.pu = self.portal.plone_utils
-        self.basic_auth = 'portal_manager:secret'
-
-        uf = self.app.acl_users
-        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
-        user = uf.getUserById('portal_manager')
-        if not hasattr(user, 'aq_base'):
-            user = user.__of__(uf)
-        newSecurityManager(None, user)
-
-        '''Preparation for functional testing'''
-        my_doc = self.portal.invokeFactory('Document', id='my_doc')
-        my_doc = self.portal['my_doc']
-        self.canonurl = 'http://nohost/plone/test.html'
-        self.sp.manage_changeProperties(**global_custom_metatags)
-        abs_path = "/%s" % my_doc.absolute_url(1)
-        self.form_data = {'seo_description': 'it is description', 'seo_keywords_override:int': 1, 'seo_custommetatags_override:int': 1,
-                        'seo_robots_override:int': 1, 'seo_robots': 'ALL', 'seo_description_override:int': 1, 'seo_canonical_override:int': 1,
-                        'seo_keywords:list': 'keyword1', 'seo_html_comment': 'no comments',
-                        'seo_title_override:int': 1, 'seo_title': 'hello world', 'seo_html_comment_override:int': 1,
-                        'seo_distribution_override:int': 1, 'seo_distribution': 'Global', 'seo_canonical': self.canonurl, 'form.submitted:int': 1}
-        st = ''
-        for d in custom_metatags:
-            st += '&seo_custommetatags.meta_name:records=%s&seo_custommetatags.meta_content:records=%s' % (d['meta_name'],d['meta_content'])
-        self.publish(path=abs_path+'/@@seo-context-properties', basic=self.basic_auth, request_method='POST', stdin=StringIO(urllib.urlencode(self.form_data)+st))
-        #self.publish(abs_path+'/@@seo-context-properties?%s' % urllib.urlencode(self.form_data), self.basic_auth)
-
-        wf_tool = self.portal.portal_workflow
-        wf_tool.doActionFor(my_doc, 'publish')
-
-        self.abs_path = abs_path
-        self.my_doc = my_doc
-        self.html = self.publish(abs_path, self.basic_auth).getBody()
-
-        # now setup page with title equal to plone site's title
-        my_doc2 = self.portal.invokeFactory('Document', id='my_doc2')
-        my_doc2 = self.portal['my_doc2']
-        my_doc2.update(title=self.portal.Title())
-        wf_tool.doActionFor(my_doc2, 'publish')
-        abs_path2 = "/%s" % my_doc2.absolute_url(1)
-        self.html2 = self.publish(abs_path2, self.basic_auth).getBody()
-
-    def testTitle(self):
-        m = re.match('.*<title>\\s*hello world\\s*</title>', self.html, re.S|re.M)
-        self.assert_(m, 'Title not set in')
-
-    def testTitleDuplication(self):
-        """If we are not overriding page title and current page title equals title of the plone site
-        then there should be no concatenation of both titles. Only one should be displayed.
-        """
-        m = re.match('.*<title>\\s*%s\\s*</title>' % self.portal.Title(), self.html2, re.S|re.M)
-        self.assert_(m, 'Title is not set correctly, perhaps it is duplicated with plone site title')
-
-    def testDescription(self):
-        m = re.match('.*<meta name="description" content="it is description" />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta content="it is description" name="description" />', self.html, re.S|re.M)
-        self.assert_(m, 'Description not set in')
-
-    def testKeywords(self):
-        m = re.match('.*<meta name="keywords" content="keyword1" />', self.html, re.S|re.M)
-        if not m:
-             m = re.match('.*<meta content="keyword1" name="keywords" />', self.html, re.S|re.M)
-        self.assert_(m, 'Keywords not set in')
-
-    def testRobots(self):
-        m = re.match('.*<meta name="robots" content="ALL" />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta content="ALL" name="robots" />', self.html, re.S|re.M)
-        self.assert_(m, 'Robots not set in')
-
-    def testDistribution(self):
-        m = re.match('.*<meta name="distribution" content="Global" />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta content="Global" name="distribution" />', self.html, re.S|re.M)
-        self.assert_(m, 'Distribution not set in')
-
-    def testHTMLComments(self):
-        m = re.match('.*<!--\\s*no comments\\s*-->', self.html, re.S|re.M)
-        self.assert_(m, 'Comments not set in')
-
-    def testTagsOrder(self):
-        mtop = self.sp.getProperty('metatags_order')
-        metatags_order = [t.split(' ')[0] for t in mtop if len(t.split(' '))==2 and t.split(' ')[0] in view_metatags]
-        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
-        self.assert_(m, "Meta tags order not supported.")
-
-        mtop = list(mtop)
-        mtop.reverse()
-        metatags_order = [t.split(' ')[0] for t in mtop if len(t.split(' '))==2 and t.split(' ')[0] in view_metatags]
-        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
-        self.assertFalse(m, "Meta tags order not supported.")
-
-        self.sp.manage_changeProperties(**{'metatags_order':tuple(mtop)})
-        html = self.publish(self.abs_path, self.basic_auth).getBody()
-        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
-        self.assertFalse(m, "Meta tags order not supported.")
-
-        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), html, re.S|re.M)
-        self.assert_(m, "Meta tags order not supported.")
-
-
-    def testCustomMetaTags(self):
-        for tag in custom_metatags:
-            m = re.search('<meta name="%(meta_name)s" content="%(meta_content)s" />' % tag, self.html, re.S|re.M)
-            if not m:
-                m = re.search('<meta content="%(meta_content)s" name="%(meta_name)s" />' % tag, self.html, re.S|re.M)
-            if tag['meta_content']:
-                self.assert_(m, "Custom meta tag %s not applied." % tag['meta_name'])
-            else:
-                self.assert_(not m, "Meta tag %s has no content, but is present in the page." % tag['meta_name'])
-        m = re.search('<meta name="metatag4" content="global_metatag4value" />' , self.html, re.S|re.M)
-        if not m:
-            m = re.search('<meta content="global_metatag4value" name="metatag4" />' , self.html, re.S|re.M)
-        self.assert_(m, "Global custom meta tag %s not applied." % 'metatag4')
-
-    def testDeleteCustomMetaTags(self):
-        self.sp.manage_changeProperties(**{'default_custom_metatags':'metatag1|global_metatag1value'})
-        my_doc = self.my_doc
-        self.form_data = {'seo_custommetatags': custom_metatags,  'seo_custommetatags_override:int': 0, 'form.submitted:int': 1}
-        self.publish(path=self.abs_path+'/@@seo-context-properties', basic=self.basic_auth, request_method='POST', stdin=StringIO(urllib.urlencode(self.form_data)))
-        html = self.publish(self.abs_path, self.basic_auth).getBody()
-        m = re.search('<meta name="metatag4" content="global_metatag4value" />' , html, re.S|re.M)
-        if not m:
-            m = re.search('<meta content="global_metatag4value" name="metatag4" />' , html, re.S|re.M)
-        self.assert_(not m, "Global custom meta tag %s is prosent in the page." % 'metatag4')
-        m = re.search('<meta name="metatag1" content="global_metatag1value" />' , html, re.S|re.M)
-        if not m:
-            m = re.search('<meta content="global_metatag1value" name="metatag1" />' , html, re.S|re.M)
-        self.assert_(m, "Global custom meta tag %s not applied." % 'metatag1')
-
-    def testCanonical(self):
-        m = re.match('.*<link rel="canonical" href="%s" />' % self.canonurl, self.html, re.S|re.M)
-        self.assert_(m, self.canonurl)
-
-    def testDefaultCanonical(self):
-        """Default canonical url mast add document absolute_url
-        """
-        # Delete custom canonical url
-        my_doc = self.portal['my_doc']
-        my_doc._delProperty(id='qSEO_canonical')
-        # Get document without customized canonical url
-        abs_path = "/%s" % my_doc.absolute_url(1)
-        self.html = self.publish(abs_path, self.basic_auth).getBody()
-
-        my_url = my_doc.absolute_url()
-        m = re.match('.*<link rel="canonical" href="%s" />' % my_url, self.html, re.S|re.M)
-        self.assert_(m, my_url)
-
-class TestAdditionalKeywords(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
-        self.sp = self.portal.portal_properties.seo_properties
-        self.pu = self.portal.plone_utils
-
-        self.basic_auth = 'portal_manager:secret'
-        uf = self.app.acl_users
-        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
-        user = uf.getUserById('portal_manager')
-        if not hasattr(user, 'aq_base'):
-            user = user.__of__(uf)
-        newSecurityManager(None, user)
-
-        '''Preparation for functional testing'''
-        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
-        self.my_doc = self.portal['my_doc']
-
-    def test_additional_keywords_in_configlet(self):
-        quoted_keywords = urllib.quote('foo\nbar')
-        path = self.portal.id+'/@@seo-controlpanel?additionalKeywords:lines=%s&form.submitted=1'%quoted_keywords
-        self.publish(path, self.basic_auth)
-        self.assertEqual(self.sp.additional_keywords, ('foo', 'bar'))
-        self.publish(self.portal.id+'/@@seo-controlpanel?form.submitted=1', self.basic_auth)
-        self.assertEqual(self.sp.additional_keywords, ())
-
-    def test_listMetaTags_empty(self):
-        metatags = self.pu.listMetaTags(self.my_doc)
-        self.assert_('keywords' not in metatags)
-
-    def test_listMetaTags_one(self):        
-        self.my_doc.manage_addProperty('qSEO_keywords', ('foo',), 'lines')
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ content="foo"\ name="keywords"\ />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
-        self.assert_(m, "No 'foo' keyword find")
-
-    def test_listMetaTags_two(self):        
-        self.my_doc.manage_addProperty('qSEO_keywords', ('foo', 'bar'), 'lines')
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ content="foo, bar"\ name="keywords"\ />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
-        self.assert_(m, "No 'foo, bar' keyword find")
-
-    def test_additional_keywords_in_listMetaTags_empty(self):        
-        self.sp.additional_keywords = ('foo',)
-        metatags = self.pu.listMetaTags(self.my_doc)
-        self.assert_('keywords' not in metatags)
-
-    def test_additional_keywords_in_listMetaTags_one(self):
-        self.my_doc.setText('<p>foo</p>')
-        self.sp.additional_keywords = ('foo',)
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ content="foo"\ name="keywords"\ />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
-        self.assert_(m, "No 'foo' keyword find")
-
-    def test_additional_keywords_in_listMetaTags_two(self):
-        self.my_doc.setText('<p>foo bar</p>')
-        self.sp.additional_keywords = ('foo', 'bar')
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.match('.*<meta\ content="foo, bar"\ name="keywords"\ />', self.html, re.S|re.M)
-        if not m:
-            m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
-        self.assert_(m, "No 'foo, bar' keyword find")
-
-    def test_additional_keywords_in_listMetaTags_merge(self):
-        self.my_doc.setText('<p>foo bar</p>')
-        self.sp.additional_keywords = ('foo', 'bar')
-        self.my_doc.manage_addProperty('qSEO_keywords', ('baz',), 'lines')
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m = re.findall('.*(<meta\s+(?:content="(?:(?:baz|bar|foo),\s*(?:baz|foo|bar),\s*(?:baz|foo|bar)"\s*)|(?:name="keywords"\s*)){2}/>)', self.html, re.S|re.M)
-        self.assert_(m, "No 'foo, bar, baz' keyword find")
-
-
-class TestExposeDCMetaTags(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.qi = self.portal.portal_quickinstaller
-        self.sp = self.portal.portal_properties.site_properties
-        self.qi.installProduct(PRODUCT)
-        self.basic_auth = 'portal_manager:secret'
-        uf = self.app.acl_users
-        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
-        user = uf.getUserById('portal_manager')
-        if not hasattr(user, 'aq_base'):
-            user = user.__of__(uf)
-        newSecurityManager(None, user)
-
-        '''Preparation for functional testing'''
-        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
-        self.my_doc = self.portal['my_doc']
-
-    def test_exposeDCMetaTags_in_configletOn(self):
-        path = self.portal.id+'/@@seo-controlpanel?exposeDCMetaTags=True&form.submitted=1'
-        self.publish(path, self.basic_auth)
-        self.assert_(self.sp.exposeDCMetaTags)
-
-    def test_exposeDCMetaTags_in_configletOff(self):
-        self.publish(self.portal.id+'/@@seo-controlpanel?form.submitted=1', self.basic_auth)
-        self.assert_(not self.sp.exposeDCMetaTags)
-
-    def test_exposeDCMetaTagsPropertyOff(self):
-        self.sp.manage_changeProperties(exposeDCMetaTags = False)
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m1 = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M)
-        if not m1:
-            m1 = re.match('.*<meta content=".*?" name="DC.format" />', self.html, re.S|re.M)
-        m2 = re.match('.*<meta name="DC.distribution" content=".*?" />', self.html, re.S|re.M)
-        if not m2:
-            m2 = re.match('.*<meta content=".*?" name="DC.distribution" />', self.html, re.S|re.M)
-        m = m1 or m2
-        self.assert_(not m, 'DC meta tags avaliable when exposeDCMetaTags=False')
-
-    def test_exposeDCMetaTagsPropertyOn(self):
-        self.sp.manage_changeProperties(exposeDCMetaTags = True)
-        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
-        m1 = re.match('.*<meta\ content=".*?"\ name="DC.format"\ />', self.html, re.S|re.M)
-        if not m1:
-            m1 = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M)
-        m2 = re.match('.*<meta\ content=".*?"\ name="DC.type"\ />', self.html, re.S|re.M)
-        if not m2:
-            m2 = re.match('.*<meta\ name="DC.type"\ content=".*?"\ />', self.html, re.S|re.M)
-        m = m1 and m2
-        self.assert_(m, 'DC meta tags not avaliable when createManager=True')
-
-
-class TestMetaTagsDuplication(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.qi = self.portal.portal_quickinstaller
-        self.basic_auth = 'portal_manager:secret'
-        uf = self.app.acl_users
-        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
-        user = uf.getUserById('portal_manager')
-        if not hasattr(user, 'aq_base'):
-            user = user.__of__(uf)
-        newSecurityManager(None, user)
-
-        '''Preparation for functional testing'''
-        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
-        self.my_doc = self.portal['my_doc']
-        self.my_doc.update(description="Document description")
-
-    def test_GeneratorMeta(self):
-        # Get document without customized canonical url
-        abs_path = "/%s" % self.my_doc.absolute_url(1)
-        regen = re.compile('<meta\s+[^>]*name=\"generator\"[^>]*>', re.S|re.M)
-
-        # Before product installation
-        html = self.publish(abs_path, self.basic_auth).getBody()
-        lengen = len(regen.findall(html))
-        self.assert_(lengen==1, "There is %d generator meta tag(s) " \
-           "before seoptimizer installation" % lengen)
-
-#         # After PRODUCT installation
-#         self.qi.installProduct(PRODUCT)
-#         html = self.publish(abs_path, self.basic_auth).getBody()
-#         lengen = len(regen.findall(html))
-#         self.assert_(lengen==1, "There is %d generator meta tag(s) " \
-#            "after seoptimizer installation" % lengen)
-
-    def test_DescriptionMeta(self):
-        # Get document without customized canonical url
-        abs_path = "/%s" % self.my_doc.absolute_url(1)
-        regen = re.compile('<meta\s+[^>]*name=\"description\"[^>]*>', re.S|re.M)
-
-        # Before product installation
-        html = self.publish(abs_path, self.basic_auth).getBody()
-        lendesc = len(regen.findall(html))
-        self.assert_(lendesc==1, "There is %d DESCRIPTION meta tag(s) " \
-           "before seoptimizer installation" % lendesc)
-
-#         # After PRODUCT installation
-#         self.qi.installProduct(PRODUCT)
-#         html = self.publish(abs_path, self.basic_auth).getBody()
-#         lendesc = len(regen.findall(html))
-#         self.assert_(lendesc==1, "There is %d DESCRIPTION meta tag(s) " \
-#            "after seoptimizer installation" % lendesc)
-
-class TestBaseURL(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
-        #self.portal.changeSkin('Plone Default')
-
-        self.basic_auth = 'portal_manager:secret'
-        uf = self.app.acl_users
-        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
-        user = uf.getUserById('portal_manager')
-        if not hasattr(user, 'aq_base'):
-            user = user.__of__(uf)
-        newSecurityManager(None, user)
-
-    def test_notFolderBaseURL(self):
-        my_doc = self.portal.invokeFactory('Document', id='my_doc')
-        my_doc = self.portal['my_doc']
-        regen = re.compile('<base\s+[^>]*href=\"([^\"]*)\"[^>]*>', re.S|re.M)
-
-        path = "/%s" % my_doc.absolute_url(1)
-        html = self.publish(path, self.basic_auth).getBody()
-        burls = regen.findall(html)
-
-        mydocurl = my_doc.absolute_url()
-        self.assert_(not [1 for burl in burls if not burl==mydocurl],
-           "Wrong BASE URL for document: %s, all must be: %s" % (burls, mydocurl))
-
-    def test_folderBaseURL(self):
-        my_fldr = self.portal.invokeFactory('Folder', id='my_fldr')
-        my_fldr = self.portal['my_fldr']
-        regen = re.compile('<base\s+[^>]*href=\"([^\"]*)\"[^>]*>', re.S|re.M)
-        
-        path = "/%s" % my_fldr.absolute_url(1)
-        html = self.publish(path, self.basic_auth).getBody()
-        burls = regen.findall(html)
-
-        myfldrurl = my_fldr.absolute_url() + '/'
-        self.assert_(not [1 for burl in burls if not burl==myfldrurl],
-           "Wrong BASE URL for folder: %s , all must be : %s" % (burls, myfldrurl))
-
-
-class TestBugs(PloneTestCase.FunctionalTestCase):
-
-    def afterSetUp(self):
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
-        #self.portal.changeSkin('Plone Default')
-
-        self.basic_auth = 'portal_manager:secret'
-        uf = self.app.acl_users
-        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
-        user = uf.getUserById('portal_manager')
-        if not hasattr(user, 'aq_base'):
-            user = user.__of__(uf)
-        newSecurityManager(None, user)
-
-    def test_modification_date(self):
-        """ Modification date changing on SEO properties edit """
-        my_doc = self.portal.invokeFactory('Document', id='my_doc')
-        my_doc = self.portal['my_doc']
-
-        md_before = my_doc.modification_date
-        abs_path = "/%s" % my_doc.absolute_url(1)
-        form_data = {'seo_title': 'New Title',  'seo_title_override:int': 1, 'form.submitted:int': 1}
-        self.publish(path=abs_path+'/@@seo-context-properties', basic=self.basic_auth, request_method='POST', stdin=StringIO(urllib.urlencode(form_data)))
-        md_after = my_doc.modification_date
-        self.assertNotEqual(md_before, md_after)
-
-
-TESTS = [TestBeforeInstall,
-         TestInstallation,
-         TestResponse,
-         TestExposeDCMetaTags,
-         TestAdditionalKeywords,
-         TestMetaTagsDuplication,
-         TestBaseURL,
-         TestBugs,
-        ]
-
-def test_suite():
-    from unittest import TestSuite, makeSuite
-    suite = TestSuite()
-    for suite_class in TESTS:
-        suite.addTest(makeSuite(suite_class))
-
-    return suite
-
-if __name__ == '__main__':
-    framework()
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerAdditionalKeywords.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerAdditionalKeywords.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerAdditionalKeywords.py	(revision 1493)
@@ -0,0 +1,88 @@
+import urllib, re
+from base import getToolByName, FunctionalTestCase, newSecurityManager
+from config import *
+
+class TestAdditionalKeywords(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PROJECT_NAME)
+        self.sp = self.portal.portal_properties.seo_properties
+        self.pu = self.portal.plone_utils
+
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+        '''Preparation for functional testing'''
+        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+
+    def test_additional_keywords_in_configlet(self):
+        quoted_keywords = urllib.quote('foo\nbar')
+        path = self.portal.id+'/@@seo-controlpanel?additionalKeywords:lines=%s&form.submitted=1'%quoted_keywords
+        self.publish(path, self.basic_auth)
+        self.assertEqual(self.sp.additional_keywords, ('foo', 'bar'))
+        self.publish(self.portal.id+'/@@seo-controlpanel?form.submitted=1', self.basic_auth)
+        self.assertEqual(self.sp.additional_keywords, ())
+
+    def test_listMetaTags_empty(self):
+        metatags = self.pu.listMetaTags(self.my_doc)
+        self.assert_('keywords' not in metatags)
+
+    def test_listMetaTags_one(self):        
+        self.my_doc.manage_addProperty('qSEO_keywords', ('foo',), 'lines')
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m = re.match('.*<meta\ content="foo"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
+        self.assert_(m, "No 'foo' keyword find")
+
+    def test_listMetaTags_two(self):        
+        self.my_doc.manage_addProperty('qSEO_keywords', ('foo', 'bar'), 'lines')
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m = re.match('.*<meta\ content="foo, bar"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
+        self.assert_(m, "No 'foo, bar' keyword find")
+
+    def test_additional_keywords_in_listMetaTags_empty(self):        
+        self.sp.additional_keywords = ('foo',)
+        metatags = self.pu.listMetaTags(self.my_doc)
+        self.assert_('keywords' not in metatags)
+
+    def test_additional_keywords_in_listMetaTags_one(self):
+        self.my_doc.setText('<p>foo</p>')
+        self.sp.additional_keywords = ('foo',)
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m = re.match('.*<meta\ content="foo"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
+        self.assert_(m, "No 'foo' keyword find")
+
+    def test_additional_keywords_in_listMetaTags_two(self):
+        self.my_doc.setText('<p>foo bar</p>')
+        self.sp.additional_keywords = ('foo', 'bar')
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m = re.match('.*<meta\ content="foo, bar"\ name="keywords"\ />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
+        self.assert_(m, "No 'foo, bar' keyword find")
+
+    def test_additional_keywords_in_listMetaTags_merge(self):
+        self.my_doc.setText('<p>foo bar</p>')
+        self.sp.additional_keywords = ('foo', 'bar')
+        self.my_doc.manage_addProperty('qSEO_keywords', ('baz',), 'lines')
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m = re.findall('.*(<meta\s+(?:content="(?:(?:baz|bar|foo),\s*(?:baz|foo|bar),\s*(?:baz|foo|bar)"\s*)|(?:name="keywords"\s*)){2}/>)', self.html, re.S|re.M)
+        self.assert_(m, "No 'foo, bar, baz' keyword find")
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestAdditionalKeywords))
+    return suite
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerBaseURL.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerBaseURL.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerBaseURL.py	(revision 1493)
@@ -0,0 +1,50 @@
+import re
+from base import getToolByName, FunctionalTestCase, newSecurityManager
+from config import *
+
+class TestBaseURL(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PROJECT_NAME)
+        #self.portal.changeSkin('Plone Default')
+
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+    def test_notFolderBaseURL(self):
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        my_doc = self.portal['my_doc']
+        regen = re.compile('<base\s+[^>]*href=\"([^\"]*)\"[^>]*>', re.S|re.M)
+
+        path = "/%s" % my_doc.absolute_url(1)
+        html = self.publish(path, self.basic_auth).getBody()
+        burls = regen.findall(html)
+
+        mydocurl = my_doc.absolute_url()
+        self.assert_(not [1 for burl in burls if not burl==mydocurl],
+           "Wrong BASE URL for document: %s, all must be: %s" % (burls, mydocurl))
+
+    def test_folderBaseURL(self):
+        my_fldr = self.portal.invokeFactory('Folder', id='my_fldr')
+        my_fldr = self.portal['my_fldr']
+        regen = re.compile('<base\s+[^>]*href=\"([^\"]*)\"[^>]*>', re.S|re.M)
+        
+        path = "/%s" % my_fldr.absolute_url(1)
+        html = self.publish(path, self.basic_auth).getBody()
+        burls = regen.findall(html)
+
+        myfldrurl = my_fldr.absolute_url() + '/'
+        self.assert_(not [1 for burl in burls if not burl==myfldrurl],
+           "Wrong BASE URL for folder: %s , all must be : %s" % (burls, myfldrurl))
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestBaseURL))
+    return suite
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerBugs.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerBugs.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerBugs.py	(revision 1493)
@@ -0,0 +1,37 @@
+import urllib, re
+from cStringIO import StringIO
+from base import getToolByName, FunctionalTestCase, newSecurityManager
+from config import *
+
+class TestBugs(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PROJECT_NAME)
+        #self.portal.changeSkin('Plone Default')
+
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+    def test_modification_date(self):
+        """ Modification date changing on SEO properties edit """
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        my_doc = self.portal['my_doc']
+
+        md_before = my_doc.modification_date
+        abs_path = "/%s" % my_doc.absolute_url(1)
+        form_data = {'seo_title': 'New Title',  'seo_title_override:int': 1, 'form.submitted:int': 1}
+        self.publish(path=abs_path+'/@@seo-context-properties', basic=self.basic_auth, request_method='POST', stdin=StringIO(urllib.urlencode(form_data)))
+        md_after = my_doc.modification_date
+        self.assertNotEqual(md_before, md_after)
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestBugs))
+    return suite
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerExposeDCMetaTags.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerExposeDCMetaTags.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerExposeDCMetaTags.py	(revision 1493)
@@ -0,0 +1,60 @@
+import re
+from base import getToolByName, FunctionalTestCase, newSecurityManager
+from config import *
+
+class TestExposeDCMetaTags(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.sp = self.portal.portal_properties.site_properties
+        self.qi.installProduct(PROJECT_NAME)
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+        '''Preparation for functional testing'''
+        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+
+    def test_exposeDCMetaTags_in_configletOn(self):
+        path = self.portal.id+'/@@seo-controlpanel?exposeDCMetaTags=True&form.submitted=1'
+        self.publish(path, self.basic_auth)
+        self.assert_(self.sp.exposeDCMetaTags)
+
+    def test_exposeDCMetaTags_in_configletOff(self):
+        self.publish(self.portal.id+'/@@seo-controlpanel?form.submitted=1', self.basic_auth)
+        self.assert_(not self.sp.exposeDCMetaTags)
+
+    def test_exposeDCMetaTagsPropertyOff(self):
+        self.sp.manage_changeProperties(exposeDCMetaTags = False)
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m1 = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M)
+        if not m1:
+            m1 = re.match('.*<meta content=".*?" name="DC.format" />', self.html, re.S|re.M)
+        m2 = re.match('.*<meta name="DC.distribution" content=".*?" />', self.html, re.S|re.M)
+        if not m2:
+            m2 = re.match('.*<meta content=".*?" name="DC.distribution" />', self.html, re.S|re.M)
+        m = m1 or m2
+        self.assert_(not m, 'DC meta tags avaliable when exposeDCMetaTags=False')
+
+    def test_exposeDCMetaTagsPropertyOn(self):
+        self.sp.manage_changeProperties(exposeDCMetaTags = True)
+        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
+        m1 = re.match('.*<meta\ content=".*?"\ name="DC.format"\ />', self.html, re.S|re.M)
+        if not m1:
+            m1 = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M)
+        m2 = re.match('.*<meta\ content=".*?"\ name="DC.type"\ />', self.html, re.S|re.M)
+        if not m2:
+            m2 = re.match('.*<meta\ name="DC.type"\ content=".*?"\ />', self.html, re.S|re.M)
+        m = m1 and m2
+        self.assert_(m, 'DC meta tags not avaliable when createManager=True')
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestExposeDCMetaTags))
+    return suite
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerInstall.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerInstall.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerInstall.py	(revision 1493)
@@ -0,0 +1,126 @@
+#
+# Test product's installation
+#
+import string
+from base import getToolByName, FunctionalTestCase, TestCase, newSecurityManager
+from config import *
+
+
+class TestBeforeInstall(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.basic_auth = 'mgr:mgrpw'
+        self.portal_path = '/%s' % self.portal.absolute_url(1)
+
+    def testAccessPortalRootAnonymous(self):
+        response = self.publish(self.portal_path)
+        self.assertEqual(response.getStatus(), 200)
+
+    def testAccessPortalRootAuthenticated(self):
+        response = self.publish(self.portal_path, self.basic_auth)
+        self.assertEqual(response.getStatus(), 200)
+
+
+class TestInstallation(TestCase):
+
+    def afterSetUp(self):
+        self.properties = getToolByName(self.portal, 'portal_properties')
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PROJECT_NAME)
+
+    def testAddingPropertySheet(self):
+        """ Test adding property sheet to portal_properties tool """
+        self.failUnless(hasattr(self.properties.aq_base, PROPERTY_SHEET))
+
+    def testAddingPropertyFields(self):
+        """ Test adding property field to portal_properties.maps_properties sheet """
+        map_sheet = self.properties[PROPERTY_SHEET]
+        for key, value in PROPS.items():
+            self.failUnless(map_sheet.hasProperty(key) and list(map_sheet.getProperty(key)) == value)
+
+    def test_configlet_install(self):
+        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
+        self.assert_(PROJECT_NAME in [a.getId() for a in configTool.listActions()], 'Configlet not found')
+
+    def test_actions_install(self):
+        portal_types = getToolByName(self.portal, 'portal_types')
+        for ptype in portal_types.objectValues():
+            try:
+                #for Plone-2.5 and higher
+                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+                action = acts and acts[0] or None
+            except AttributeError:
+                action = ptype.getActionById('seo_properties', default=None )
+
+            if ptype.getId() in qSEO_TYPES:
+                self.assert_(action, 'Action for %s not found' % ptype.getId())
+            else:
+                self.assert_(not action, 'Action found for %s' % ptype.getId())
+
+    def test_skins_install(self):
+        skinstool=getToolByName(self.portal, 'portal_skins')
+
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            self.assert_(PROJECT_NAME in path, 'qSEOptimizer layer not found in %s' %skin)
+
+    def test_versionedskin_install(self):
+        skinstool=getToolByName(self.portal, 'portal_skins')
+        mtool = getToolByName(self.portal, 'portal_migration')
+        plone_version = mtool.getFileSystemVersion()
+        if plone_version < "3":
+            for skin in skinstool.getSkinSelections():
+                path = skinstool.getSkinPath(skin)
+                path = map( string.strip, string.split( path,',' ) )
+                self.assert_(PROJECT_NAME+'/%s' % plone_version in path, 'qSEOptimizer versioned layer not found in %s' %skin)
+
+    def test_actions_uninstall(self):
+        self.qi.uninstallProducts([PROJECT_NAME])
+        self.assertNotEqual(self.qi.isProductInstalled(PROJECT_NAME), True,'qSEOptimizer is already installed')
+        portal_types = getToolByName(self.portal, 'portal_types')
+        for ptype in portal_types.objectValues():
+            try:
+                #for Plone-2.5 and higher
+                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
+                action = acts and acts[0] or None
+            except AttributeError:
+                action = ptype.getActionById('seo_properties', default=None )
+
+            self.assert_(not action, 'Action for %s found after uninstallation' % ptype.getId())
+
+    def test_skins_uninstall(self):
+        self.qi.uninstallProducts([PROJECT_NAME])
+        self.assertNotEqual(self.qi.isProductInstalled(PROJECT_NAME), True,'qSEOptimizer is already installed')
+        skinstool=getToolByName(self.portal, 'portal_skins')
+
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            self.assert_(not PROJECT_NAME in path, 'qSEOptimizer layer found in %s after uninstallation' %skin)
+
+    def test_versionedskin_uninstall(self):
+        self.qi.uninstallProducts([PROJECT_NAME])
+        self.assertNotEqual(self.qi.isProductInstalled(PROJECT_NAME), True,'qSEOptimizer is already installed')
+        skinstool=getToolByName(self.portal, 'portal_skins')
+        mtool = getToolByName(self.portal, 'portal_migration')
+        plone_version = mtool.getFileSystemVersion()
+
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            self.assert_(not PROJECT_NAME+'/%s' % plone_version in path, 'qSEOptimizer versioned layer found in %s after uninstallation' %skin)
+
+    def test_configlet_uninstall(self):
+        self.qi.uninstallProducts([PROJECT_NAME])
+        self.assertNotEqual(self.qi.isProductInstalled(PROJECT_NAME), True,'qSEOptimizer is already installed')
+
+        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
+        self.assert_(not PROJECT_NAME in [a.getId() for a in configTool.listActions()], 'Configlet found after uninstallation')
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestBeforeInstall))
+    suite.addTest(makeSuite(TestInstallation))
+    return suite
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerMetaTagsDuplication.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerMetaTagsDuplication.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerMetaTagsDuplication.py	(revision 1493)
@@ -0,0 +1,61 @@
+import re
+from base import getToolByName, FunctionalTestCase, newSecurityManager
+from config import *
+
+class TestMetaTagsDuplication(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.basic_auth = 'portal_manager:secret'
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+        '''Preparation for functional testing'''
+        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        self.my_doc = self.portal['my_doc']
+        self.my_doc.update(description="Document description")
+
+    def test_GeneratorMeta(self):
+        # Get document without customized canonical url
+        abs_path = "/%s" % self.my_doc.absolute_url(1)
+        regen = re.compile('<meta\s+[^>]*name=\"generator\"[^>]*>', re.S|re.M)
+
+        # Before product installation
+        html = self.publish(abs_path, self.basic_auth).getBody()
+        lengen = len(regen.findall(html))
+        self.assert_(lengen==1, "There is %d generator meta tag(s) " \
+           "before seoptimizer installation" % lengen)
+
+#         # After PROJECT_NAME installation
+#         self.qi.installProduct(PROJECT_NAME)
+#         html = self.publish(abs_path, self.basic_auth).getBody()
+#         lengen = len(regen.findall(html))
+#         self.assert_(lengen==1, "There is %d generator meta tag(s) " \
+#            "after seoptimizer installation" % lengen)
+
+    def test_DescriptionMeta(self):
+        # Get document without customized canonical url
+        abs_path = "/%s" % self.my_doc.absolute_url(1)
+        regen = re.compile('<meta\s+[^>]*name=\"description\"[^>]*>', re.S|re.M)
+
+        # Before product installation
+        html = self.publish(abs_path, self.basic_auth).getBody()
+        lendesc = len(regen.findall(html))
+        self.assert_(lendesc==1, "There is %d DESCRIPTION meta tag(s) " \
+           "before seoptimizer installation" % lendesc)
+
+#         # After PROJECT_NAME installation
+#         self.qi.installProduct(PROJECT_NAME)
+#         html = self.publish(abs_path, self.basic_auth).getBody()
+#         lendesc = len(regen.findall(html))
+#         self.assert_(lendesc==1, "There is %d DESCRIPTION meta tag(s) " \
+#            "after seoptimizer installation" % lendesc)
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestMetaTagsDuplication))
+    return suite
Index: quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerResponse.py
===================================================================
--- quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerResponse.py	(revision 1493)
+++ quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizerResponse.py	(revision 1493)
@@ -0,0 +1,165 @@
+import urllib, re
+from cStringIO import StringIO
+from base import getToolByName, FunctionalTestCase, newSecurityManager
+from config import *
+
+class TestResponse(FunctionalTestCase):
+
+    def afterSetUp(self):
+        self.qi = self.portal.portal_quickinstaller
+        self.qi.installProduct(PROJECT_NAME)
+        self.sp = self.portal.portal_properties.seo_properties
+        self.pu = self.portal.plone_utils
+        self.basic_auth = 'portal_manager:secret'
+
+        uf = self.app.acl_users
+        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
+        user = uf.getUserById('portal_manager')
+        if not hasattr(user, 'aq_base'):
+            user = user.__of__(uf)
+        newSecurityManager(None, user)
+
+        '''Preparation for functional testing'''
+        my_doc = self.portal.invokeFactory('Document', id='my_doc')
+        my_doc = self.portal['my_doc']
+        self.canonurl = 'http://nohost/plone/test.html'
+        self.sp.manage_changeProperties(**GLOBAL_CUSTOM_METATAGS)
+        abs_path = "/%s" % my_doc.absolute_url(1)
+        self.form_data = {'seo_description': 'it is description', 'seo_keywords_override:int': 1, 'seo_custommetatags_override:int': 1,
+                        'seo_robots_override:int': 1, 'seo_robots': 'ALL', 'seo_description_override:int': 1, 'seo_canonical_override:int': 1,
+                        'seo_keywords:list': 'keyword1', 'seo_html_comment': 'no comments',
+                        'seo_title_override:int': 1, 'seo_title': 'hello world', 'seo_html_comment_override:int': 1,
+                        'seo_distribution_override:int': 1, 'seo_distribution': 'Global', 'seo_canonical': self.canonurl, 'form.submitted:int': 1}
+        st = ''
+        for d in CUSTOM_METATAGS:
+            st += '&seo_custommetatags.meta_name:records=%s&seo_custommetatags.meta_content:records=%s' % (d['meta_name'],d['meta_content'])
+        self.publish(path=abs_path+'/@@seo-context-properties', basic=self.basic_auth, request_method='POST', stdin=StringIO(urllib.urlencode(self.form_data)+st))
+        #self.publish(abs_path+'/@@seo-context-properties?%s' % urllib.urlencode(self.form_data), self.basic_auth)
+
+        wf_tool = self.portal.portal_workflow
+        wf_tool.doActionFor(my_doc, 'publish')
+
+        self.abs_path = abs_path
+        self.my_doc = my_doc
+        self.html = self.publish(abs_path, self.basic_auth).getBody()
+
+        # now setup page with title equal to plone site's title
+        my_doc2 = self.portal.invokeFactory('Document', id='my_doc2')
+        my_doc2 = self.portal['my_doc2']
+        my_doc2.update(title=self.portal.Title())
+        wf_tool.doActionFor(my_doc2, 'publish')
+        abs_path2 = "/%s" % my_doc2.absolute_url(1)
+        self.html2 = self.publish(abs_path2, self.basic_auth).getBody()
+
+    def testTitle(self):
+        m = re.match('.*<title>\\s*hello world\\s*</title>', self.html, re.S|re.M)
+        self.assert_(m, 'Title not set in')
+
+    def testTitleDuplication(self):
+        """If we are not overriding page title and current page title equals title of the plone site
+        then there should be no concatenation of both titles. Only one should be displayed.
+        """
+        m = re.match('.*<title>\\s*%s\\s*</title>' % self.portal.Title(), self.html2, re.S|re.M)
+        self.assert_(m, 'Title is not set correctly, perhaps it is duplicated with plone site title')
+
+    def testDescription(self):
+        m = re.match('.*<meta name="description" content="it is description" />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta content="it is description" name="description" />', self.html, re.S|re.M)
+        self.assert_(m, 'Description not set in')
+
+    def testKeywords(self):
+        m = re.match('.*<meta name="keywords" content="keyword1" />', self.html, re.S|re.M)
+        if not m:
+             m = re.match('.*<meta content="keyword1" name="keywords" />', self.html, re.S|re.M)
+        self.assert_(m, 'Keywords not set in')
+
+    def testRobots(self):
+        m = re.match('.*<meta name="robots" content="ALL" />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta content="ALL" name="robots" />', self.html, re.S|re.M)
+        self.assert_(m, 'Robots not set in')
+
+    def testDistribution(self):
+        m = re.match('.*<meta name="distribution" content="Global" />', self.html, re.S|re.M)
+        if not m:
+            m = re.match('.*<meta content="Global" name="distribution" />', self.html, re.S|re.M)
+        self.assert_(m, 'Distribution not set in')
+
+    def testHTMLComments(self):
+        m = re.match('.*<!--\\s*no comments\\s*-->', self.html, re.S|re.M)
+        self.assert_(m, 'Comments not set in')
+
+    def testTagsOrder(self):
+        mtop = self.sp.getProperty('metatags_order')
+        metatags_order = [t.split(' ')[0] for t in mtop if len(t.split(' '))==2 and t.split(' ')[0] in VIEW_METATAGS]
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
+        self.assert_(m, "Meta tags order not supported.")
+
+        mtop = list(mtop)
+        mtop.reverse()
+        metatags_order = [t.split(' ')[0] for t in mtop if len(t.split(' '))==2 and t.split(' ')[0] in VIEW_METATAGS]
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
+        self.assertFalse(m, "Meta tags order not supported.")
+
+        self.sp.manage_changeProperties(**{'metatags_order':tuple(mtop)})
+        html = self.publish(self.abs_path, self.basic_auth).getBody()
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), self.html, re.S|re.M)
+        self.assertFalse(m, "Meta tags order not supported.")
+
+        m = re.search('.*'.join(['<meta.*name="%s".*/>' %t for t in metatags_order]), html, re.S|re.M)
+        self.assert_(m, "Meta tags order not supported.")
+
+
+    def testCustomMetaTags(self):
+        for tag in CUSTOM_METATAGS:
+            m = re.search('<meta name="%(meta_name)s" content="%(meta_content)s" />' % tag, self.html, re.S|re.M)
+            if not m:
+                m = re.search('<meta content="%(meta_content)s" name="%(meta_name)s" />' % tag, self.html, re.S|re.M)
+            if tag['meta_content']:
+                self.assert_(m, "Custom meta tag %s not applied." % tag['meta_name'])
+            else:
+                self.assert_(not m, "Meta tag %s has no content, but is present in the page." % tag['meta_name'])
+        m = re.search('<meta name="metatag4" content="global_metatag4value" />' , self.html, re.S|re.M)
+        if not m:
+            m = re.search('<meta content="global_metatag4value" name="metatag4" />' , self.html, re.S|re.M)
+        self.assert_(m, "Global custom meta tag %s not applied." % 'metatag4')
+
+    def testDeleteCustomMetaTags(self):
+        self.sp.manage_changeProperties(**{'default_custom_metatags':'metatag1|global_metatag1value'})
+        my_doc = self.my_doc
+        self.form_data = {'seo_custommetatags': CUSTOM_METATAGS,  'seo_custommetatags_override:int': 0, 'form.submitted:int': 1}
+        self.publish(path=self.abs_path+'/@@seo-context-properties', basic=self.basic_auth, request_method='POST', stdin=StringIO(urllib.urlencode(self.form_data)))
+        html = self.publish(self.abs_path, self.basic_auth).getBody()
+        m = re.search('<meta name="metatag4" content="global_metatag4value" />' , html, re.S|re.M)
+        if not m:
+            m = re.search('<meta content="global_metatag4value" name="metatag4" />' , html, re.S|re.M)
+        self.assert_(not m, "Global custom meta tag %s is prosent in the page." % 'metatag4')
+        m = re.search('<meta name="metatag1" content="global_metatag1value" />' , html, re.S|re.M)
+        if not m:
+            m = re.search('<meta content="global_metatag1value" name="metatag1" />' , html, re.S|re.M)
+        self.assert_(m, "Global custom meta tag %s not applied." % 'metatag1')
+
+    def testCanonical(self):
+        m = re.match('.*<link rel="canonical" href="%s" />' % self.canonurl, self.html, re.S|re.M)
+        self.assert_(m, self.canonurl)
+
+    def testDefaultCanonical(self):
+        """Default canonical url mast add document absolute_url
+        """
+        # Delete custom canonical url
+        my_doc = self.portal['my_doc']
+        my_doc._delProperty(id='qSEO_canonical')
+        # Get document without customized canonical url
+        abs_path = "/%s" % my_doc.absolute_url(1)
+        self.html = self.publish(abs_path, self.basic_auth).getBody()
+
+        my_url = my_doc.absolute_url()
+        m = re.match('.*<link rel="canonical" href="%s" />' % my_url, self.html, re.S|re.M)
+        self.assert_(m, my_url)
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestResponse))
+    return suite
