import urllib
from cStringIO import StringIO
from zope.component import queryAdapter, getMultiAdapter
from zope.interface import directlyProvides
from zope.viewlet.interfaces import IViewlet, IViewletManager
from quintagroup.seoptimizer.browser.interfaces import IPloneSEOLayer
from quintagroup.seoptimizer.browser.seo_configlet import ISEOConfigletSchema
from quintagroup.seoptimizer.tests.base import FunctionalTestCase
from Products.PloneTestCase.PloneTestCase import portal_owner, \
default_password
import re
from Products.Five import zcml
class TestBugs(FunctionalTestCase):
def afterSetUp(self):
self.basic_auth = ':'.join((portal_owner, default_password))
self.loginAsPortalOwner()
# prepare test document
self.portal.invokeFactory('Document', id='my_doc')
self.my_doc = self.portal['my_doc']
self.mydoc_path = "/%s" % self.my_doc.absolute_url(1)
def set_title(self, title='', title_override=0, comment='',
comment_override=0):
""" Set seo title """
portal = self.portal
fp = portal['front-page']
request = portal.REQUEST
view = portal.restrictedTraverse('@@plone')
manager = getMultiAdapter((fp, request, view), IViewletManager,
name=u'plone.htmlhead')
directlyProvides(request, IPloneSEOLayer)
viewlet = getMultiAdapter((fp, request, view, manager), IViewlet,
name=u'plone.htmlhead.title')
form_data = {'seo_title': title,
'seo_title_override:int': title_override,
'seo_html_comment': comment,
'seo_html_comment_override:int': comment_override,
'form.button.Save': "Save",
'form.submitted:int': 1}
self.publish(path=fp.absolute_url(1) + '/@@seo-context-properties',
basic=self.basic_auth, request_method='POST',
stdin=StringIO(urllib.urlencode(form_data)))
viewlet.update()
seo_title_comment = viewlet.render()
return seo_title_comment
def test_seo_title(self):
""" Test changing title """
title = "New Title"
new_title = u'
%s' % title
seo_title = self.set_title(title=title, title_override=1)
self.assertEqual(new_title, seo_title)
def test_seo_comment(self):
""" Test changing comment """
comment = "New Comment"
seo_title_comment = self.set_title(comment=comment, comment_override=1)
self.assert_(seo_title_comment.endswith("" % comment))
def test_seo_title_comment(self):
""" Test changing title and comment """
title = "New Title"
comment = "New Comment"
new_title = u'%s\n' % (title, comment)
seo_title_comment = self.set_title(title=title, title_override=1,
comment=comment, comment_override=1)
self.assertEqual(new_title, seo_title_comment)
def test_modification_date(self):
""" Modification date changing on SEO properties edit """
form_data = {'seo_title': 'New Title',
'seo_title_override:int': 1,
'form.button.Save': "Save",
'form.submitted:int': 1}
md_before = self.my_doc.modification_date
self.publish(path=self.mydoc_path + '/@@seo-context-properties',
basic=self.basic_auth, request_method='POST',
stdin=StringIO(urllib.urlencode(form_data)))
md_after = self.my_doc.modification_date
self.assertNotEqual(md_before, md_after)
def test_bug_20_at_plone_org(self):
portal = self.portal
fp = portal['front-page']
request = portal.REQUEST
view = portal.restrictedTraverse('@@plone')
manager = getMultiAdapter((fp, request, view), IViewletManager,
name=u'plone.htmlhead')
viewlet = getMultiAdapter((fp, request, view, manager), IViewlet,
name=u'plone.htmlhead.title')
viewlet.update()
old_title = viewlet.render()
# add IPloneSEOLayer
directlyProvides(request, IPloneSEOLayer)
viewlet = getMultiAdapter((fp, request, view, manager), IViewlet,
name=u'plone.htmlhead.title')
viewlet.update()
new_title = viewlet.render()
self.assertEqual(old_title, new_title)
def test_bug_19_23_at_plone_org(self):
"""overrides.zcml should present in the root of the package"""
import quintagroup.seoptimizer
try:
zcml.load_config('overrides.zcml', quintagroup.seoptimizer)
except IOError:
self.fail("overrides.zcml removed from the package root")
def test_escape_characters_title(self):
"""Change escape characters in title of SEO properties
Bug url http://plone.org/products/plone-seo/issues/31
"""
from cgi import escape
title = 'New Title'
form_data = {'seo_title': title,
'seo_title_override:int': 1,
'form.button.Save': "Save",
'form.submitted:int': 1}
self.publish(path=self.mydoc_path + '/@@seo-context-properties',
basic=self.basic_auth, request_method='POST',
stdin=StringIO(urllib.urlencode(form_data)))
html = self.publish(self.mydoc_path, self.basic_auth).getBody()
m = re.match('.*\\s*%s\\s*' % escape(title), html,
re.S | re.M)
self.assert_(m, 'Title is not escaped properly.')
def test_escape_characters_desctiption(self):
"""Change escape characters in desctiption of SEO properties
"""
from cgi import escape
description = 'New Description'
form_data = {'seo_description': description,
'seo_description_override:int': 1,
'form.button.Save': "Save",
'form.submitted:int': 1}
self.publish(path=self.mydoc_path + '/@@seo-context-properties',
basic=self.basic_auth, request_method='POST',
stdin=StringIO(urllib.urlencode(form_data)))
html = self.publish(self.mydoc_path, self.basic_auth).getBody()
m = re.match('.*' % escape(comment), html,
re.S | re.M)
self.assert_(m, 'Comment is not escaped properly.')
def test_bug_custom_metatags_update(self):
# Prepare a page for the test
page = self.portal["front-page"]
request = self.portal.REQUEST
directlyProvides(request, IPloneSEOLayer)
seo_context_props = getMultiAdapter((page, request),
name="seo-context-properties")
# Set default custom meta tag without default value (tag name only)
self.gseo = queryAdapter(self.portal, ISEOConfigletSchema)
self.gseo.default_custom_metatags = ["test_tag", ]
try:
# Breakage on updating custom metatag
# with seo-context-properties view
seo_context_props.updateSEOCustomMetaTagsProperties([])
except IndexError:
self.fail("Error in calculating of default tag value, when only "\
"tag name set in default_custom_metatags property of "\
"the configlet.")
class TestBug24AtPloneOrg(FunctionalTestCase):
def afterSetUp(self):
# Add test users: member, editor
member_id = 'test_member'
editor_id = 'test_editor'
test_pswd = 'pswd'
uf = self.portal.acl_users
uf.userFolderAddUser(member_id, test_pswd,
['Member'], [])
uf.userFolderAddUser(editor_id, test_pswd,
['Member', 'Editor'], [])
self.member_auth = '%s:%s' % (member_id, test_pswd)
self.editor_auth = '%s:%s' % (editor_id, test_pswd)
self.portal_url = '/'.join(self.portal.getPhysicalPath())
def test_not_break(self):
"""Default portal page should not breaks for any user"""
# Anonymous
resp = self.publish(path=self.portal_url)
self.assertEqual(resp.getStatus(), 200)
# Member
resp = self.publish(path=self.portal_url, basic=self.member_auth)
self.assertEqual(resp.getStatus(), 200)
# Editor: this fails, althought must pass
resp = self.publish(path=self.portal_url, basic=self.editor_auth)
self.assertEqual(resp.getStatus(), 200)
def test_tab_visibility(self):
"""Only Editor can view seo tab"""
rexp = re.compile(']*' \
'href="[a-zA-Z0-9\:\/_-]*/@@seo-context-properties"[^>]*>'\
'\s*SEO Properties\s*', re.I | re.S)
# Anonymous: NO SEO Properties link
res = self.publish(path=self.portal_url).getBody()
self.assertEqual(rexp.search(res), None)
# Member: NO 'SEO Properties' link
res = self.publish(path=self.portal_url,
basic=self.member_auth).getBody()
self.assertEqual(rexp.search(res), None)
# Editor: PRESENT 'SEO Properties' link
res = self.publish(path=self.portal_url,
basic=self.editor_auth).getBody()
self.assertNotEqual(rexp.search(res), None)
def test_tab_access(self):
"""Only Editor can access 'SEO Properties' tab"""
test_url = self.portal_url + '/front-page/@@seo-context-properties'
# Anonymous: can NOT ACCESS
headers = self.publish(path=test_url).headers
self.assert_('Unauthorized' in headers.get('bobo-exception-type', ""),
"No 'Unauthorized' exception rised for Anonymous on " \
"'@@seo-context-properties' view")
# Member: can NOT ACCESS
self.publish(path=test_url, basic=self.member_auth).headers
self.assert_('Unauthorized' in headers.get('bobo-exception-type', ""),
"No 'Unauthorized' exception rised for Member on " \
"'@@seo-context-properties' view")
# Editor: CAN Access
res = self.publish(path=test_url, basic=self.editor_auth)
self.assertEqual(res.status, 200)
def test_tab_edit(self):
"""Editor can change SEO Properties"""
test_url = self.portal_url + '/front-page/@@seo-context-properties'
form_data = {'seo_title': 'New Title',
'seo_title_override:int': 1,
'form.submitted:int': 1}
res = self.publish(path=test_url, basic=self.editor_auth,
request_method='POST',
stdin=StringIO(urllib.urlencode(form_data)))
self.assertNotEqual(res.status, 200)
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(TestBugs))
suite.addTest(makeSuite(TestBug24AtPloneOrg))
return suite