diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..e446ad8 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,48 @@ +name: collective.handlebars CI +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.8, 3.7, 2.7] + plone-version: [5.2, 5.1] + exclude: + - python-version: 3.8 + plone-version: 5.1 + - python-version: 3.7 + plone-version: 5.1 + steps: + # git checkout + - uses: actions/checkout@v2 + + # python setup + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + # python cache + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + # python install + - name: pip install + run: pip install -r requirements.txt + + # buildout + - name: buildout + run: buildout -t 10 -c plone-${{ matrix.plone-version }}.x.cfg code-analysis:return-status-codes=True + + # code analysis + - name: code analysis + run: bin/code-analysis + + # test + - name: test + run: bin/coverage run bin/test diff --git a/.gitignore b/.gitignore index a7f46ca..18115ae 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ report.html .*.cfg .Python Pipfile* +.idea +pyvenv.cfg diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index abf4678..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: python -sudo: false -cache: - directories: - - eggs -python: - - 2.7.13 -env: - - PLONE_VERSION=5.0 - - PLONE_VERSION=5.1 -before_install: - - pip install -r requirements.txt -install: - - sed -ie "s#test-5.1#test-$PLONE_VERSION#" buildout.cfg - - buildout annotate - - buildout -script: - - bin/code-analysis - - bin/coverage run bin/test -after_success: - - bin/coveralls -notifications: - email: - - itconsense@gmail.com diff --git a/CHANGES.rst b/CHANGES.rst index c9e7006..e135c86 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,17 +2,29 @@ Changelog ========= -1.5 (unreleased) +1.6 (unreleased) ---------------- - Nothing changed yet. +1.5 (2021-08-28) +---------------- + +- Python 3 / Plone 5.2 compatibility + [adrianschulz, tomgross] + +- Update dependencies +- Move to Github CI +- Apply black formatting + [tomgross] + + 1.4.1 (2018-10-22) ------------------ - Add div-element to wrapper to support cases where only text is provided - (otherwise plone.protect can fail) + (otherwise plone.protect can fail) [tomgross] 1.4 (2018-09-10) diff --git a/MANIFEST.in b/MANIFEST.in index 07a60db..7b09c3e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ graft src/collective graft docs -include *.rst +include *.rst *.txt global-exclude *.pyc diff --git a/buildout.cfg b/buildout.cfg index 66b232f..cc47c72 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -1,5 +1,7 @@ [buildout] -extends = https://raw.github.com/collective/buildout.plonetest/master/test-5.1.x.cfg +extends = + https://raw.github.com/collective/buildout.plonetest/master/test-5.2.x.cfg + versions.cfg package-name = collective.handlebars package-extras = [test] parts += @@ -26,29 +28,4 @@ flake8-extensions = flake8-coding -[versions] -# Don't use a released version of collective.handlebars -collective.handlebars = -PyMeta3 = 0.5.1 -PyYAML = 3.13 -chardet = 3.0.4 -check-manifest = 0.37 -configparser = 3.5.0 -coverage = 4.0.3 -flake8 = 3.0.4 -flake8-blind-except = 0.1.1 -flake8-coding = 1.3.1 -flake8-debugger = 1.4.0 -idna = 2.6 -mccabe = 0.5.3 -plone.recipe.codeanalysis = 3.0.1 -plone.recipe.zope2instance = 4.4.0 -plone.testing = 5.0.0 -pybars3 = 0.9.6 -pycodestyle = 2.0.0 -pyflakes = 1.2.3 -python-coveralls = 2.7.0 -setuptools = 33.1.1 -sh = 1.12.14 -zc.buildout = 2.12.2 -zc.recipe.egg = 2.0.6 + diff --git a/plone-5.1.x.cfg b/plone-5.1.x.cfg new file mode 100644 index 0000000..af492e8 --- /dev/null +++ b/plone-5.1.x.cfg @@ -0,0 +1,32 @@ +[buildout] +extends = + https://raw.github.com/collective/buildout.plonetest/master/test-5.1.x.cfg + versions.cfg +package-name = collective.handlebars +package-extras = [test] +parts += + coverage + code-analysis + + +[coverage] +recipe = zc.recipe.egg +eggs = + ${test:eggs} + coverage + python-coveralls + + +[code-analysis] +recipe = plone.recipe.codeanalysis +directory = ${buildout:directory}/src/collective +flake8-exclude = bootstrap.py,bootstrap-buildout.py,docs,*.egg.,omelette +flake8-max-complexity = 15 +flake8-extensions = + flake8-blind-except + flake8-debugger + flake8-coding + +[versions] +plone.testing = 5.0.0 +six = 1.16.0 diff --git a/plone-5.2.x.cfg b/plone-5.2.x.cfg new file mode 100644 index 0000000..0628f9b --- /dev/null +++ b/plone-5.2.x.cfg @@ -0,0 +1,28 @@ +[buildout] +extends = + https://raw.github.com/collective/buildout.plonetest/master/test-5.2.x.cfg + versions.cfg +package-name = collective.handlebars +package-extras = [test] +parts += + coverage + code-analysis + + +[coverage] +recipe = zc.recipe.egg +eggs = + ${test:eggs} + coverage + python-coveralls + + +[code-analysis] +recipe = plone.recipe.codeanalysis +directory = ${buildout:directory}/src/collective +flake8-exclude = bootstrap.py,bootstrap-buildout.py,docs,*.egg.,omelette +flake8-max-complexity = 15 +flake8-extensions = + flake8-blind-except + flake8-debugger + flake8-coding diff --git a/requirements.txt b/requirements.txt index c16b9b0..760259f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -setuptools==33.1.1 -zc.buildout==2.12.2 +setuptools==42.0.2 +zc.buildout==2.13.4 +wheel diff --git a/setup.py b/setup.py index 455e5bf..4c8e696 100644 --- a/setup.py +++ b/setup.py @@ -14,19 +14,22 @@ setup( name='collective.handlebars', - version='1.5.dev0', + version='1.6.dev0', description="Handlebars for Plone", long_description=long_description, # Get more from https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ "Environment :: Web Environment", "Framework :: Plone", - "Framework :: Plone :: 5.0", "Framework :: Plone :: 5.1", + "Framework :: Plone :: 5.2", "Programming Language :: Python", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Operating System :: OS Independent", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", + "Development Status :: 5 - Production/Stable" ], keywords='Python Plone', author='Tom Gross', @@ -43,6 +46,7 @@ 'Products.GenericSetup>=1.8.2', 'setuptools', 'pybars3', + 'six' ], extras_require={ 'test': [ diff --git a/src/collective/__init__.py b/src/collective/__init__.py index 68c04af..03d08ff 100644 --- a/src/collective/__init__.py +++ b/src/collective/__init__.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -__import__('pkg_resources').declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) diff --git a/src/collective/handlebars/__init__.py b/src/collective/handlebars/__init__.py index 831813c..78d73ef 100644 --- a/src/collective/handlebars/__init__.py +++ b/src/collective/handlebars/__init__.py @@ -3,4 +3,4 @@ from zope.i18nmessageid import MessageFactory -_ = MessageFactory('collective.handlebars') +_ = MessageFactory("collective.handlebars") diff --git a/src/collective/handlebars/browser/views.py b/src/collective/handlebars/browser/views.py index ccb0622..9fd76f7 100644 --- a/src/collective/handlebars/browser/views.py +++ b/src/collective/handlebars/browser/views.py @@ -9,16 +9,21 @@ from zope.i18n import translate import os.path +import six import sys try: - get_distribution('plone.tiles') -except DistributionNotFound: # pragma: no cover + get_distribution("plone.tiles") +except DistributionNotFound: # pragma: no cover + class Tile(BrowserView): """Fake Tile which is only a BrowserView""" + class PersistentTile(BrowserView): """Fake persistent Tile which is only a BrowserView""" -else: # pragma: no cover + + +else: # pragma: no cover from plone.tiles.tile import Tile from plone.tiles.tile import PersistentTile @@ -34,9 +39,8 @@ def package_home(gdict): class HandlebarsMixin: - def get_contents(self): - """ Get CMS data and put it in a JSON format for hbs inclusion + """Get CMS data and put it in a JSON format for hbs inclusion :return: dictonary (must not be a JSON structure! The conversion is handled by the templating engine) @@ -63,13 +67,13 @@ def _get_hbs_template(self, hbs_filename): compiled_template = HBS_REGISTRY[hbs_filename] else: with open(hbs_filename) as f: - hbs_template = unicode(f.read(), 'utf-8') - compiled_template = compiler.compile(hbs_template) - HBS_REGISTRY[hbs_filename] = compiled_template + hbs_template = six.ensure_text(f.read()) + compiled_template = compiler.compile(hbs_template) + HBS_REGISTRY[hbs_filename] = compiled_template return compiled_template def get_partials(self, hbs_dir): - """ Get partials for rendering the master hbs_template + """Get partials for rendering the master hbs_template :param hbs_dir: directory of master template (/home/vagrant/templates/) @@ -79,7 +83,7 @@ def get_partials(self, hbs_dir): return {} def get_helpers(self): - """ Get helpers for rendering the master hbs_template + """Get helpers for rendering the master hbs_template :return: dictonary with compiled partials templates ({'list': self.list_items()}) @@ -92,25 +96,26 @@ def hbs_snippet(self, filename=None, _prefix=None): path = self.get_path_from_prefix(_prefix) hbs_file = os.path.join(path, filename) if not os.path.isfile(hbs_file): - raise ValueError('No such file', hbs_file) - elif hasattr(self, 'index'): # noqa + raise ValueError("No such file", hbs_file) + elif hasattr(self, "index"): # noqa # second scenario: get snippet from zcml # reuse filename from tile definition in zcml but read file here # otherwise it is interpreted as XML/PT hbs_file = self.index.filename else: - raise ValueError('No template found!') + raise ValueError("No template found!") hbs_template = self._get_hbs_template(hbs_file) hbs_dir = os.path.dirname(hbs_file) partials = self.get_partials(hbs_dir) helpers = self.get_helpers() - self.request.response.setHeader("Content-type", - "text/html; charset=utf-8") - return hbs_template(self.get_contents(), - helpers=helpers, - partials=partials) + self.request.response.setHeader( + "Content-type", "text/html; charset=utf-8" + ) + return hbs_template( + self.get_contents(), helpers=helpers, partials=partials + ) def get_path_from_prefix(self, _prefix): if isinstance(_prefix, str): @@ -121,42 +126,51 @@ def get_path_from_prefix(self, _prefix): path = package_home(_prefix) return path - def translate(self, msgid, domain=None, mapping=None, context=None, - target_language=None, default=None): + def translate( + self, + msgid, + domain=None, + mapping=None, + context=None, + target_language=None, + default=None, + ): if not target_language: target_language = api.portal.get_current_language(context) - return translate(msgid=msgid, - domain=domain, - mapping=mapping, - context=context, - default=default, - target_language=target_language) + return translate( + msgid=msgid, + domain=domain, + mapping=mapping, + context=context, + default=default, + target_language=target_language, + ) def _wrap_widget(self, render): - if render and render.startswith(u''): + if render and render.startswith(u""): return render - return ''.join([u'
', render, u'
']) + return "".join([u"
", render, u"
"]) class HandlebarsBrowserView(BrowserView, HandlebarsMixin): - """ A simple browserview using hbs as templating engine""" + """A simple browserview using hbs as templating engine""" def __call__(self, *args, **kwargs): return self.hbs_snippet() class HandlebarsPloneView(BrowserView, HandlebarsMixin): - """ A hbs view rendered in the content slot of the main template of Plone - """ + """A hbs view rendered in the content slot of the main template of Plone""" - main_template = ViewPageTemplateFile('templates/plone_standard_template.pt') # noqa + main_template = ViewPageTemplateFile( + "templates/plone_standard_template.pt" + ) def __call__(self, *args, **kwargs): return self.main_template(*args, **kwargs) class HandlebarsTile(Tile, HandlebarsMixin): - def __call__(self, *args, **kwargs): return self._wrap_widget(self.hbs_snippet()) @@ -166,8 +180,8 @@ def __call__(self, *args, **kwargs): class HandlebarsPersistentTile(PersistentTile, HandlebarsMixin): - def __call__(self, *args, **kwargs): return self._wrap_widget(self.hbs_snippet()) + # EOF diff --git a/src/collective/handlebars/setuphandlers.py b/src/collective/handlebars/setuphandlers.py index 625480b..1ff7ad6 100644 --- a/src/collective/handlebars/setuphandlers.py +++ b/src/collective/handlebars/setuphandlers.py @@ -5,12 +5,11 @@ @implementer(INonInstallable) class HiddenProfiles(object): - def getNonInstallableProfiles(self): """Hide uninstall profile from site-creation and quickinstaller""" return [ - 'collective.handlebars:default', - 'collective.handlebars:uninstall', + "collective.handlebars:default", + "collective.handlebars:uninstall", ] diff --git a/src/collective/handlebars/testing.py b/src/collective/handlebars/testing.py index 45f60e3..ea4f152 100644 --- a/src/collective/handlebars/testing.py +++ b/src/collective/handlebars/testing.py @@ -2,12 +2,10 @@ from collective.handlebars.browser.views import HandlebarsBrowserView from collective.handlebars.browser.views import HandlebarsPloneView from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE -from plone.app.robotframework.testing import REMOTE_LIBRARY_BUNDLE_FIXTURE from plone.app.testing import applyProfile from plone.app.testing import FunctionalTesting from plone.app.testing import IntegrationTesting from plone.app.testing import PloneSandboxLayer -from plone.testing import z2 import collective.handlebars @@ -21,23 +19,23 @@ def setUpZope(self, app, configurationContext): # The z3c.autoinclude feature is disabled in the Plone fixture base # layer. self.loadZCML(package=collective.handlebars) - self.loadZCML(package=collective.handlebars, name='testing.zcml') + self.loadZCML(package=collective.handlebars, name="testing.zcml") def setUpPloneSite(self, portal): - applyProfile(portal, 'collective.handlebars:default') + applyProfile(portal, "collective.handlebars:default") class HBSTestView(HandlebarsBrowserView): - def get_contents(self): - return {'title': u'Fäncy Title', 'body': u'This is the body'} + return {"title": u"Fäncy Title", "body": u"This is the body"} class HBSTestPloneView(HandlebarsPloneView): - def get_contents(self): - return {'title': u'Fäncy Title', - 'body': u'This is the Plone View body'} + return { + "title": u"Fäncy Title", + "body": u"This is the Plone View body", + } COLLECTIVE_HANDLEBARS_FIXTURE = CollectiveHandlebarsLayer() @@ -45,21 +43,11 @@ def get_contents(self): COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING = IntegrationTesting( bases=(COLLECTIVE_HANDLEBARS_FIXTURE,), - name='CollectiveHandlebarsLayer:IntegrationTesting' + name="CollectiveHandlebarsLayer:IntegrationTesting", ) COLLECTIVE_HANDLEBARS_FUNCTIONAL_TESTING = FunctionalTesting( bases=(COLLECTIVE_HANDLEBARS_FIXTURE,), - name='CollectiveHandlebarsLayer:FunctionalTesting' -) - - -COLLECTIVE_HANDLEBARS_ACCEPTANCE_TESTING = FunctionalTesting( - bases=( - COLLECTIVE_HANDLEBARS_FIXTURE, - REMOTE_LIBRARY_BUNDLE_FIXTURE, - z2.ZSERVER_FIXTURE - ), - name='CollectiveHandlebarsLayer:AcceptanceTesting' + name="CollectiveHandlebarsLayer:FunctionalTesting", ) diff --git a/src/collective/handlebars/tests/robot/test_example.robot b/src/collective/handlebars/tests/robot/test_example.robot deleted file mode 100644 index dde1662..0000000 --- a/src/collective/handlebars/tests/robot/test_example.robot +++ /dev/null @@ -1,66 +0,0 @@ -# ============================================================================ -# EXAMPLE ROBOT TESTS -# ============================================================================ -# -# Run this robot test stand-alone: -# -# $ bin/test -s collective.handlebars -t test_example.robot --all -# -# Run this robot test with robot server (which is faster): -# -# 1) Start robot server: -# -# $ bin/robot-server --reload-path src collective.handlebars.testing.COLLECTIVE_HANDLEBARS_ACCEPTANCE_TESTING -# -# 2) Run robot tests: -# -# $ bin/robot src/collective/handlebars/tests/robot/test_example.robot -# -# See the http://docs.plone.org for further details (search for robot -# framework). -# -# ============================================================================ - -*** Settings ***************************************************************** - -Resource plone/app/robotframework/selenium.robot -Resource plone/app/robotframework/keywords.robot - -Library Remote ${PLONE_URL}/RobotRemote - -Test Setup Open test browser -Test Teardown Close all browsers - - -*** Test Cases *************************************************************** - -Scenario: As a member I want to be able to log into the website - [Documentation] Example of a BDD-style (Behavior-driven development) test. - Given a login form - When I enter valid credentials - Then I am logged in - - -*** Keywords ***************************************************************** - -# --- Given ------------------------------------------------------------------ - -a login form - Go To ${PLONE_URL}/login_form - Wait until page contains Login Name - Wait until page contains Password - - -# --- WHEN ------------------------------------------------------------------- - -I enter valid credentials - Input Text __ac_name admin - Input Text __ac_password secret - Click Button Log in - - -# --- THEN ------------------------------------------------------------------- - -I am logged in - Wait until page contains You are now logged in - Page should contain You are now logged in diff --git a/src/collective/handlebars/tests/test_robot.py b/src/collective/handlebars/tests/test_robot.py deleted file mode 100644 index f83b9fc..0000000 --- a/src/collective/handlebars/tests/test_robot.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from collective.handlebars.testing import COLLECTIVE_HANDLEBARS_ACCEPTANCE_TESTING # noqa -from plone.app.testing import ROBOT_TEST_LEVEL -from plone.testing import layered - -import os -import robotsuite -import unittest - - -def test_suite(): - suite = unittest.TestSuite() - current_dir = os.path.abspath(os.path.dirname(__file__)) - robot_dir = os.path.join(current_dir, 'robot') - robot_tests = [ - os.path.join('robot', doc) for doc in os.listdir(robot_dir) - if doc.endswith('.robot') and doc.startswith('test_') - ] - for robot_test in robot_tests: - robottestsuite = robotsuite.RobotTestSuite(robot_test) - robottestsuite.level = ROBOT_TEST_LEVEL - suite.addTests([ - layered( - robottestsuite, - layer=COLLECTIVE_HANDLEBARS_ACCEPTANCE_TESTING - ), - ]) - return suite diff --git a/src/collective/handlebars/tests/test_setup.py b/src/collective/handlebars/tests/test_setup.py index 195d137..1cab495 100644 --- a/src/collective/handlebars/tests/test_setup.py +++ b/src/collective/handlebars/tests/test_setup.py @@ -1,11 +1,29 @@ # -*- coding: utf-8 -*- """Setup tests for this package.""" -from collective.handlebars.testing import COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING # noqa +from collective.handlebars.testing import ( + COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING, +) from plone import api import unittest +try: + from Products.CMFPlone.utils import get_installer +except ImportError: # pragma: no cover + # Quick shim for 5.1 api change + + class get_installer(object): # noqa + def __init__(self, portal, request): # noqa + self.installer = api.portal.get_tool(name="portal_quickinstaller") + + def is_product_installed(self, name): + return self.installer.isProductInstalled(name) + + def uninstall_product(self, name): + return self.installer.uninstallProducts([name]) + + class TestSetup(unittest.TestCase): """Test that collective.handlebars is properly installed.""" @@ -13,19 +31,20 @@ class TestSetup(unittest.TestCase): def setUp(self): """Custom shared utility setup for tests.""" - self.portal = self.layer['portal'] - self.installer = api.portal.get_tool('portal_quickinstaller') + self.portal = self.layer["portal"] + self.installer = get_installer(self.portal, self.layer["request"]) def test_product_installed(self): """Test if collective.handlebars is installed.""" - self.assertTrue(self.installer.isProductInstalled( - 'collective.handlebars')) + self.assertTrue( + self.installer.is_product_installed("collective.handlebars") + ) def test_browserlayer(self): """Test that ICollectiveHandlebarsLayer is registered.""" - from collective.handlebars.interfaces import ( - ICollectiveHandlebarsLayer) + from collective.handlebars.interfaces import ICollectiveHandlebarsLayer from plone.browserlayer import utils + self.assertIn(ICollectiveHandlebarsLayer, utils.registered_layers()) @@ -34,27 +53,34 @@ class TestUninstall(unittest.TestCase): layer = COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING def setUp(self): - self.portal = self.layer['portal'] - self.installer = api.portal.get_tool('portal_quickinstaller') - self.installer.uninstallProducts(['collective.handlebars']) + self.portal = self.layer["portal"] + self.installer = get_installer(self.portal, self.layer["request"]) + self.installer.uninstall_product("collective.handlebars") def test_product_uninstalled(self): """Test if collective.handlebars is cleanly uninstalled.""" - self.assertFalse(self.installer.isProductInstalled( - 'collective.handlebars')) + self.assertFalse( + self.installer.is_product_installed("collective.handlebars") + ) def test_browserlayer_removed(self): """Test that ICollectiveHandlebarsLayer is removed.""" from collective.handlebars.interfaces import ICollectiveHandlebarsLayer from plone.browserlayer import utils + self.assertNotIn(ICollectiveHandlebarsLayer, utils.registered_layers()) def test_hiddenprofiles(self): - """ Test uninstall profile is hidden + """Test uninstall profile is hidden :return:None """ from collective.handlebars.setuphandlers import HiddenProfiles - self.assertIn('collective.handlebars:default', - HiddenProfiles().getNonInstallableProfiles()) - self.assertIn('collective.handlebars:uninstall', - HiddenProfiles().getNonInstallableProfiles()) + + self.assertIn( + "collective.handlebars:default", + HiddenProfiles().getNonInstallableProfiles(), + ) + self.assertIn( + "collective.handlebars:uninstall", + HiddenProfiles().getNonInstallableProfiles(), + ) diff --git a/src/collective/handlebars/tests/test_views.py b/src/collective/handlebars/tests/test_views.py index 3b5e41d..9a8b3a5 100644 --- a/src/collective/handlebars/tests/test_views.py +++ b/src/collective/handlebars/tests/test_views.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """Setup tests for this package.""" -from collective.handlebars.testing import COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING # noqa +from collective.handlebars.testing import ( + COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING, +) from collective.handlebars.browser.views import HandlebarsBrowserView from collective.handlebars.browser.views import HandlebarsTile from collective.handlebars.browser.views import HandlebarsPersistentTile @@ -9,72 +11,66 @@ from zope.i18nmessageid import MessageFactory from zope import component, interface from zope.i18n.interfaces import ITranslationDomain -import os.path +import os.path +import six import unittest -TEST_DATA__DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'data') -_ = MessageFactory('my.domain') +TEST_DATA__DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "data" +) +_ = MessageFactory("my.domain") def normalize(stream): - return ' '.join([snippet.strip() for snippet in stream.split()]) + return " ".join([snippet.strip() for snippet in stream.split()]) class DummyHbsTile(HandlebarsTile): - def get_contents(self): - return {'src': 'foo', 'alt': 'bar'} + return {"src": "foo", "alt": "bar"} class DummyHbsPersistentTile(HandlebarsPersistentTile): - def get_contents(self): - return {'src': 'foo', 'alt': 'bar'} + return {"src": "foo", "alt": "bar"} class DummyHbsTemplate(object): - def __init__(self, filename): self.filename = filename @interface.implementer(ITranslationDomain) class TestDomain(dict): - def translate(self, text, *_, **__): return self[text], _[2] class DummyHbsFile(HandlebarsBrowserView): - def get_contents(self): - return {'title': u'File Test', - 'body': u'Hello HBS World!'} + return {"title": u"File Test", "body": u"Hello HBS World!"} def __call__(self): - return self.hbs_snippet('data/minimal.hbs') + return self.hbs_snippet("data/minimal.hbs") def invalid(self): - return self.hbs_snippet('data/minimal_notfound.hbs') + return self.hbs_snippet("data/minimal_notfound.hbs") class HelperHbsView(HandlebarsBrowserView): - def get_contents(self): - return {'title': u'File Test', - 'body': u'Hello HBS Wörld!'} + return {"title": u"File Test", "body": u"Hello HBS Wörld!"} def _myhelper(self, options, items): - return u'HELPER: ' + items + return u"HELPER: " + items def get_helpers(self): - return {'myhelper': self._myhelper} + return {"myhelper": self._myhelper} def __call__(self): - return self.hbs_snippet('data/helper.hbs') + return self.hbs_snippet("data/helper.hbs") class TestBrowserView(unittest.TestCase): @@ -84,64 +80,73 @@ class TestBrowserView(unittest.TestCase): def setUp(self): """Custom shared utility setup for tests.""" - self.portal = self.layer['portal'] - self.view = self.portal.restrictedTraverse('@@hbs_test_view') + self.portal = self.layer["portal"] + self.view = self.portal.restrictedTraverse("@@hbs_test_view") def test_get_contents_default(self): - """ Method `get_tile_data` is not implemented in base class + """Method `get_tile_data` is not implemented in base class :return: """ - view = HandlebarsBrowserView(self.portal, self.layer['request']) + view = HandlebarsBrowserView(self.portal, self.layer["request"]) self.assertRaises(NotImplementedError, view.get_contents) def test_base_view(self): - """ An error is raised, if no template is specified """ - view = HandlebarsBrowserView(self.portal, self.layer['request']) + """An error is raised, if no template is specified""" + view = HandlebarsBrowserView(self.portal, self.layer["request"]) self.assertRaises(ValueError, view) def test_example_view(self): """Test that a handlebars template is rendered.""" - view = self.portal.restrictedTraverse('@@hbs_test_view') - result_file = open(os.path.join(TEST_DATA__DIR, 'minimal.html')) - self.assertEqual(view(), unicode(result_file.read(), encoding='utf-8')) + view = self.portal.restrictedTraverse("@@hbs_test_view") + result_file = open(os.path.join(TEST_DATA__DIR, "minimal.html")) + self.assertEqual(view(), six.ensure_text(result_file.read())) def test_translate(self): - view = HandlebarsBrowserView(self.portal, self.layer['request']) - component.provideUtility(TestDomain(Allowed=_('Erlaubt')), - name='my.domain') - self.assertEqual(view.translate(_('Allowed'), target_language='de'), - (u'Erlaubt', 'de')) + view = HandlebarsBrowserView(self.portal, self.layer["request"]) + component.provideUtility( + TestDomain(Allowed=_("Erlaubt")), + provides=ITranslationDomain, + name="my.domain", + ) + self.assertEqual( + view.translate(_("Allowed"), target_language="de"), + (u"Erlaubt", "de"), + ) def test_translate_default_lang(self): - view = HandlebarsBrowserView(self.portal, self.layer['request']) - component.provideUtility(TestDomain(Allowed=_('Allowed')), - name='my.domain') - self.assertEqual(view.translate(_('Allowed')), (u'Allowed', 'en')) + view = HandlebarsBrowserView(self.portal, self.layer["request"]) + component.provideUtility( + TestDomain(Allowed=_("Allowed")), + provides=ITranslationDomain, + name="my.domain", + ) + self.assertEqual(view.translate(_("Allowed")), (u"Allowed", "en")) def test_hbs_snippet(self): - view = DummyHbsFile(self.portal, self.layer['request']) - result_file = open(os.path.join(TEST_DATA__DIR, 'minimal_file.html')) - self.assertEqual(view(), unicode(result_file.read(), encoding='utf-8')) + view = DummyHbsFile(self.portal, self.layer["request"]) + result_file = open(os.path.join(TEST_DATA__DIR, "minimal_file.html")) + self.assertEqual(view(), six.ensure_text(result_file.read())) def test_hbs_snippet_nofile(self): - view = DummyHbsFile(self.portal, self.layer['request']) + view = DummyHbsFile(self.portal, self.layer["request"]) self.assertRaises(ValueError, view.invalid) def test_get_path_from_prefix(self): - view = DummyHbsFile(self.portal, self.layer['request']) - self.assertEqual(view.get_path_from_prefix(_prefix='.'), '.') + view = DummyHbsFile(self.portal, self.layer["request"]) + self.assertEqual(view.get_path_from_prefix(_prefix="."), ".") def test_helpers(self): - view = HelperHbsView(self.portal, self.layer['request']) - result_file = open(os.path.join(TEST_DATA__DIR, 'helper.html')) - self.assertEqual(view(), unicode(result_file.read(), encoding='utf-8')) + view = HelperHbsView(self.portal, self.layer["request"]) + result_file = open(os.path.join(TEST_DATA__DIR, "helper.html")) + self.assertEqual(view(), six.ensure_text(result_file.read())) def test_render_helper(self): - view = HandlebarsBrowserView(self.portal, self.layer['request']) - rendered = view._wrap_widget('Hello unittest') + view = HandlebarsBrowserView(self.portal, self.layer["request"]) + rendered = view._wrap_widget("Hello unittest") self.assertEqual( - rendered, '
Hello unittest
') + rendered, "
Hello unittest
" + ) class TestPloneView(unittest.TestCase): @@ -151,55 +156,31 @@ class TestPloneView(unittest.TestCase): def setUp(self): """Custom shared utility setup for tests.""" - self.portal = self.layer['portal'] - self.view = self.portal.restrictedTraverse('@@hbs_test_ploneview') + self.portal = self.layer["portal"] + self.view = self.portal.restrictedTraverse("@@hbs_test_ploneview") def test_get_contents_default(self): - """ Method `get_tile_data` is not implemented in base class + """Method `get_tile_data` is not implemented in base class :return: """ - view = HandlebarsBrowserView(self.portal, self.layer['request']) + view = HandlebarsBrowserView(self.portal, self.layer["request"]) self.assertRaises(NotImplementedError, view.get_contents) def test_base_view(self): - """ An error is raised, if no template is specified """ - view = HandlebarsBrowserView(self.portal, self.layer['request']) + """An error is raised, if no template is specified""" + view = HandlebarsBrowserView(self.portal, self.layer["request"]) self.assertRaises(ValueError, view) def test_example_view(self): """Test that a handlebars template is rendered.""" - view = self.portal.restrictedTraverse('@@hbs_test_ploneview') - if api.env.plone_version().startswith('5.0'): - filename = os.path.join(TEST_DATA__DIR, 'minimal_plone.html') + view = self.portal.restrictedTraverse("@@hbs_test_ploneview") + if api.env.plone_version().startswith("5.0"): + filename = os.path.join(TEST_DATA__DIR, "minimal_plone.html") else: - filename = os.path.join(TEST_DATA__DIR, 'minimal_plone_51.html') + filename = os.path.join(TEST_DATA__DIR, "minimal_plone_51.html") with open(filename) as f: - self.assertIn( - unicode(f.read().strip(), encoding='utf-8'), - normalize(view()) - ) - - -class TestUninstall(unittest.TestCase): - - layer = COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING - - def setUp(self): - self.portal = self.layer['portal'] - self.installer = api.portal.get_tool('portal_quickinstaller') - self.installer.uninstallProducts(['collective.handlebars']) - - def test_product_uninstalled(self): - """Test if collective.handlebars is cleanly uninstalled.""" - self.assertFalse(self.installer.isProductInstalled( - 'collective.handlebars')) - - def test_browserlayer_removed(self): - """Test that ICollectiveHandlebarsLayer is removed.""" - from collective.handlebars.interfaces import ICollectiveHandlebarsLayer - from plone.browserlayer import utils - self.assertNotIn(ICollectiveHandlebarsLayer, utils.registered_layers()) + self.assertIn(six.ensure_text(f.read().strip()), normalize(view())) class TestHandlebarTile(unittest.TestCase): @@ -208,49 +189,53 @@ class TestHandlebarTile(unittest.TestCase): layer = COLLECTIVE_HANDLEBARS_INTEGRATION_TESTING def setUp(self): - self.tile = HandlebarsTile(self.layer['portal'], self.layer['request']) - self.template_path = os.path.join(TEST_DATA__DIR, - '_slideshow_slide.js.hbs') + self.tile = HandlebarsTile(self.layer["portal"], self.layer["request"]) + self.template_path = os.path.join( + TEST_DATA__DIR, "_slideshow_slide.js.hbs" + ) def test_get_contents_default(self): - """ Method `get_tile_data` is not implemented in base class + """Method `get_tile_data` is not implemented in base class :return: """ - view = HandlebarsBrowserView(self.layer['portal'], - self.layer['request']) + view = HandlebarsBrowserView( + self.layer["portal"], self.layer["request"] + ) self.assertRaises(NotImplementedError, view.get_contents) def test_get_hbs_template(self): template = self.tile._get_hbs_template(self.template_path) self.assertEqual( - template({'src': 'foo', 'alt': 'bar'}).strip(), - u'bar' + template({"src": "foo", "alt": "bar"}).strip(), + u'bar', ) def test_get_partial_key(self): - self.assertEqual(self.tile._get_partial_key(self.template_path), - '_slideshow_slide.js') + self.assertEqual( + self.tile._get_partial_key(self.template_path), + "_slideshow_slide.js", + ) def test_call_notemplate(self): - """ An error is raised, if no template is specified """ + """An error is raised, if no template is specified""" self.assertRaises(ValueError, self.tile) def test_call_with_template(self): - tile = DummyHbsTile(self.layer['portal'], self.layer['request']) - setattr(tile, 'index', DummyHbsTemplate(self.template_path)) + tile = DummyHbsTile(self.layer["portal"], self.layer["request"]) + setattr(tile, "index", DummyHbsTemplate(self.template_path)) self.assertEqual( normalize(tile()), - u'
bar
' + u'
bar
', ) def test_call_with_body_template(self): - template_path = os.path.join(TEST_DATA__DIR, 'body_tile.hbs') - tile = DummyHbsTile(self.layer['portal'], self.layer['request']) - setattr(tile, 'index', DummyHbsTemplate(template_path)) + template_path = os.path.join(TEST_DATA__DIR, "body_tile.hbs") + tile = DummyHbsTile(self.layer["portal"], self.layer["request"]) + setattr(tile, "index", DummyHbsTemplate(template_path)) self.assertEqual( tile().strip(), - u'' + u'', ) @@ -261,38 +246,43 @@ class TestHandlebarPersistentTile(unittest.TestCase): def setUp(self): self.tile = HandlebarsPersistentTile( - self.layer['portal'], self.layer['request']) - self.template_path = os.path.join(TEST_DATA__DIR, - '_slideshow_slide.js.hbs') + self.layer["portal"], self.layer["request"] + ) + self.template_path = os.path.join( + TEST_DATA__DIR, "_slideshow_slide.js.hbs" + ) def test_get_contents_default(self): - """ Method `get_tile_data` is not implemented in base class + """Method `get_tile_data` is not implemented in base class :return: """ - view = HandlebarsBrowserView(self.layer['portal'], - self.layer['request']) + view = HandlebarsBrowserView( + self.layer["portal"], self.layer["request"] + ) self.assertRaises(NotImplementedError, view.get_contents) def test_get_hbs_template(self): template = self.tile._get_hbs_template(self.template_path) self.assertEqual( - template({'src': 'foo', 'alt': 'bar'}).strip(), - u'bar' + template({"src": "foo", "alt": "bar"}).strip(), + u'bar', ) def test_get_partial_key(self): - self.assertEqual(self.tile._get_partial_key(self.template_path), - '_slideshow_slide.js') + self.assertEqual( + self.tile._get_partial_key(self.template_path), + "_slideshow_slide.js", + ) def test_call_notemplate(self): - """ An error is raised, if no template is specified """ + """An error is raised, if no template is specified""" self.assertRaises(ValueError, self.tile) def test_call_with_template(self): - tile = DummyHbsTile(self.layer['portal'], self.layer['request']) - setattr(tile, 'index', DummyHbsTemplate(self.template_path)) + tile = DummyHbsTile(self.layer["portal"], self.layer["request"]) + setattr(tile, "index", DummyHbsTemplate(self.template_path)) self.assertEqual( normalize(tile()), - u'
bar
' + u'
bar
', ) diff --git a/versions.cfg b/versions.cfg new file mode 100644 index 0000000..3412694 --- /dev/null +++ b/versions.cfg @@ -0,0 +1,24 @@ +[versions] +# Don't use a released version of collective.handlebars +collective.handlebars = +zc.buildout = +setuptools = +zc.recipe.egg = 2.0.7 +PyMeta3 = 0.5.1 +PyYAML = 5.4.1 +chardet = 3.0.4 +check-manifest = 0.37 +configparser = 4.0.2 +coverage = 5.5 +flake8 = 3.9.2 +flake8-blind-except = 0.2.0 +flake8-coding = 1.3.2 +flake8-debugger = 3.2.1 +idna = 2.10 +mccabe = 0.6.1 +plone.recipe.codeanalysis = 3.0.1 +pybars3 = 0.9.7 +pycodestyle = 2.7.0 +pyflakes = 2.3.1 +python-coveralls = 2.9.3 +sh = 1.14.2