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

Last change on this file since 3143 was 3143, checked in by vmaksymiv, 13 years ago

Added seo title tests

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