source: products/quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testQSEOptimizer.py @ 1052

Last change on this file since 1052 was 1052, checked in by piv, 15 years ago

fix page title in case it equals portal title, add appropriate test, update documentation, ready for tagging

  • Property svn:eol-style set to native
File size: 21.0 KB
Line 
1import re
2import string
3import urllib
4from zope.component import getMultiAdapter
5from Products.Five import zcml, fiveconfigure
6from Testing.ZopeTestCase import installPackage, hasPackage
7from Products.PloneTestCase import PloneTestCase
8from Products.CMFCore.utils import getToolByName
9from Products.CMFCore.permissions import ManagePortal
10from Products.CMFQuickInstallerTool.InstalledProduct import InstalledProduct
11from AccessControl.SecurityManagement import newSecurityManager, noSecurityManager
12
13import quintagroup.seoptimizer
14from quintagroup.seoptimizer.config import *
15
16zcml.load_site()
17zcml.load_config('overrides.zcml', quintagroup.seoptimizer)
18zcml.load_config('configure.zcml', quintagroup.seoptimizer)
19PRODUCT = 'quintagroup.seoptimizer'
20installPackage(PRODUCT)
21props = {'stop_words':STOP_WORDS, 'fields':FIELDS, 'additional_keywords': []}
22
23custom_metatags = [{'meta_name'    : 'metatag1',
24                    'meta_content' : 'metatag1value'},
25                   {'meta_name'    : 'metatag2',
26                    'meta_content' : 'metatag2value'}]
27
28configlets = ({'id':'qSEOptimizer',
29    'name':'Search Engine Optimizer',
30    'action':'string:${portal_url}/seo-controlpanel',
31    'condition':'',
32    'category':'Products',
33    'visible':1,
34    'appId':'qSEOptimizer',
35    'permission':ManagePortal},)
36
37qSEO_CONTENT = ['File','Document','News Item']
38qSEO_FOLDER  = []
39qSEO_TYPES   = qSEO_CONTENT + qSEO_FOLDER
40
41
42PloneTestCase.setupPloneSite()
43
44class TestBeforeInstall(PloneTestCase.FunctionalTestCase):
45
46    def afterSetUp(self):
47        self.basic_auth = 'mgr:mgrpw'
48        self.portal_path = '/%s' % self.portal.absolute_url(1)
49
50    def testAccessPortalRootAnonymous(self):
51        response = self.publish(self.portal_path)
52        self.assertEqual(response.getStatus(), 200)
53
54    def testAccessPortalRootAuthenticated(self):
55        response = self.publish(self.portal_path, self.basic_auth)
56        self.assertEqual(response.getStatus(), 200)
57
58
59class TestInstallation(PloneTestCase.PloneTestCase):
60
61    def afterSetUp(self):
62        self.properties = getToolByName(self.portal, 'portal_properties')
63        self.qi = self.portal.portal_quickinstaller
64        self.qi.installProduct(PRODUCT)
65
66    def testAddingPropertySheet(self):
67        """ Test adding property sheet to portal_properties tool """
68        self.failUnless(hasattr(self.properties.aq_base, PROPERTY_SHEET))
69
70    def testAddingPropertyFields(self):
71        """ Test adding property field to portal_properties.maps_properties sheet """
72        map_sheet = self.properties[PROPERTY_SHEET]
73        for key, value in props.items():
74            self.failUnless(map_sheet.hasProperty(key) and list(map_sheet.getProperty(key)) == value)
75
76    def test_configlet_install(self):
77        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
78        self.assert_(PRODUCT in [a.getId() for a in configTool.listActions()], 'Configlet not found')
79
80    def test_actions_install(self):
81        portal_types = getToolByName(self.portal, 'portal_types')
82        for ptype in portal_types.objectValues():
83            try:
84                #for Plone-2.5 and higher
85                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
86                action = acts and acts[0] or None
87            except AttributeError:
88                action = ptype.getActionById('seo_properties', default=None )
89
90            if ptype.getId() in qSEO_TYPES:
91                self.assert_(action, 'Action for %s not found' % ptype.getId())
92            else:
93                self.assert_(not action, 'Action found for %s' % ptype.getId())
94
95    def test_skins_install(self):
96        skinstool=getToolByName(self.portal, 'portal_skins')
97
98        for skin in skinstool.getSkinSelections():
99            path = skinstool.getSkinPath(skin)
100            path = map( string.strip, string.split( path,',' ) )
101            self.assert_(PRODUCT in path, 'qSEOptimizer layer not found in %s' %skin)
102
103    def test_versionedskin_install(self):
104        skinstool=getToolByName(self.portal, 'portal_skins')
105        mtool = getToolByName(self.portal, 'portal_migration')
106        plone_version = mtool.getFileSystemVersion()
107        if plone_version < "3":
108            for skin in skinstool.getSkinSelections():
109                path = skinstool.getSkinPath(skin)
110                path = map( string.strip, string.split( path,',' ) )
111                self.assert_(PRODUCT+'/%s' % plone_version in path, 'qSEOptimizer versioned layer not found in %s' %skin)
112
113    def test_actions_uninstall(self):
114        self.qi.uninstallProducts([PRODUCT])
115        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
116        portal_types = getToolByName(self.portal, 'portal_types')
117        for ptype in portal_types.objectValues():
118            try:
119                #for Plone-2.5 and higher
120                acts = filter(lambda x: x.id == 'seo_properties', ptype.listActions())
121                action = acts and acts[0] or None
122            except AttributeError:
123                action = ptype.getActionById('seo_properties', default=None )
124
125            self.assert_(not action, 'Action for %s found after uninstallation' % ptype.getId())
126
127    def test_skins_uninstall(self):
128        self.qi.uninstallProducts([PRODUCT])
129        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
130        skinstool=getToolByName(self.portal, 'portal_skins')
131
132        for skin in skinstool.getSkinSelections():
133            path = skinstool.getSkinPath(skin)
134            path = map( string.strip, string.split( path,',' ) )
135            self.assert_(not PRODUCT in path, 'qSEOptimizer layer found in %s after uninstallation' %skin)
136
137    def test_versionedskin_uninstall(self):
138        self.qi.uninstallProducts([PRODUCT])
139        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
140        skinstool=getToolByName(self.portal, 'portal_skins')
141        mtool = getToolByName(self.portal, 'portal_migration')
142        plone_version = mtool.getFileSystemVersion()
143
144        for skin in skinstool.getSkinSelections():
145            path = skinstool.getSkinPath(skin)
146            path = map( string.strip, string.split( path,',' ) )
147            self.assert_(not PRODUCT+'/%s' % plone_version in path, 'qSEOptimizer versioned layer found in %s after uninstallation' %skin)
148
149    def test_configlet_uninstall(self):
150        self.qi.uninstallProducts([PRODUCT])
151        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'qSEOptimizer is already installed')
152
153        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
154        self.assert_(not PRODUCT in [a.getId() for a in configTool.listActions()], 'Configlet found after uninstallation')
155
156
157class TestResponse(PloneTestCase.FunctionalTestCase):
158
159    def afterSetUp(self):
160        self.qi = self.portal.portal_quickinstaller
161        self.qi.installProduct(PRODUCT)
162        #self.portal.changeSkin('Plone Default')
163
164        self.basic_auth = 'mgr:mgrpw'
165        self.loginAsPortalOwner()
166
167        '''Preparation for functional testing'''
168        my_doc = self.portal.invokeFactory('Document', id='my_doc')
169        my_doc = self.portal['my_doc']
170        self.canonurl = 'http://test.site.com/test.html'
171
172        my_doc.qseo_properties_edit(title='hello world', description='it is description',
173                                    keywords='my1|key2', html_comment='no comments',
174                                    robots='ALL', distribution='Global', 
175                                    canonical=self.canonurl, canonical_override=1,
176                                    title_override=1,
177                                    description_override=1, keywords_override=1,
178                                    html_comment_override=1, robots_override=1,
179                                    distribution_override=1, custommetatags=custom_metatags)
180
181        wf_tool = self.portal.portal_workflow
182        wf_tool.doActionFor(my_doc, 'publish')
183
184        abs_path = "/%s" % my_doc.absolute_url(1)
185        self.html = self.publish(abs_path, self.basic_auth).getBody()
186
187        # now setup page with title equal to plone site's title
188        my_doc2 = self.portal.invokeFactory('Document', id='my_doc2')
189        my_doc2 = self.portal['my_doc2']
190        my_doc2.update(title=self.portal.Title())
191        wf_tool.doActionFor(my_doc2, 'publish')
192        abs_path2 = "/%s" % my_doc2.absolute_url(1)
193        self.html2 = self.publish(abs_path2, self.basic_auth).getBody()
194
195    def testTitle(self):
196        m = re.match('.*<title>\\s*hello world\\s*</title>', self.html, re.S|re.M)
197        self.assert_(m, 'Title not set in')
198
199    def testTitleDuplication(self):
200        """If we are not overriding page title and current page title equals title of the plone site
201        then there should be no concatenation of both titles. Only one should be displayed.
202        """
203        m = re.match('.*<title>\\s*%s\\s*</title>' % self.portal.Title(), self.html2, re.S|re.M)
204        self.assert_(m, 'Title is not set correctly, perhaps it is duplicated with plone site title')
205
206    def testDescription(self):
207        m = re.match('.*<meta name="description" content="it is description" />', self.html, re.S|re.M)
208        self.assert_(m, 'Description not set in')
209
210    def testKeywords(self):
211        m = re.match('.*<meta name="keywords" content="my1|key2" />', self.html, re.S|re.M)
212        self.assert_(m, 'Keywords not set in')
213
214    def testRobots(self):
215        m = re.match('.*<meta name="robots" content="ALL" />', self.html, re.S|re.M)
216        self.assert_(m, 'Robots not set in')
217
218    def testDistribution(self):
219        m = re.match('.*<meta name="distribution" content="Global" />', self.html, re.S|re.M)
220        self.assert_(m, 'Distribution not set in')
221
222    def testHTMLComments(self):
223        m = re.match('.*<!--\\s*no comments\\s*-->', self.html, re.S|re.M)
224        self.assert_(m, 'Comments not set in')
225
226    def testTagsOrder(self):
227        m = re.search('name="description".+name="keywords"', self.html, re.S|re.M)
228        self.assert_(m, "Meta tags order not supported.")
229
230    def testCustomMetaTags(self):
231        for tag in custom_metatags:
232            m = re.search('<meta name="%(meta_name)s" content="%(meta_content)s" />' % tag, self.html, re.S|re.M)
233            self.assert_(m, "Custom meta tag %s not applied." % tag['meta_name'])
234
235    def testCanonical(self):
236        m = re.match('.*<link rel="canonical" href="%s" />' % self.canonurl, self.html, re.S|re.M)
237        self.assert_(m, self.canonurl)
238
239    def testDefaultCanonical(self):
240        """Default canonical url mast add document absolute_url
241        """
242        # Delete custom canonical url
243        my_doc = self.portal['my_doc']
244        my_doc._delProperty(id='qSEO_canonical')
245        # Get document without customized canonical url
246        abs_path = "/%s" % my_doc.absolute_url(1)
247        self.html = self.publish(abs_path, self.basic_auth).getBody()
248
249        my_url = my_doc.absolute_url()
250        m = re.match('.*<link rel="canonical" href="%s" />' % my_url, self.html, re.S|re.M)
251        self.assert_(m, my_url)
252
253class TestAdditionalKeywords(PloneTestCase.FunctionalTestCase):
254
255    def afterSetUp(self):
256        self.qi = self.portal.portal_quickinstaller
257        self.qi.installProduct(PRODUCT)
258        self.sp = self.portal.portal_properties.seo_properties
259        self.pu = self.portal.plone_utils
260        #self.portal.changeSkin('Plone Default')
261
262        self.basic_auth = 'portal_manager:secret'
263        uf = self.app.acl_users
264        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
265        user = uf.getUserById('portal_manager')
266        if not hasattr(user, 'aq_base'):
267            user = user.__of__(uf)
268        newSecurityManager(None, user)
269
270        '''Preparation for functional testing'''
271        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
272        self.my_doc = self.portal['my_doc']
273
274    def test_additional_keywords_in_configlet(self):
275        quoted_keywords = urllib.quote('foo\nbar')
276        path = self.portal.id+'/@@seo-controlpanel?additionalKeywords:lines=%s&form.submitted=1'%quoted_keywords
277        self.publish(path, self.basic_auth)
278        self.assertEqual(self.sp.additional_keywords, ('foo', 'bar'))
279        self.publish(self.portal.id+'/@@seo-controlpanel?form.submitted=1', self.basic_auth)
280        self.assertEqual(self.sp.additional_keywords, ())
281
282    def test_listMetaTags_empty(self):
283        metatags = self.pu.listMetaTags(self.my_doc)
284        self.assert_('keywords' not in metatags)
285
286    def test_listMetaTags_one(self):       
287        self.my_doc.manage_addProperty('qSEO_keywords', ('foo',), 'lines')
288        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
289        m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
290        self.assert_(m, "No 'foo' keyword find")
291
292    def test_listMetaTags_two(self):       
293        self.my_doc.manage_addProperty('qSEO_keywords', ('foo', 'bar'), 'lines')
294        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
295        m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
296        self.assert_(m, "No 'foo, bar' keyword find")
297
298    def test_additional_keywords_in_listMetaTags_empty(self):       
299        self.sp.additional_keywords = ('foo',)
300        metatags = self.pu.listMetaTags(self.my_doc)
301        self.assert_('keywords' not in metatags)
302
303    def test_additional_keywords_in_listMetaTags_one(self):
304        self.my_doc.setText('<p>foo</p>')
305        self.sp.additional_keywords = ('foo',)
306        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
307        m = re.match('.*<meta\ name="keywords"\ content="foo"\ />', self.html, re.S|re.M)
308        self.assert_(m, "No 'foo' keyword find")
309
310    def test_additional_keywords_in_listMetaTags_two(self):
311        self.my_doc.setText('<p>foo bar</p>')
312        self.sp.additional_keywords = ('foo', 'bar')
313        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
314        m = re.match('.*<meta\ name="keywords"\ content="foo, bar"\ />', self.html, re.S|re.M)
315        self.assert_(m, "No 'foo, bar' keyword find")
316
317    def test_additional_keywords_in_listMetaTags_merge(self):
318        self.my_doc.setText('<p>foo bar</p>')
319        self.sp.additional_keywords = ('foo', 'bar')
320        self.my_doc.manage_addProperty('qSEO_keywords', ('baz',), 'lines')
321        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
322        m = re.match('.*<meta\ name="keywords"\ content="baz,\ foo,\ bar"\ />', self.html, re.S|re.M)
323        self.assert_(m, "No 'foo, bar, baz' keyword find")
324
325
326class TestExposeDCMetaTags(PloneTestCase.FunctionalTestCase):
327
328    def afterSetUp(self):
329        self.qi = self.portal.portal_quickinstaller
330        self.sp = self.portal.portal_properties.site_properties
331        self.qi.installProduct(PRODUCT)
332        #self.portal.changeSkin('Plone Default')
333
334        self.basic_auth = 'portal_manager:secret'
335        uf = self.app.acl_users
336        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
337        user = uf.getUserById('portal_manager')
338        if not hasattr(user, 'aq_base'):
339            user = user.__of__(uf)
340        newSecurityManager(None, user)
341
342        '''Preparation for functional testing'''
343        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
344        self.my_doc = self.portal['my_doc']
345
346    def test_exposeDCMetaTags_in_configletOn(self):
347        path = self.portal.id+'/@@seo-controlpanel?exposeDCMetaTags=True&form.submitted=1'
348        self.publish(path, self.basic_auth)
349        self.assert_(self.sp.exposeDCMetaTags)
350
351    def test_exposeDCMetaTags_in_configletOff(self):
352        self.publish(self.portal.id+'/@@seo-controlpanel?form.submitted=1', self.basic_auth)
353        self.assert_(not self.sp.exposeDCMetaTags)
354
355    def test_exposeDCMetaTagsPropertyOff(self):
356        self.sp.manage_changeProperties(exposeDCMetaTags = False)
357
358        self.my_doc.qseo_properties_edit()
359        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
360
361        m = re.match('.*<meta content=".*?" name="DC.format" />', self.html, re.S|re.M) or re.match('.*<meta content=".*?" name="DC.distribution" />', self.html, re.S|re.M)
362        self.assert_(not m, 'DC meta tags avaliable when exposeDCMetaTags=False')
363
364    def test_exposeDCMetaTagsPropertyOn(self):
365        self.sp.manage_changeProperties(exposeDCMetaTags = True)
366        self.my_doc.qseo_properties_edit()
367        self.html = str(self.publish(self.portal.id+'/my_doc', self.basic_auth))
368        m = re.match('.*<meta\ name="DC.format"\ content=".*?"\ />', self.html, re.S|re.M) and re.match('.*<meta\ name="DC.type"\ content=".*?"\ />', self.html, re.S|re.M)
369        self.assert_(m, 'DC meta tags not avaliable when createManager=True')
370
371
372class TestMetaTagsDuplication(PloneTestCase.FunctionalTestCase):
373
374    def afterSetUp(self):
375        self.qi = self.portal.portal_quickinstaller
376        self.basic_auth = 'portal_manager:secret'
377        uf = self.app.acl_users
378        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
379        user = uf.getUserById('portal_manager')
380        if not hasattr(user, 'aq_base'):
381            user = user.__of__(uf)
382        newSecurityManager(None, user)
383
384        '''Preparation for functional testing'''
385        self.my_doc = self.portal.invokeFactory('Document', id='my_doc')
386        self.my_doc = self.portal['my_doc']
387        self.my_doc.update(description="Document description")
388
389    def test_GeneratorMeta(self):
390        # Get document without customized canonical url
391        abs_path = "/%s" % self.my_doc.absolute_url(1)
392        regen = re.compile('<meta\s+[^>]*name=\"generator\"[^>]*>', re.S|re.M)
393
394        # Before product installation
395        html = self.publish(abs_path, self.basic_auth).getBody()
396        lengen = len(regen.findall(html))
397        self.assert_(lengen==1, "There is %d generator meta tag(s) " \
398           "before seoptimizer installation" % lengen)
399
400#         # After PRODUCT installation
401#         self.qi.installProduct(PRODUCT)
402#         html = self.publish(abs_path, self.basic_auth).getBody()
403#         lengen = len(regen.findall(html))
404#         self.assert_(lengen==1, "There is %d generator meta tag(s) " \
405#            "after seoptimizer installation" % lengen)
406
407    def test_DescriptionMeta(self):
408        # Get document without customized canonical url
409        abs_path = "/%s" % self.my_doc.absolute_url(1)
410        regen = re.compile('<meta\s+[^>]*name=\"description\"[^>]*>', re.S|re.M)
411
412        # Before product installation
413        html = self.publish(abs_path, self.basic_auth).getBody()
414        lendesc = len(regen.findall(html))
415        self.assert_(lendesc==1, "There is %d DESCRIPTION meta tag(s) " \
416           "before seoptimizer installation" % lendesc)
417
418#         # After PRODUCT installation
419#         self.qi.installProduct(PRODUCT)
420#         html = self.publish(abs_path, self.basic_auth).getBody()
421#         lendesc = len(regen.findall(html))
422#         self.assert_(lendesc==1, "There is %d DESCRIPTION meta tag(s) " \
423#            "after seoptimizer installation" % lendesc)
424
425class TestBaseURL(PloneTestCase.FunctionalTestCase):
426
427    def afterSetUp(self):
428        self.qi = self.portal.portal_quickinstaller
429        self.qi.installProduct(PRODUCT)
430        #self.portal.changeSkin('Plone Default')
431
432        self.basic_auth = 'portal_manager:secret'
433        uf = self.app.acl_users
434        uf.userFolderAddUser('portal_manager', 'secret', ['Manager'], [])
435        user = uf.getUserById('portal_manager')
436        if not hasattr(user, 'aq_base'):
437            user = user.__of__(uf)
438        newSecurityManager(None, user)
439
440    def test_notFolderBaseURL(self):
441        my_doc = self.portal.invokeFactory('Document', id='my_doc')
442        my_doc = self.portal['my_doc']
443        regen = re.compile('<base\s+[^>]*href=\"([^\"]*)\"[^>]*>', re.S|re.M)
444       
445        path = "/%s" % my_doc.absolute_url(1)
446        html = self.publish(path, self.basic_auth).getBody()
447        burls = regen.findall(html)
448       
449        mydocurl = my_doc.absolute_url()
450        self.assert_(not [1 for burl in burls if not burl==mydocurl],
451           "Wrong BASE URL for document: %s, all must be: %s" % (burls, mydocurl))
452
453    def test_folderBaseURL(self):
454        my_fldr = self.portal.invokeFactory('Folder', id='my_fldr')
455        my_fldr = self.portal['my_fldr']
456        regen = re.compile('<base\s+[^>]*href=\"([^\"]*)\"[^>]*>', re.S|re.M)
457       
458        path = "/%s" % my_fldr.absolute_url(1)
459        html = self.publish(path, self.basic_auth).getBody()
460        burls = regen.findall(html)
461
462        myfldrurl = my_fldr.absolute_url() + '/'
463        self.assert_(not [1 for burl in burls if not burl==myfldrurl],
464           "Wrong BASE URL for folder: %s , all must be : %s" % (burls, myfldrurl))
465
466TESTS = [TestBeforeInstall,
467         TestInstallation,
468         TestResponse,
469         TestExposeDCMetaTags,
470         TestAdditionalKeywords,
471         TestMetaTagsDuplication,
472         TestBaseURL,
473        ]
474
475def test_suite():
476    from unittest import TestSuite, makeSuite
477    suite = TestSuite()
478    for suite_class in TESTS:
479        suite.addTest(makeSuite(suite_class))
480
481    return suite
482
483if __name__ == '__main__':
484    framework()
Note: See TracBrowser for help on using the repository browser.