source: products/quintagroup.seoptimizer/trunk/quintagroup/seoptimizer/tests/testBugs.py @ 3332

Last change on this file since 3332 was 3332, checked in by vmaksymiv, 12 years ago

disable test_bug_22_at_plone_org for 4.1.1 version

File size: 14.3 KB
Line 
1import urllib
2from cStringIO import StringIO
3import pkg_resources
4
5from OFS.interfaces import ITraversable
6
7from zope.component import getGlobalSiteManager
8from zope.component import queryAdapter, getMultiAdapter
9from zope.interface import directlyProvides
10from zope.viewlet.interfaces import IViewlet, IViewletManager
11
12from quintagroup.seoptimizer.browser.interfaces import IPloneSEOLayer
13from quintagroup.seoptimizer.browser.seo_configlet import ISEOConfigletSchema
14from quintagroup.canonicalpath.interfaces import ICanonicalLink
15from quintagroup.canonicalpath.adapters import DefaultCanonicalLinkAdapter
16
17from quintagroup.seoptimizer.tests.base import FunctionalTestCase
18from Products.PloneTestCase.PloneTestCase import portal_owner, \
19    default_password
20import re
21from Products.Five import zcml
22from Products.CMFCore.utils import getToolByName
23
24
25class TestBugs(FunctionalTestCase):
26
27    def afterSetUp(self):
28        self.basic_auth = ':'.join((portal_owner, default_password))
29        self.loginAsPortalOwner()
30        # prepare test document
31        self.portal.invokeFactory('Document', id='my_doc')
32        self.my_doc = self.portal['my_doc']
33        self.mydoc_path = "/%s" % self.my_doc.absolute_url(1)
34
35    def set_title(self, title='', title_override=0, comment='',
36                  comment_override=0):
37        """ Set seo title """
38        portal = self.portal
39        fp = portal['front-page']
40        request = portal.REQUEST
41        view = portal.restrictedTraverse('@@plone')
42        manager = getMultiAdapter((fp, request, view), IViewletManager,
43                        name=u'plone.htmlhead')
44
45        directlyProvides(request, IPloneSEOLayer)
46        viewlet = getMultiAdapter((fp, request, view, manager), IViewlet,
47                        name=u'plone.htmlhead.title')
48
49        form_data = {'seo_title': title,
50                     'seo_title_override:int': title_override,
51                     'seo_html_comment': comment,
52                     'seo_html_comment_override:int': comment_override,
53                     'form.button.Save': "Save",
54                     'form.submitted:int': 1}
55
56        self.publish(path=fp.absolute_url(1) + '/@@seo-context-properties',
57                     basic=self.basic_auth, request_method='POST',
58                     stdin=StringIO(urllib.urlencode(form_data)))
59        viewlet.update()
60        seo_title_comment = viewlet.render()
61        return seo_title_comment
62
63    def test_seo_title(self):
64        """ Test changing title """
65        title = "New Title"
66        new_title = u'<title>%s</title>' % title
67        seo_title = self.set_title(title=title, title_override=1)
68        self.assertEqual(new_title, seo_title)
69
70    def test_seo_comment(self):
71        """ Test changing comment """
72        comment = "New Comment"
73        seo_title_comment = self.set_title(comment=comment, comment_override=1)
74        self.assert_(seo_title_comment.endswith("<!--%s-->" % comment))
75
76    def test_seo_title_comment(self):
77        """ Test changing title and comment """
78        title = "New Title"
79        comment = "New Comment"
80        new_title = u'<title>%s</title>\n<!--%s-->' % (title, comment)
81        seo_title_comment = self.set_title(title=title, title_override=1,
82                                           comment=comment, comment_override=1)
83        self.assertEqual(new_title, seo_title_comment)
84
85    def test_modification_date(self):
86        """ Modification date changing on SEO properties edit """
87        form_data = {'seo_title': 'New Title',
88                     'seo_title_override:int': 1,
89                     'form.button.Save': "Save",
90                     'form.submitted:int': 1}
91
92        md_before = self.my_doc.modification_date
93        self.publish(path=self.mydoc_path + '/@@seo-context-properties',
94                     basic=self.basic_auth, request_method='POST',
95                     stdin=StringIO(urllib.urlencode(form_data)))
96        md_after = self.my_doc.modification_date
97
98        self.assertNotEqual(md_before, md_after)
99
100    def test_bug_20_at_plone_org(self):
101        portal = self.portal
102        fp = portal['front-page']
103        request = portal.REQUEST
104        view = portal.restrictedTraverse('@@plone')
105
106        manager = getMultiAdapter((fp, request, view), IViewletManager,
107                        name=u'plone.htmlhead')
108        viewlet = getMultiAdapter((fp, request, view, manager), IViewlet,
109                        name=u'plone.htmlhead.title')
110        viewlet.update()
111        old_title = viewlet.render()
112
113        # add IPloneSEOLayer
114        directlyProvides(request, IPloneSEOLayer)
115
116        viewlet = getMultiAdapter((fp, request, view, manager), IViewlet,
117                        name=u'plone.htmlhead.title')
118        viewlet.update()
119        new_title = viewlet.render()
120
121        self.assertEqual(old_title, new_title)
122
123    def test_bug_22_at_plone_org(self):
124        """If ICanonicalLink adapter is not found for the context object
125           - page rendering should not break, but only canonical link
126           should disappear.
127        """
128        # XXX: in 4.0.6 version we has quick fix bug #33
129        # http://plone.org/products/plone-seo/issues/33
130        # so this test hasn't any sense for versions with this fix
131        try:
132            # try to get version from egg-info. Need for plone<3.3
133            seo_version = pkg_resources.get_distribution(
134                            'quintagroup.seoptimizer').version
135        except pkg_resources.DistributionNotFound:
136            qi = getToolByName(self.getPortal(), "portal_quickinstaller")
137            seo_version = qi.getProductVersion('quintagroup.seoptimizer')
138
139        exclude_versions = ("4.0.6", "4.1.0", "4.1.1")
140        if seo_version is not None:
141            for v in exclude_versions:
142                if seo_version.startswith(v):
143                    return
144
145        curl = re.compile('<link\srel\s*=\s*"canonical"\s+' \
146                          '[^>]*href\s*=\s*\"([^\"]*)\"[^>]*>', re.S | re.M)
147        # When adapter registered for the object - canoncal link
148        # present on the page
149        self.assertNotEqual(queryAdapter(self.my_doc, ICanonicalLink), None)
150
151        res = self.publish(path=self.mydoc_path, basic=self.basic_auth)
152        self.assertNotEqual(curl.search(res.getBody()), None)
153
154        # Now remove adapter from the registry -> this should :
155        #     - not break page on rendering;
156        #     - canonical link will be absent on the page
157        gsm = getGlobalSiteManager()
158        gsm.unregisterAdapter(DefaultCanonicalLinkAdapter, [ITraversable, ],
159                              ICanonicalLink)
160        self.assertEqual(queryAdapter(self.my_doc, ICanonicalLink), None)
161
162        res = self.publish(path=self.mydoc_path, basic=self.basic_auth)
163        self.assertEqual(curl.search(res.getBody()), None)
164
165        # register adapter back in the global site manager
166        gsm.registerAdapter(DefaultCanonicalLinkAdapter, [ITraversable, ],
167                            ICanonicalLink)
168
169    def test_bug_19_23_at_plone_org(self):
170        """overrides.zcml should present in the root of the package"""
171        import quintagroup.seoptimizer
172        try:
173            zcml.load_config('overrides.zcml', quintagroup.seoptimizer)
174        except IOError:
175            self.fail("overrides.zcml removed from the package root")
176
177    def test_escape_characters_title(self):
178        """Change escape characters in title of SEO properties
179           Bug url http://plone.org/products/plone-seo/issues/31
180        """
181        from cgi import escape
182        title = 'New <i>Title</i>'
183        form_data = {'seo_title': title,
184                     'seo_title_override:int': 1,
185                     'form.button.Save': "Save",
186                     'form.submitted:int': 1}
187
188        self.publish(path=self.mydoc_path + '/@@seo-context-properties',
189                     basic=self.basic_auth, request_method='POST',
190                     stdin=StringIO(urllib.urlencode(form_data)))
191        html = self.publish(self.mydoc_path, self.basic_auth).getBody()
192        m = re.match('.*<title>\\s*%s\\s*</title>' % escape(title), html,
193                     re.S | re.M)
194        self.assert_(m, 'Title is not escaped properly.')
195
196    def test_escape_characters_desctiption(self):
197        """Change escape characters in desctiption of SEO properties
198        """
199        from cgi import escape
200        description = 'New <i>Description</i>'
201        form_data = {'seo_description': description,
202                     'seo_description_override:int': 1,
203                     'form.button.Save': "Save",
204                     'form.submitted:int': 1}
205
206        self.publish(path=self.mydoc_path + '/@@seo-context-properties',
207                     basic=self.basic_auth, request_method='POST',
208                     stdin=StringIO(urllib.urlencode(form_data)))
209        html = self.publish(self.mydoc_path, self.basic_auth).getBody()
210        m = re.match('.*<meta name="description" content="%s"' %
211                     escape(description), html, re.S | re.M)
212        self.assert_(m, 'Desctiption is not escaped properly.')
213
214    def test_escape_characters_comment(self):
215        """Change escape characters in comment of SEO properties
216        """
217        from cgi import escape
218        comment = 'New <i>comment</i>'
219        form_data = {'seo_title': 'New Title',
220                     'seo_title_override:int': 1,
221                     'seo_html_comment': comment,
222                     'seo_html_comment_override:int': 1,
223                     'form.button.Save': "Save",
224                     'form.submitted:int': 1}
225
226        self.publish(path=self.mydoc_path + '/@@seo-context-properties',
227                     basic=self.basic_auth, request_method='POST',
228                     stdin=StringIO(urllib.urlencode(form_data)))
229        html = self.publish(self.mydoc_path, self.basic_auth).getBody()
230        m = re.match('.*<!--\\s*%s\\s*-->' % escape(comment), html,
231                     re.S | re.M)
232        self.assert_(m, 'Comment is not escaped properly.')
233
234    def test_bug_custom_metatags_update(self):
235        # Prepare a page for the test
236        page = self.portal["front-page"]
237        request = self.portal.REQUEST
238        directlyProvides(request, IPloneSEOLayer)
239        seo_context_props = getMultiAdapter((page, request),
240                                            name="seo-context-properties")
241        # Set default custom meta tag without default value (tag name only)
242        self.gseo = queryAdapter(self.portal, ISEOConfigletSchema)
243        self.gseo.default_custom_metatags = ["test_tag", ]
244        try:
245            # Breakage on updating custom metatag
246            # with seo-context-properties view
247            seo_context_props.updateSEOCustomMetaTagsProperties([])
248        except IndexError:
249            self.fail("Error in calculating of default tag value, when only "\
250                      "tag name set in default_custom_metatags property of "\
251                      "the configlet.")
252
253
254class TestBug24AtPloneOrg(FunctionalTestCase):
255
256    def afterSetUp(self):
257        # Add test users: member, editor
258        member_id = 'test_member'
259        editor_id = 'test_editor'
260        test_pswd = 'pswd'
261        uf = self.portal.acl_users
262        uf.userFolderAddUser(member_id, test_pswd,
263                        ['Member'], [])
264        uf.userFolderAddUser(editor_id, test_pswd,
265                        ['Member', 'Editor'], [])
266
267        self.member_auth = '%s:%s' % (member_id, test_pswd)
268        self.editor_auth = '%s:%s' % (editor_id, test_pswd)
269        self.portal_url = '/'.join(self.portal.getPhysicalPath())
270
271    def test_not_break(self):
272        """Default portal page should not breaks for any user"""
273        # Anonymous
274        resp = self.publish(path=self.portal_url)
275        self.assertEqual(resp.getStatus(), 200)
276        # Member
277        resp = self.publish(path=self.portal_url, basic=self.member_auth)
278        self.assertEqual(resp.getStatus(), 200)
279        # Editor: this fails, althought must pass
280        resp = self.publish(path=self.portal_url, basic=self.editor_auth)
281        self.assertEqual(resp.getStatus(), 200)
282
283    def test_tab_visibility(self):
284        """Only Editor can view seo tab"""
285        rexp = re.compile('<a\s+[^>]*' \
286               'href="[a-zA-Z0-9\:\/_-]*/@@seo-context-properties"[^>]*>'\
287               '\s*SEO Properties\s*</a>', re.I | re.S)
288        # Anonymous: NO SEO Properties link
289        res = self.publish(path=self.portal_url).getBody()
290        self.assertEqual(rexp.search(res), None)
291        # Member: NO 'SEO Properties' link
292        res = self.publish(path=self.portal_url,
293                           basic=self.member_auth).getBody()
294        self.assertEqual(rexp.search(res), None)
295        # Editor: PRESENT 'SEO Properties' link
296        res = self.publish(path=self.portal_url,
297                           basic=self.editor_auth).getBody()
298        self.assertNotEqual(rexp.search(res), None)
299
300    def test_tab_access(self):
301        """Only Editor can access 'SEO Properties' tab"""
302        test_url = self.portal_url + '/front-page/@@seo-context-properties'
303        # Anonymous: can NOT ACCESS
304        headers = self.publish(path=test_url).headers
305        self.assert_('Unauthorized' in headers.get('bobo-exception-type', ""),
306                     "No 'Unauthorized' exception rised for Anonymous on " \
307                     "'@@seo-context-properties' view")
308        # Member: can NOT ACCESS
309        self.publish(path=test_url, basic=self.member_auth).headers
310        self.assert_('Unauthorized' in headers.get('bobo-exception-type', ""),
311                     "No 'Unauthorized' exception rised for Member on " \
312                     "'@@seo-context-properties' view")
313        # Editor: CAN Access
314        res = self.publish(path=test_url, basic=self.editor_auth)
315        self.assertEqual(res.status, 200)
316
317    def test_tab_edit(self):
318        """Editor can change SEO Properties"""
319        test_url = self.portal_url + '/front-page/@@seo-context-properties'
320        form_data = {'seo_title': 'New Title',
321                     'seo_title_override:int': 1,
322                     'form.submitted:int': 1}
323        res = self.publish(path=test_url, basic=self.editor_auth,
324                           request_method='POST',
325                           stdin=StringIO(urllib.urlencode(form_data)))
326        self.assertNotEqual(res.status, 200)
327
328
329def test_suite():
330    from unittest import TestSuite, makeSuite
331    suite = TestSuite()
332    suite.addTest(makeSuite(TestBugs))
333    suite.addTest(makeSuite(TestBug24AtPloneOrg))
334    return suite
Note: See TracBrowser for help on using the repository browser.