# # Test product's installation # import string from zope.interface import alsoProvides from zope.component import queryMultiAdapter from zope.viewlet.interfaces import IViewletManager from quintagroup.canonicalpath.adapters import PROPERTY_LINK from quintagroup.seoptimizer.browser.interfaces import IPloneSEOLayer from quintagroup.seoptimizer.tests.base import TestCase, \ FunctionalTestCaseNotInstalled from quintagroup.seoptimizer.config import PROJECT_NAME, SUPPORT_BLAYER from Products.CMFCore.utils import getToolByName PROPERTY_SHEET = 'seo_properties' STOP_WORDS = ['a', 'an', 'amp', 'and', 'are', 'arial', 'as', 'at', 'be', 'but', 'by', 'can', 'com', 'do', 'font', 'for', 'from', 'gif', 'had', 'has', 'have', 'he', 'helvetica', 'her', 'his', 'how', 'href', 'i', 'if', 'in', 'is', 'it', 'javascript', 'jpg', 'made', 'net', 'of', 'on', 'or', 'org', 'our', 'sans', 'see', 'serif', 'she', 'that', 'the', 'this', 'to', 'us', 'we', 'with', 'you', 'your'] PROPS = {'stop_words': STOP_WORDS, 'fields': ['seo_title', 'seo_description', 'seo_keywords']} DEFAULT_METATAGS_ORDER = [ 'DC.contributors', 'DC.creator', 'DC.date.created', 'DC.date.modified', 'DC.description', 'DC.distribution', 'DC.format', 'DC.language', 'DC.publisher', 'DC.rights', 'DC.subject', 'DC.type', 'description', 'distribution', 'keywords', 'robots'] DEFAULT_METATAGS_ORDER.sort() SEO_CONTENT = ['File', 'Document', 'News Item'] CONTENTTYPES_WITH_SEOACTION = ['File', 'Document', 'News Item', 'Folder', 'Event'] CONTENTTYPES_WITH_SEOACTION.sort() class TestBeforeInstallation(FunctionalTestCaseNotInstalled): 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') 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_viewlets_install(self): VIEWLETS = ['plone.htmlhead.title', 'plone.resourceregistries', 'quintagroup.seoptimizer.seotags', 'quintagroup.seoptimizer.customscript'] request = self.app.REQUEST # mark request with our browser layer alsoProvides(request, IPloneSEOLayer) view = queryMultiAdapter((self.portal, request), name="plone") manager = queryMultiAdapter((self.portal['front-page'], request, view), IViewletManager, name='plone.htmlhead') for p in VIEWLETS: self.assert_(manager.get(p) is not None, "Not registered '%s' " \ "viewlet" % p) def test_browser_layer(self): if not SUPPORT_BLAYER: return from plone.browserlayer import utils self.assert_(IPloneSEOLayer in utils.registered_layers(), "Not registered 'IPloneSEOLayer' browser layer") def test_action_install(self): atool = getToolByName(self.portal, 'portal_actions') action_ids = [a.id for a in atool.listActions()] self.assert_("SEOProperties" in action_ids, "Not added 'SEOProperties' action") class TestUninstallation(TestCase): def afterSetUp(self): self.qi = self.portal.portal_quickinstaller self.qi.uninstallProducts([PROJECT_NAME]) def test_propertysheet_uninstall(self): properties = getToolByName(self.portal, 'portal_properties') self.assert_(hasattr(properties.aq_base, PROPERTY_SHEET), "'%s' property sheet not uninstalled" % PROPERTY_SHEET) def test_configlet_uninstall(self): self.assertNotEqual(self.qi.isProductInstalled(PROJECT_NAME), True, 'qSEOptimizer is already installed') configTool = getToolByName(self.portal, 'portal_controlpanel', None) self.assertEqual(PROJECT_NAME in [a.getId() for a in \ configTool.listActions()], False, 'Configlet found after uninstallation') def test_viewlets_uninstall(self): VIEWLETS = ['quintagroup.seoptimizer.seotags', 'quintagroup.seoptimizer.customscript'] request = self.app.REQUEST view = queryMultiAdapter((self.portal, request), name="plone") manager = queryMultiAdapter((self.portal['front-page'], request, view), IViewletManager, name='plone.htmlhead') for p in VIEWLETS: self.assertEqual(manager.get(p) is None, True, "'%s' viewlet present after uninstallation" % p) def test_browserlayer_uninstall(self): if not SUPPORT_BLAYER: return from plone.browserlayer import utils self.assertEqual(IPloneSEOLayer in utils.registered_layers(), False, "Still registered 'IPloneSEOLayer' browser layer") def test_action_uninstall(self): atool = getToolByName(self.portal, 'portal_actions') action_ids = [a.id for a in atool.listActions()] self.assertEqual("SEOProperties" in action_ids, False, "'SEOProperties' action not removed from " \ "portal_actions on uninstallation") class TestReinstallation(TestCase): def afterSetUp(self): self.qi = self.portal.portal_quickinstaller self.types_tool = getToolByName(self.portal, 'portal_types') self.setup_tool = getToolByName(self.portal, 'portal_setup') self.pprops_tool = getToolByName(self.portal, 'portal_properties') self.seoprops_tool = getToolByName(self.pprops_tool, 'seo_properties', None) # Set earlier version profile (2.0.0) for using upgrade steps self.setup_tool.setLastVersionForProfile('%s:default' % PROJECT_NAME, '2.0.0') def testChangeDomain(self): # Test changed of content type's domain from 'quintagroup.seoptimizer' # to 'plone' for type in SEO_CONTENT: i18n_domain = 'quintagroup.seoptimizer' self.types_tool.getTypeInfo(type).i18n_domain = i18n_domain self.qi.reinstallProducts([PROJECT_NAME]) for type in SEO_CONTENT: self.assertEqual(self.types_tool.getTypeInfo(type).i18n_domain, 'plone', "Not changed of %s content type's " \ "domain to 'plone'" % type) def testCutItemsMetatagsOrderList(self): # Test changed format metatags order list from "metaname accessor" # to "metaname" value, expect_mto = ['name1 accessor1', 'name2 accessor2'], \ ['name1', 'name2'] self.seoprops_tool.manage_changeProperties(metatags_order=value) self.qi.reinstallProducts([PROJECT_NAME]) mto = list(self.seoprops_tool.getProperty('metatags_order')) mto.sort() self.assertEqual(mto, expect_mto, "Not changed format metatags order list from \"" \ "metaname accessor\" to \"metaname\". %s != %s" \ % (mto, expect_mto)) def testAddMetatagsOrderList(self): # Test added metatags order list if it was not there before self.seoprops_tool.manage_delProperties(['metatags_order']) self.qi.reinstallProducts([PROJECT_NAME]) mto = list(self.seoprops_tool.getProperty('metatags_order')) mto.sort() self.assertEqual(mto, DEFAULT_METATAGS_ORDER, "Not added metatags order list with default values." \ "%s != %s" % (mto, DEFAULT_METATAGS_ORDER)) def testMigrationActions(self): # Test migrated actions from portal_types action to seoproperties tool self.seoprops_tool.content_types_with_seoproperties = () # Add seoaction to content type for testing for type in CONTENTTYPES_WITH_SEOACTION: self.types_tool.getTypeInfo(type).addAction( id='seo_properties', name='SEO Properties', action=None, condition=None, permission=(u'Modify portal content',), category='object', ) # Check presence seoaction in content type seoaction = [act.id for act in \ self.types_tool.getTypeInfo(type).listActions() \ if act.id == 'seo_properties'] self.assertEqual(bool(seoaction), True, "Not added seoaction to content type %s for " \ "testing" % type) self.qi.reinstallProducts([PROJECT_NAME]) # Check presence seoaction in content type for type in CONTENTTYPES_WITH_SEOACTION: seoaction = [act.id for act in \ self.types_tool.getTypeInfo(type).listActions() \ if act.id == 'seo_properties'] self.assertEqual(bool(seoaction), False, "Not removed seoaction in content type %s" % type) # Check added content type names in seo properties tool # if content types have seoaction ctws = list(self.seoprops_tool.content_types_with_seoproperties) ctws.sort() self.assertEqual(ctws, CONTENTTYPES_WITH_SEOACTION, "Not added content type names in seo properties " \ "tool if content types have seoaction. %s != %s" \ % (ctws, CONTENTTYPES_WITH_SEOACTION)) def testRemoveSkin(self): # Test remove layers layer = 'quintagroup.seoptimizer' skinstool = getToolByName(self.portal, 'portal_skins') for skin in skinstool.getSkinSelections(): paths = ','.join((skinstool.getSkinPath(skin), layer)) skinstool.addSkinSelection(skin, paths) self.qi.reinstallProducts([PROJECT_NAME]) for skin in skinstool.getSkinSelections(): path = skinstool.getSkinPath(skin) path = map(string.strip, string.split(path, ',')) self.assertEqual(layer in path, False, '%s layer found in %s after uninstallation' \ % (layer, skin)) def testMigrateCanonical(self): """ Test Migrate qSEO_canonical property into PROPERTY_LINK for all portal objects, which use SEO """ doc = self.portal.get('front-page') doc.manage_addProperty('qSEO_canonical', 'val', 'string') value = doc.getProperty('qSEO_canonical') assert doc.getProperty('qSEO_canonical') == 'val' self.qi.reinstallProducts([PROJECT_NAME]) value = doc.getProperty(PROPERTY_LINK) has_prop = bool(doc.hasProperty('qSEO_canonical')) self.assertEqual(has_prop, False, "Property 'qSEO_canonical' is " \ "not deleted.") self.assertEqual(value == 'val', True, "Property not migrated from " \ "'qSEO_canonical' to '%s'." % PROPERTY_LINK) def test_suite(): from unittest import TestSuite, makeSuite suite = TestSuite() suite.addTest(makeSuite(TestBeforeInstallation)) suite.addTest(makeSuite(TestInstallation)) suite.addTest(makeSuite(TestUninstallation)) suite.addTest(makeSuite(TestReinstallation)) return suite