diff --git a/sc/social/like/browser/helper.py b/sc/social/like/browser/helper.py index 227bc6d1..40c23a2b 100644 --- a/sc/social/like/browser/helper.py +++ b/sc/social/like/browser/helper.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from Acquisition import aq_inner +from plone import api from plone.app.layout.globals.interfaces import IViewView from plone.memoize.view import memoize from plone.memoize.view import memoize_contextless @@ -8,7 +9,6 @@ from sc.social.like.interfaces import IHelperView from sc.social.like.interfaces import ISocialLikeSettings from sc.social.like.plugins import IPlugin -from zope.component import getMultiAdapter from zope.component import getUtilitiesFor from zope.component import getUtility from zope.interface import implementer @@ -16,18 +16,11 @@ @implementer(IHelperView) class HelperView(BrowserView): - """ Social Like configuration helpers - """ + """Social Like configuration helpers.""" - def __init__(self, context, request, *args, **kwargs): - super(HelperView, self).__init__(context, request, *args, **kwargs) - context = aq_inner(context) - self.context = context - self.portal_state = getMultiAdapter((self.context, self.request), - name=u'plone_portal_state') - self.portal = self.portal_state.portal() - self.context_state = getMultiAdapter((self.context, self.request), - name=u'plone_context_state') + def __init__(self, context, request): + self.context = aq_inner(context) + self.request = request @memoize_contextless def configs(self): @@ -76,4 +69,6 @@ def typebutton(self): @memoize def view_template_id(self): - return self.context_state.view_template_id() + context_state = api.content.get_view( + 'plone_context_state', self.context, self.request) + return context_state.view_template_id() diff --git a/sc/social/like/browser/socialikes.py b/sc/social/like/browser/socialikes.py index 9c86bb5e..bed89c40 100644 --- a/sc/social/like/browser/socialikes.py +++ b/sc/social/like/browser/socialikes.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from plone import api -from plone.api.exc import InvalidParameterError from Products.Five import BrowserView from sc.social.like.interfaces import ISocialLikes from sc.social.like.interfaces import ISocialLikeSettings @@ -20,9 +19,5 @@ def __init__(self, context, request): def enabled(self): """Validates if social bookmarks should be enabled in this context.""" record = ISocialLikeSettings.__identifier__ + '.enabled_portal_types' - try: - enabled_portal_types = api.portal.get_registry_record(record) - except InvalidParameterError: - enabled_portal_types = [] - + enabled_portal_types = api.portal.get_registry_record(record, default=[]) return self.context.portal_type in enabled_portal_types diff --git a/sc/social/like/browser/templates/metadata.pt b/sc/social/like/browser/templates/metadata.pt index 812a9449..93f5e0e6 100644 --- a/sc/social/like/browser/templates/metadata.pt +++ b/sc/social/like/browser/templates/metadata.pt @@ -1,5 +1,16 @@ - - - - - + + + + + + + + + + + + + + + + diff --git a/sc/social/like/browser/viewlets.py b/sc/social/like/browser/viewlets.py index f221bb2b..1ca616a0 100644 --- a/sc/social/like/browser/viewlets.py +++ b/sc/social/like/browser/viewlets.py @@ -1,12 +1,18 @@ # -*- coding: utf-8 -*- +from Acquisition import aq_inner from plone import api -from plone.api.exc import InvalidParameterError from plone.app.layout.viewlets import ViewletBase from plone.memoize.view import memoize +from plone.registry.interfaces import IRegistry from Products.CMFCore.WorkflowCore import WorkflowException from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile +from sc.social.like.behaviors import ISocialMedia from sc.social.like.interfaces import ISocialLikeSettings +from sc.social.like.plugins.facebook.utils import facebook_language +from sc.social.like.utils import get_content_image +from sc.social.like.utils import get_language from zope.component import getMultiAdapter +from zope.component import getUtility class BaseLikeViewlet(ViewletBase): @@ -58,14 +64,29 @@ def update(self): class SocialMetadataViewlet(BaseLikeViewlet): - """Viewlet used to insert metadata into page header - """ - render = ViewPageTemplateFile('templates/metadata.pt') + """Open Graph properties and plugin specific metadata.""" + + index = ViewPageTemplateFile('templates/metadata.pt') render_method = 'metadata' + def update(self): + registry = getUtility(IRegistry) + self.settings = registry.forInterface(ISocialLikeSettings) + self.helper = getMultiAdapter((self.context, self.request), name=u'sl_helper') + self.title = self.context.Title() + self.description = self.context.Description() + portal = api.portal.get() + self.site_name = portal.Title() + self.language = facebook_language(get_language(self.context), 'en_US') + self.image = get_content_image(self.context) + + def render(self): + if self.enabled(): + return self.index() + return '' + def enabled(self): - """Validates if the viewlet should be enabled for this context - """ + """Check if the viewlet should be shown in this context.""" template = self.helper.view_template_id() # If using folder_full_view or all_content, we add metadata # in order to proper display share buttons for @@ -74,10 +95,47 @@ def enabled(self): return True return self.helper.enabled(self.view) + def portal_url(self): + portal = api.portal.get() + return portal.absolute_url() + + def canonical_url(self): + if ISocialMedia.providedBy(self.context): + return self.context.canonical_url + return self.context.absolute_url() + + def image_url(self): + """Return lead image URL.""" + img = self.image + if img: + return img.url + else: + return self.portal_url + '/logo.png' + + def image_width(self): + return self.image.width + + def image_height(self): + return self.image.height + + def image_type(self): + """Return lead image MIME type.""" + try: + return self.image.content_type # Dexterity + except AttributeError: + return self.image.mimetype # Archetypes + + def type(self): + context = aq_inner(self.context) + context_state = api.content.get_view('plone_context_state', context, self.request) + if context_state.is_portal_root(): + return 'website' + return 'article' + class SocialLikesViewlet(BaseLikeViewlet): - """Viewlet used to display the buttons - """ + """Viewlet used to display the buttons.""" + render = ViewPageTemplateFile('templates/sociallikes.pt') @property @@ -89,10 +147,7 @@ def render_method(self): # site specific privacy level check record = ISocialLikeSettings.__identifier__ + '.do_not_track' - try: - do_not_track = api.portal.get_registry_record(record) - except InvalidParameterError: - do_not_track = False + do_not_track = api.portal.get_registry_record(record, default=False) if do_not_track: return 'link' diff --git a/sc/social/like/plugins/facebook/__init__.py b/sc/social/like/plugins/facebook/__init__.py index 6d236763..d7e3e4c5 100644 --- a/sc/social/like/plugins/facebook/__init__.py +++ b/sc/social/like/plugins/facebook/__init__.py @@ -1,5 +1,4 @@ # -*- coding:utf-8 -*-: - from sc.social.like.plugins import IPlugin from sc.social.like.plugins import Plugin from zope.interface import implementer diff --git a/sc/social/like/plugins/facebook/browser.py b/sc/social/like/plugins/facebook/browser.py index e5de8e98..7d04a2d5 100644 --- a/sc/social/like/plugins/facebook/browser.py +++ b/sc/social/like/plugins/facebook/browser.py @@ -1,67 +1,47 @@ # -*- coding:utf-8 -*- +"""Helper view to generate Facebook widget. + +More information: +* https://developers.facebook.com/docs/plugins +""" from Acquisition import aq_inner -from Acquisition import aq_parent from plone import api -from plone.api.exc import InvalidParameterError -from Products.CMFCore.interfaces import ISiteRoot -from Products.CMFCore.utils import getToolByName from Products.Five import BrowserView from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from sc.social.like.behaviors import ISocialMedia from sc.social.like.config import IS_PLONE_5 from sc.social.like.interfaces import ISocialLikeSettings from sc.social.like.plugins.facebook.utils import facebook_language -from sc.social.like.utils import get_content_image from sc.social.like.utils import get_language from urllib import urlencode -from zope.component import getMultiAdapter - - -BASE_URL = 'https://www.facebook.com/plugins/like.php?' -PARAMS = 'locale={0}&href={1}&send=false&layout={2}&show_faces=true&action={3}' class PluginView(BrowserView): - - fb_enabled = False - language = 'en_US' + """Helper view to generate Facebook widget.""" metadata = ViewPageTemplateFile('templates/metadata.pt') plugin = ViewPageTemplateFile('templates/plugin.pt') link = ViewPageTemplateFile('templates/link.pt') def __init__(self, context, request): - self.context = context + self.context = aq_inner(context) self.request = request - # FIXME: the following could rise unexpected exceptions - # move it to a new setup() method - # see: http://docs.plone.org/develop/plone/views/browserviews.html#creating-a-view - self.title = context.title - self.description = context.Description() - self.portal_state = getMultiAdapter((self.context, self.request), - name=u'plone_portal_state') - self.portal = self.portal_state.portal() - self.site_url = self.portal_state.portal_url() - self.portal_title = self.portal_state.portal_title() - self.url = context.absolute_url() - self.language = facebook_language(get_language(context), self.language) - self.image = get_content_image(context, width=1200, height=630) - self.typebutton # XXX: needed to initialize self.width - - @property + + def portal_url(self): + portal = api.portal.get() + return portal.absolute_url() + def canonical_url(self): if ISocialMedia.providedBy(self.context): return self.context.canonical_url - else: - # use current URL if the object don't provide the behavior - return self.url + return self.context.absolute_url() - @property def is_plone_5(self): return IS_PLONE_5 def fbjs(self): - js_source = """ + language = get_language(self.context) + return """ (function() {{ var po = document.createElement('script'); po.async = true; @@ -69,51 +49,13 @@ def fbjs(self): var head = document.getElementsByTagName('head')[0]; head.appendChild(po); }}()); - """.format(self.language) - return js_source - - def image_height(self): - """ Return height to image - """ - img = self.image - if img: - return img.height - - def image_type(self): - """ Return content type to image - """ - img = self.image - if img: - return getattr(img, 'content_type', - getattr(img, 'mimetype', 'image/jpeg')) - - def image_width(self): - """ Return width to image - """ - img = self.image - if img: - return img.width - - def image_url(self): - """ Return url to image - """ - img = self.image - if img: - return img.url - else: - return '{0}/logo.png'.format(self.site_url) + """.format(facebook_language(language, 'en_US')) - @property def typebutton(self): - typerecord = ISocialLikeSettings.__identifier__ + '.typebutton' - showlikesrecord = ISocialLikeSettings.__identifier__ + '.fbshowlikes' - - try: - typebutton = api.portal.get_registry_record(typerecord) - fbshowlikes = api.portal.get_registry_record(showlikesrecord) - except InvalidParameterError: - typebutton = '' - fbshowlikes = True + record = ISocialLikeSettings.__identifier__ + '.typebutton' + typebutton = api.portal.get_registry_record(record, default='') + record = ISocialLikeSettings.__identifier__ + '.fbshowlikes' + fbshowlikes = api.portal.get_registry_record(record, default=True) if typebutton == 'horizontal' and fbshowlikes: typebutton = 'button_count' self.width = '90px' @@ -127,70 +69,33 @@ def typebutton(self): return typebutton - @property def fbaction(self): record = ISocialLikeSettings.__identifier__ + '.fbaction' - try: - return api.portal.get_registry_record(record) - except InvalidParameterError: - return '' + return api.portal.get_registry_record(record, default='') - @property def app_id(self): record = ISocialLikeSettings.__identifier__ + '.facebook_app_id' - try: - return api.portal.get_registry_record(record) - except InvalidParameterError: - return '' + return api.portal.get_registry_record(record, default='') - @property def admins(self): record = ISocialLikeSettings.__identifier__ + '.facebook_username' - try: - return api.portal.get_registry_record(record) - except InvalidParameterError: - return '' + return api.portal.get_registry_record(record, default='') - @property def fbshow_like(self): record = ISocialLikeSettings.__identifier__ + '.fbbuttons' - try: - return 'Like' in api.portal.get_registry_record(record) - except InvalidParameterError: - return False + fbbuttons = api.portal.get_registry_record(record, default=[]) + return 'Like' in fbbuttons - @property def fbshow_share(self): record = ISocialLikeSettings.__identifier__ + '.fbbuttons' - try: - return 'Share' in api.portal.get_registry_record(record) - except InvalidParameterError: - return False - - def _isPortalDefaultView(self): - context = self.context - if ISiteRoot.providedBy(aq_parent(aq_inner(context))): - putils = getToolByName(context, 'plone_utils') - return putils.isDefaultPage(context) - return False - - def _isPortal(self): - context = self.context - if ISiteRoot.providedBy(aq_inner(context)): - return True - return self._isPortalDefaultView() - - def type(self): - if self._isPortal(): - return 'website' - return 'article' + fbbuttons = api.portal.get_registry_record(record, default=[]) + return 'Share' in fbbuttons def share_link(self): - params = dict( - app_id=self.app_id, - display='popup', - href=self.canonical_url, - redirect_uri=self.url, - ) - url = 'https://www.facebook.com/dialog/share?' + urlencode(params) - return url + params = { + 'app_id': self.app_id(), + 'display': 'popup', + 'href': self.canonical_url(), + 'redirect_uri': self.context.absolute_url(), + } + return 'https://www.facebook.com/dialog/share?' + urlencode(params) diff --git a/sc/social/like/plugins/facebook/templates/link.pt b/sc/social/like/plugins/facebook/templates/link.pt index e6888b78..4145471b 100644 --- a/sc/social/like/plugins/facebook/templates/link.pt +++ b/sc/social/like/plugins/facebook/templates/link.pt @@ -12,6 +12,6 @@ Share on Facebook + tal:attributes="src string:${view/portal_url}/++resource++sl_images/share-facebook.png" /> diff --git a/sc/social/like/plugins/facebook/templates/metadata.pt b/sc/social/like/plugins/facebook/templates/metadata.pt index 70621b70..a4bc2996 100644 --- a/sc/social/like/plugins/facebook/templates/metadata.pt +++ b/sc/social/like/plugins/facebook/templates/metadata.pt @@ -2,19 +2,7 @@ Plone 5 already includes metadata for Facebook and Twitter. - - - - - - - - - - - - - + diff --git a/sc/social/like/plugins/facebook/templates/plugin.pt b/sc/social/like/plugins/facebook/templates/plugin.pt index c40b2e99..395f1be0 100644 --- a/sc/social/like/plugins/facebook/templates/plugin.pt +++ b/sc/social/like/plugins/facebook/templates/plugin.pt @@ -1,16 +1,13 @@ - - -
-
-
+ +
+
diff --git a/sc/social/like/tests/test_plugin_facebook.py b/sc/social/like/tests/test_plugin_facebook.py index 7d62faa0..bf19ab54 100644 --- a/sc/social/like/tests/test_plugin_facebook.py +++ b/sc/social/like/tests/test_plugin_facebook.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from plone.app.testing import setRoles -from plone.app.testing import TEST_USER_ID +from plone import api from plone.registry.interfaces import IRegistry from sc.social.like import utils from sc.social.like.config import IS_PLONE_5 @@ -10,8 +9,6 @@ from sc.social.like.plugins.facebook.utils import fix_iso from sc.social.like.plugins.interfaces import IPlugin from sc.social.like.testing import INTEGRATION_TESTING -from sc.social.like.testing import load_image -from sc.social.like.tests.api_hacks import set_image_field from zope.component import getUtilitiesFor from zope.component import getUtility @@ -57,8 +54,10 @@ class PluginViewsTest(unittest.TestCase): def setUp(self): self.portal = self.layer['portal'] self.request = self.layer['request'] - setRoles(self.portal, TEST_USER_ID, ['Manager']) - self.setup_content(self.portal) + + with api.env.adopt_roles(['Manager']): + self.newsitem = api.content.create( + self.portal, type='News Item', title='foo') self.registry = getUtility(IRegistry) self.settings = self.registry.forInterface(ISocialLikeSettings) @@ -66,225 +65,107 @@ def setUp(self): self.plugins = dict(getUtilitiesFor(IPlugin)) self.plugin = self.plugins[name] - def setup_content(self, portal): - portal.invokeFactory('Document', 'my-document') - portal.invokeFactory('News Item', 'my-newsitem') - portal.invokeFactory('Image', 'my-image') - self.document = portal['my-document'] - self.newsitem = portal['my-newsitem'] - set_image_field(self.newsitem, load_image(1024, 768), 'image/png') - self.image = portal['my-image'] - set_image_field(self.image, load_image(1024, 768), 'image/png') - def test_plugin_view(self): - plugin = self.plugin - portal = self.portal - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) + plugin_view = self.plugin.view() + view = self.portal.restrictedTraverse(plugin_view) self.assertTrue(isinstance(view, browser.PluginView)) def test_plugin_view_html_likeonly(self): - plugin = self.plugin - portal = self.portal self.settings.fbbuttons = ('Like',) - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) + plugin_view = self.plugin.view() + view = self.portal.restrictedTraverse(plugin_view) html = view.plugin() self.assertIn('fb-like', html) self.assertNotIn('fb-share-button', html) def test_plugin_view_html_shareonly(self): - plugin = self.plugin - portal = self.portal self.settings.fbbuttons = ('Share',) - - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) + plugin_view = self.plugin.view() + view = self.portal.restrictedTraverse(plugin_view) html = view.plugin() self.assertNotIn('fb-like', html) self.assertIn('fb-share-button', html) def test_plugin_view_html_both(self): - plugin = self.plugin - portal = self.portal self.settings.fbbuttons = ('Like', 'Share') - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) + plugin_view = self.plugin.view() + view = self.portal.restrictedTraverse(plugin_view) html = view.plugin() self.assertIn('fb-like', html) self.assertIn('data-share', html) self.assertNotIn('fb-share-button', html) def test_privacy_plugin_view_html(self): - plugin = self.plugin - portal = self.portal self.settings.do_not_track = True - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) + plugin_view = self.plugin.view() + view = self.portal.restrictedTraverse(plugin_view) html = view.link() # Check that an app_id is required self.assertEqual('', html.strip()) self.settings.facebook_app_id = '12345' - view = portal.restrictedTraverse(plugin_view) + view = self.portal.restrictedTraverse(plugin_view) html = view.link() self.assertIn('Share on Facebook', html) # FIXME: we need to rethink this feature @unittest.skipIf(IS_PLONE_5, 'Metadata viewlet is disabled in Plone 5') def test_plugin_view_metadata(self): - plugin = self.plugin - portal = self.portal - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) - - metadata = view.metadata() - self.assertIn('og:site_name', metadata) - - # At root, use site logo - image_url = view.image_url() - self.assertIn('logo.png', image_url) - - # At root, use website type - og_type = view.type() - self.assertIn('website', og_type) - - def test_plugin_view_document(self): - plugin = self.plugin - document = self.document - portal = self.portal - - plugin_view = plugin.view() - view = document.restrictedTraverse(plugin_view) - - # At document, use site logo - image_url = view.image_url() - self.assertIn('logo.png', image_url) - - # At document, use article type - og_type = view.type() - self.assertIn('article', og_type) - - # At document, default page of portal, use website type - portal.setDefaultPage(document.id) - og_type = view.type() - self.assertIn('website', og_type) - - def test_plugin_view_image(self): - plugin = self.plugin - image = self.image - - plugin_view = plugin.view() - view = image.restrictedTraverse(plugin_view) - - # At image, use local image - image_url = view.image_url() - self.assertNotIn('logo.png', image_url) - self.assertEqual(view.image_width(), 1024) - self.assertEqual(view.image_height(), 768) - self.assertEqual(view.image_type(), 'image/png') - # XXX: avoid failures because of unchanged modification date - # this happens only on Dexterity-based content types - from time import sleep - sleep(1) + def get_meta_property(name): + meta = html.find('*/meta[@property="{0}"]'.format(name)) + if meta is not None: + return meta.attrib['content'] - # Set a larger image - set_image_field(image, load_image(1920, 1080), 'image/png') + view = self.newsitem.restrictedTraverse(self.plugin.view()) + self.settings.facebook_username = 'plone' + self.settings.facebook_app_id = '1234567890' - plugin_view = plugin.view() - view = image.restrictedTraverse(plugin_view) - self.assertEqual(view.image_width(), 1200) - self.assertEqual(view.image_height(), 675) - - def test_plugin_view_image_large(self): - plugin = self.plugin - image = self.image - set_image_field(image, load_image(1920, 1080), 'image/png') - - plugin_view = plugin.view() - view = image.restrictedTraverse(plugin_view) - - # At newsitem, use image - image_url = view.image_url() - self.assertNotIn('logo.png', image_url) - - self.assertEqual(view.image_width(), 1200) - self.assertEqual(view.image_height(), 675) - - def test_plugin_view_newsitem(self): - plugin = self.plugin - newsitem = self.newsitem - - plugin_view = plugin.view() - view = newsitem.restrictedTraverse(plugin_view) - - # At newsitem, use image - image_url = view.image_url() - self.assertNotIn('logo.png', image_url) - self.assertEqual(view.image_width(), 1024) - self.assertEqual(view.image_height(), 768) - - def test_plugin_view_newsitem_large(self): - plugin = self.plugin - newsitem = self.newsitem - set_image_field(newsitem, load_image(1920, 1080), 'image/png') - - plugin_view = plugin.view() - view = newsitem.restrictedTraverse(plugin_view) - - # At newsitem, use image - image_url = view.image_url() - self.assertNotIn('logo.png', image_url) - - self.assertEqual(view.image_width(), 1200) - self.assertEqual(view.image_height(), 675) + from lxml import etree + html = etree.HTML(view.metadata()) + self.assertEqual(get_meta_property('fb:admins'), 'plone') + self.assertEqual(get_meta_property('fb:app_id'), '1234567890') # FIXME: we need to rethink this feature @unittest.skipIf(IS_PLONE_5, 'Metadata viewlet is disabled in Plone 5') def test_plugin_language(self): - plugin = self.plugin - document = self.document - plugin_view = plugin.view() - self.document.setLanguage('pt-br') - view = document.restrictedTraverse(plugin_view) + plugin_view = self.plugin.view() + self.newsitem.setLanguage('pt-br') + view = self.newsitem.restrictedTraverse(plugin_view) html = view.metadata() self.assertIn('connect.facebook.net/pt_BR/all.js', html) - self.document.setLanguage('en') - view = document.restrictedTraverse(plugin_view) + self.newsitem.setLanguage('en') + view = self.newsitem.restrictedTraverse(plugin_view) html = view.metadata() self.assertIn('connect.facebook.net/en_GB/all.js', html) def test_plugin_view_typebutton(self): - portal = self.portal - plugin = self.plugin - - plugin_view = plugin.view() - view = portal.restrictedTraverse(plugin_view) - self.assertEqual(view.typebutton, 'button_count') + plugin_view = self.plugin.view() + view = self.portal.restrictedTraverse(plugin_view) + self.assertEqual(view.typebutton(), 'button_count') self.assertEqual(view.width, '90px') # Change to vertical self.settings.typebutton = 'vertical' - view = portal.restrictedTraverse(plugin_view) - self.assertEqual(view.typebutton, 'box_count') + view = self.portal.restrictedTraverse(plugin_view) + self.assertEqual(view.typebutton(), 'box_count') self.assertEqual(view.width, '55px') # disable show number of likes on vertical self.settings.typebutton = 'vertical' self.settings.fbshowlikes = False - view = portal.restrictedTraverse(plugin_view) - self.assertEqual(view.typebutton, 'button') + view = self.portal.restrictedTraverse(plugin_view) + self.assertEqual(view.typebutton(), 'button') self.assertEqual(view.width, '55px') # horizontal without numbers is also button self.settings.typebutton = 'horizontal' self.settings.fbshowlikes = False - view = portal.restrictedTraverse(plugin_view) - self.assertEqual(view.typebutton, 'button') + view = self.portal.restrictedTraverse(plugin_view) + self.assertEqual(view.typebutton(), 'button') self.assertEqual(view.width, '55px') diff --git a/sc/social/like/tests/test_viewlets.py b/sc/social/like/tests/test_viewlets.py index 93536cf8..f295f074 100644 --- a/sc/social/like/tests/test_viewlets.py +++ b/sc/social/like/tests/test_viewlets.py @@ -112,7 +112,16 @@ def test_metadata_viewlet_is_disabled_on_content_edit(self): def test_metadata_viewlet_rendering(self): viewlet = self.viewlet(self.obj) html = viewlet.render() - self.assertGreater(len(html), 0) + self.assertIn('og:title', html) + self.assertIn('og:description', html) + self.assertIn('og:type', html) + self.assertIn('og:url', html) + self.assertIn('og:image', html) + self.assertIn('og:image:width', html) + self.assertIn('og:image:height', html) + self.assertIn('og:image:type', html) + self.assertIn('og:locale', html) + self.assertIn('og:site_name', html) @unittest.skipIf(skip_profiling, 'Skipping performance measure and code profiling') def test_metadata_viewlet_rendering_performance(self):