diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..9b23c02 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,35 @@ +name: Black +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.8] + + 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- + + # install black + - name: install black + run: pip install black + + # run black + - name: run black + run: black src/ --check --diff diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml new file mode 100644 index 0000000..97c7dfa --- /dev/null +++ b/.github/workflows/flake8.yml @@ -0,0 +1,35 @@ +name: Flake8 +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.8] + + 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- + + # install flake8 + - name: install flake8 + run: pip install flake8 + + # run black + - name: run flake8 + run: flake8 src/ setup.py diff --git a/.github/workflows/isort.yaml b/.github/workflows/isort.yaml new file mode 100644 index 0000000..039aeb0 --- /dev/null +++ b/.github/workflows/isort.yaml @@ -0,0 +1,10 @@ +name: Run isort +on: + - push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: isort/isort-action@v1 diff --git a/.github/workflows/pyroma.yml b/.github/workflows/pyroma.yml new file mode 100644 index 0000000..bbad3c2 --- /dev/null +++ b/.github/workflows/pyroma.yml @@ -0,0 +1,35 @@ +name: Pyroma +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.8] + + 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- + + # install pyroma + - name: install pyroma + run: pip install pyroma + + # run pyroma + - name: run pyroma + run: pyroma -n 10 -d . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..4d9c4d1 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,67 @@ +name: Tests + +on: + push: + paths-ignore: + - '**.md' + - '**.rst' +jobs: + build: + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python: ["3.8", "3.9", "3.10", "3.11"] + plone: ["52", "60"] + tz: ["UTC", "Europe/Rome"] + exclude: + - python: "3.10" + plone: "52" + - python: "3.11" + plone: "52" + steps: + - uses: actions/checkout@v3 + - name: Cache eggs + uses: actions/cache@v3 + with: + path: eggs + key: ${{ runner.OS }}-build-python${{ matrix.python }}-${{ matrix.plone }} + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python }} + - name: Install dependencies + run: | + pip install -r requirements.txt -c constraints_plone${{ matrix.plone }}.txt + cp test_plone${{ matrix.plone }}.cfg buildout.cfg + - name: Install buildout + run: | + buildout -N code-analysis:return-status-codes=True + - name: Code analysis + run: | + bin/code-analysis + - name: Run tests + run: | + bin/test-coverage + env: + PROXY_BEARER_AUTH: on + TZ: ${{ matrix.tz }} + - name: Upload coverage data to coveralls.io + run: | + pip install coveralls + coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_FLAG_NAME: py${{ matrix.python }}-plone${{ matrix.plone }}-tz${{ matrix.tz }} + COVERALLS_PARALLEL: true + + coveralls_finish: + needs: build + runs-on: ubuntu-latest + steps: + - name: Finished + run: | + pip install --upgrade coveralls + coveralls --service=github --finish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/zpretty.yml b/.github/workflows/zpretty.yml new file mode 100644 index 0000000..180f0e9 --- /dev/null +++ b/.github/workflows/zpretty.yml @@ -0,0 +1,40 @@ +name: zpretty +on: [push] +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.8] + + 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- + + # install zpretty + - name: install zpretty + run: pip install zpretty + + # run zpretty + - name: run zpretty + run: find src -name '*.zcml' | xargs zpretty -i + + # XXX: this doesn't work on gh actions (https://github.com/plone/plone.restapi/pull/1119/checks?check_run_id=2686474411) + # run git diff + - name: run git diff + run: git diff --exit-code diff --git a/.gitignore b/.gitignore index def23af..0bf387c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,16 @@ +__pycache__ +.DS_Store +pyvenv.cfg .coverage +coverage.json +.python-version *.egg-info -*.EGG-INFO -*.egg -*.EGG *.log *.mo *.py? *.swp # dirs +__pycache__ bin/ buildout-cache/ develop-eggs/ @@ -16,8 +19,10 @@ htmlcov/ include/ lib/ local/ +node_modules/ parts/ src/* +dist/* test.plone_addon/ var/ # files @@ -28,6 +33,9 @@ log.html output.xml pip-selfcheck.json report.html +.vscode/ +.tox/ +reports/ # excludes !.coveragerc !.editorconfig @@ -35,5 +43,5 @@ report.html !.gitignore !.gitkeep !.travis.yml -!src/rer -.DS_Store +!src/redturtle +.idea/ diff --git a/base.cfg b/base.cfg index 331cad6..a18af18 100644 --- a/base.cfg +++ b/base.cfg @@ -17,8 +17,11 @@ parts = omelette robot plone-helper-scripts -develop = . +develop = . +sources-dir = extras +auto-checkout = * +always-checkout = force [instance] recipe = plone.recipe.zope2instance @@ -29,12 +32,24 @@ environment-vars = eggs = Plone Pillow - rer.externalnews [test] - + rer.externalnews[test] + +zcml-additional = + + + [code-analysis] -recipe = plone.recipe.codeanalysis[recommended] -directory = ${buildout:directory}/src/collective +recipe = plone.recipe.codeanalysis +directory = ${buildout:directory}/src/rer return-status-codes = False @@ -46,8 +61,6 @@ eggs = ${instance:eggs} [test] recipe = zc.recipe.testrunner eggs = ${instance:eggs} -initialization = - os.environ['TZ'] = 'UTC' defaults = ['-s', 'rer.externalnews', '--auto-color', '--auto-progress'] @@ -60,10 +73,12 @@ eggs = coverage recipe = collective.recipe.template input = inline: #!/bin/bash + set -e export TZ=UTC ${buildout:directory}/bin/coverage run bin/test $* ${buildout:directory}/bin/coverage html - ${buildout:directory}/bin/coverage report -m --fail-under=90 + ${buildout:directory}/bin/coverage report -m --fail-under=60 + # ${buildout:directory}/bin/coverage report -m --fail-under=90 # Fail (exit status 1) if coverage returns exit status 2 (this happens # when test coverage is below 100%. output = ${buildout:directory}/bin/test-coverage @@ -104,6 +119,4 @@ scripts = [versions] # Don't use a released version of rer.externalnews rer.externalnews = - -[sources] -plone.formwidget.contenttree = git https://github.com/plone/plone.formwidget.contenttree.git +setuptools = diff --git a/buildout.cfg b/buildout.cfg index 1672439..d7db038 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -2,7 +2,4 @@ # use this extend one of the buildout configuration: extends = -# test_plone43.cfg -# test_plone50.cfg -# test_plone51.cfg - test_plone52.cfg + test_plone60.cfg diff --git a/constraints.txt b/constraints.txt index 24cbf87..c1fb3a1 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1 +1 @@ --c constraints_plone52.txt +-c constraints_plone60.txt diff --git a/constraints_plone50.txt b/constraints_plone50.txt deleted file mode 100644 index f3a36b2..0000000 --- a/constraints_plone50.txt +++ /dev/null @@ -1,2 +0,0 @@ -setuptools==27.3.0 -zc.buildout==2.5.3 diff --git a/constraints_plone51.txt b/constraints_plone51.txt deleted file mode 100644 index b57e577..0000000 --- a/constraints_plone51.txt +++ /dev/null @@ -1,2 +0,0 @@ -setuptools==40.2.0 -zc.buildout==2.12.2 diff --git a/constraints_plone52.txt b/constraints_plone52.txt index b57e577..3dd8048 100644 --- a/constraints_plone52.txt +++ b/constraints_plone52.txt @@ -1,2 +1,3 @@ -setuptools==40.2.0 -zc.buildout==2.12.2 +-c https://dist.plone.org/release/5.2-latest/requirements.txt +# setuptools==40.2.0 +# zc.buildout==2.13.2 diff --git a/constraints_plone60.txt b/constraints_plone60.txt new file mode 100644 index 0000000..005e694 --- /dev/null +++ b/constraints_plone60.txt @@ -0,0 +1 @@ +-c https://dist.plone.org/release/6.0-latest/requirements.txt diff --git a/requirements.txt b/requirements.txt index fa2f614..af90416 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ --c constraints_plone52.txt +pip setuptools zc.buildout +wheel diff --git a/setup.cfg b/setup.cfg index f1aa06c..5029d67 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,13 +4,32 @@ ignore = .coveragerc .editorconfig .gitattributes - bootstrap-buildout.py [isort] -# for details see -# http://docs.plone.org/develop/styleguide/python.html#grouping-and-sorting +# black compatible isort rules: force_alphabetical_sort = True -force_single_line = True +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses = True lines_after_imports = 2 -line_length = 200 -not_skip = __init__.py +line_length = 88 +not_skip = + __init__.py +skip = + +[flake8] +# black compatible flake8 rules: +ignore = + W503, + C812, + E501 + T001 + C813 +# E203, E266 +exclude = bootstrap.py,docs,*.egg.,omelette +max-line-length = 88 +max-complexity = 18 +select = B,C,E,F,W,T4,B9 + +builtins = unicode,basestring diff --git a/setup.py b/setup.py index 35d1927..ccb17b3 100644 --- a/setup.py +++ b/setup.py @@ -1,57 +1,61 @@ # -*- coding: utf-8 -*- """Installer for the rer.externalnews package.""" -from setuptools import find_packages -from setuptools import setup +from setuptools import find_packages, setup -long_description = '\n\n'.join([ - open('README.rst').read(), - open('CONTRIBUTORS.rst').read(), - open('CHANGES.rst').read(), -]) +long_description = "\n\n".join( + [ + open("README.rst").read(), + open("CONTRIBUTORS.rst").read(), + open("CHANGES.rst").read(), + ] +) setup( - name='rer.externalnews', - version='1.1.1.dev0', + name="rer.externalnews", + version="1.1.1.dev0", description="A Plone add-on for Regione Emilia Romagna", long_description=long_description, # Get more from https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ + "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Plone", - "Framework :: Plone :: 5.0", + "Framework :: Plone :: 6.0", "Programming Language :: Python", - "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.10", "Operating System :: OS Independent", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", ], - keywords='Python Plone', - author='RedTurtle Technology', - author_email='sviluppoplone@redturtle.it', - url='https://pypi.python.org/pypi/rer.externalnews', - license='GPL version 2', - packages=find_packages('src', exclude=['ez_setup']), - namespace_packages=['rer'], - package_dir={'': 'src'}, + keywords="Python Plone", + author="RedTurtle Technology", + author_email="sviluppoplone@redturtle.it", + url="https://pypi.python.org/pypi/rer.externalnews", + license="GPL version 2", + packages=find_packages("src", exclude=["ez_setup"]), + namespace_packages=["rer"], + python_requires=">=3.8", + package_dir={"": "src"}, include_package_data=True, zip_safe=False, install_requires=[ - 'plone.api', - 'Products.GenericSetup>=1.8.2', - 'setuptools', - 'plone.app.dexterity', + "plone.api", + "Products.GenericSetup>=1.8.2", + "setuptools", + "plone.app.dexterity", ], extras_require={ - 'test': [ - 'plone.app.testing', + "test": [ + "plone.app.testing", # Plone KGS does not use this version, because it would break # Remove if your package shall be part of coredev. # plone_coredev tests as of 2016-04-01. - 'plone.testing>=5.0.0', - 'plone.app.contenttypes', - 'plone.app.robotframework[debug]', + "plone.testing>=5.0.0", + "plone.app.contenttypes", + "plone.app.robotframework[debug]", ], }, entry_points=""" diff --git a/src/rer/__init__.py b/src/rer/__init__.py index 68c04af..03d08ff 100644 --- a/src/rer/__init__.py +++ b/src/rer/__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/rer/externalnews/__init__.py b/src/rer/externalnews/__init__.py index 74a5038..6acc03e 100644 --- a/src/rer/externalnews/__init__.py +++ b/src/rer/externalnews/__init__.py @@ -3,4 +3,4 @@ from zope.i18nmessageid import MessageFactory -_ = MessageFactory('rer.externalnews') +_ = MessageFactory("rer.externalnews") diff --git a/src/rer/externalnews/browser/configure.zcml b/src/rer/externalnews/browser/configure.zcml index 84cff83..4ffe661 100644 --- a/src/rer/externalnews/browser/configure.zcml +++ b/src/rer/externalnews/browser/configure.zcml @@ -2,18 +2,19 @@ xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:plone="http://namespaces.plone.org/plone" - i18n_domain="rer.externalnews"> + i18n_domain="rer.externalnews" + > diff --git a/src/rer/externalnews/browser/externalnews_view.py b/src/rer/externalnews/browser/externalnews_view.py index 0aa87ed..63f0b28 100644 --- a/src/rer/externalnews/browser/externalnews_view.py +++ b/src/rer/externalnews/browser/externalnews_view.py @@ -11,19 +11,20 @@ # links starting with these URL scheme should not be redirected to NON_REDIRECTABLE_URL_SCHEMES = [ - 'mailto:', - 'tel:', - 'callto:', # nonstandard according to RFC 3966. used for skype. - 'webdav:', - 'caldav:' + "mailto:", + "tel:", + "callto:", # nonstandard according to RFC 3966. used for skype. + "webdav:", + "caldav:", ] # links starting with these URL scheme should not be resolved to paths NON_RESOLVABLE_URL_SCHEMES = NON_REDIRECTABLE_URL_SCHEMES + [ - 'file:', - 'ftp:', + "file:", + "ftp:", ] + class ExternalNewsRedirectView(BrowserView): """Redirect to the Link target URL, if and only if: - redirect_links property is enabled in @@ -31,41 +32,38 @@ class ExternalNewsRedirectView(BrowserView): - the link is of a redirectable type (no mailto:, etc) - AND current user doesn't have permission to edit the Link""" - index = ViewPageTemplateFile('templates/externalnews.pt') + index = ViewPageTemplateFile("templates/externalnews.pt") def _url_uses_scheme(self, schemes, url=None): url = url or self.context.externalUrl for scheme in schemes: if url.startswith(scheme): - return True + return True return False - def __call__(self): context = self.context - mtool = getToolByName(context, 'portal_membership') + mtool = getToolByName(context, "portal_membership") registry = getUtility(IRegistry) - settings = registry.forInterface(ITypesSchema, prefix='plone') + settings = registry.forInterface(ITypesSchema, prefix="plone") redirect_links = settings.redirect_links - can_edit = mtool.checkPermission('Modify portal content', context) - redirect_links = redirect_links\ - and not self._url_uses_scheme(NON_REDIRECTABLE_URL_SCHEMES) + can_edit = mtool.checkPermission("Modify portal content", context) + redirect_links = redirect_links and not self._url_uses_scheme( + NON_REDIRECTABLE_URL_SCHEMES + ) if redirect_links and not can_edit: return self.request.RESPONSE.redirect( - self.absolute_target_url().encode('utf-8')) + self.absolute_target_url().encode("utf-8") + ) else: return self.index() def url(self): - """Returns the url with link variables replaced. - """ - url = replace_link_variables_by_paths( - self.context, - self.context.externalUrl - ) + """Returns the url with link variables replaced.""" + url = replace_link_variables_by_paths(self.context, self.context.externalUrl) return url def absolute_target_url(self): @@ -76,19 +74,14 @@ def absolute_target_url(self): # For non http/https url schemes, there is no path to resolve. return url - if url.startswith('.'): + if url.startswith("."): # we just need to adapt ../relative/links, /absolute/ones work # anyway -> this requires relative links to start with ./ or # ../ - context_state = self.context.restrictedTraverse( - '@@plone_context_state' - ) - url = '/'.join([ - context_state.canonical_object_url(), - url - ]) + context_state = self.context.restrictedTraverse("@@plone_context_state") + url = "/".join([context_state.canonical_object_url(), url]) else: - if not (url.startswith('http://') or url.startswith('https://')): + if not (url.startswith("http://") or url.startswith("https://")): url = self.request.physicalPathToURL(url) return url diff --git a/src/rer/externalnews/browser/restapi/configure.zcml b/src/rer/externalnews/browser/restapi/configure.zcml index 38ce5e3..6bcb6a0 100644 --- a/src/rer/externalnews/browser/restapi/configure.zcml +++ b/src/rer/externalnews/browser/restapi/configure.zcml @@ -1,6 +1,7 @@ + i18n_domain="rer.externalnews" + > diff --git a/src/rer/externalnews/browser/restapi/serializers/configure.zcml b/src/rer/externalnews/browser/restapi/serializers/configure.zcml index 67d486e..6fa4c88 100644 --- a/src/rer/externalnews/browser/restapi/serializers/configure.zcml +++ b/src/rer/externalnews/browser/restapi/serializers/configure.zcml @@ -1,6 +1,7 @@ + i18n_domain="rer.externalnews" + > diff --git a/src/rer/externalnews/browser/restapi/serializers/externalnews.py b/src/rer/externalnews/browser/restapi/serializers/externalnews.py index 1eb25ff..d0901f3 100644 --- a/src/rer/externalnews/browser/restapi/serializers/externalnews.py +++ b/src/rer/externalnews/browser/restapi/serializers/externalnews.py @@ -3,10 +3,8 @@ from plone.restapi.interfaces import ISerializeToJson from plone.restapi.serializer.dxcontent import SerializeFolderToJson as Base from rer.externalnews.interfaces import IExternalNews -from zope.component import adapter -from zope.component import getMultiAdapter -from zope.interface import implementer -from zope.interface import Interface +from zope.component import adapter, getMultiAdapter +from zope.interface import implementer, Interface @implementer(ISerializeToJson) @@ -16,7 +14,7 @@ def __call__(self, version=None, include_items=True): res = super(SerializeToJson, self).__call__(version, include_items) steps = self.get_steps() res["steps"] = steps - res['remoteUrl'] = res['externalUrl'] + res["remoteUrl"] = res["externalUrl"] return res def get_steps(self): diff --git a/src/rer/externalnews/configure.zcml b/src/rer/externalnews/configure.zcml index 9d0367b..4377262 100644 --- a/src/rer/externalnews/configure.zcml +++ b/src/rer/externalnews/configure.zcml @@ -2,7 +2,8 @@ xmlns="http://namespaces.zope.org/zope" xmlns:genericsetup="http://namespaces.zope.org/genericsetup" xmlns:i18n="http://namespaces.zope.org/i18n" - i18n_domain="rer.externalnews"> + i18n_domain="rer.externalnews" + > @@ -12,23 +13,29 @@ + name="rer.externalnews-hiddenprofiles" + /> + + diff --git a/src/rer/externalnews/indexers.py b/src/rer/externalnews/indexers.py new file mode 100644 index 0000000..25ee514 --- /dev/null +++ b/src/rer/externalnews/indexers.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from plone.indexer.decorator import indexer +from rer.externalnews.interfaces import IExternalNews + + +@indexer(IExternalNews) +def getRemoteUrl(obj): + return getattr(obj, "externalUrl", "") diff --git a/src/rer/externalnews/interfaces.py b/src/rer/externalnews/interfaces.py index f9a7d60..2d70355 100644 --- a/src/rer/externalnews/interfaces.py +++ b/src/rer/externalnews/interfaces.py @@ -12,22 +12,23 @@ class IRerExternalnewsLayer(IDefaultBrowserLayer): class IExternalNews(Interface): - """ Interfaccia per il content type: External News""" + """Interfaccia per il content type: External News""" externalUrl = schema.TextLine( - title=_(u'rer_externalnews_externalurl', default=u'External url'), + title=_("rer_externalnews_externalurl", default="External url"), description=_( - u'rer_ernews_externalurl_help', - default=u'Insert a valid link to an external resource'), - default=u'', + "rer_ernews_externalurl_help", + default="Insert a valid link to an external resource", + ), + default="", required=True, ) externalSource = schema.TextLine( - title=_(u'rer_externalnews_externalsource', default=u'Source'), + title=_("rer_externalnews_externalsource", default="Source"), description=_( - u'rer_externalnews_externalsource_help', - default=u"Where the URL is from."), - default=u'', + "rer_externalnews_externalsource_help", default="Where the URL is from." + ), + default="", required=False, ) diff --git a/src/rer/externalnews/profiles/default/metadata.xml b/src/rer/externalnews/profiles/default/metadata.xml index c68c27d..749280d 100644 --- a/src/rer/externalnews/profiles/default/metadata.xml +++ b/src/rer/externalnews/profiles/default/metadata.xml @@ -1,6 +1,6 @@ - 1100 + 1200 profile-plone.app.dexterity:default diff --git a/src/rer/externalnews/setuphandlers.py b/src/rer/externalnews/setuphandlers.py index d9f7842..669c712 100644 --- a/src/rer/externalnews/setuphandlers.py +++ b/src/rer/externalnews/setuphandlers.py @@ -5,11 +5,10 @@ @implementer(INonInstallable) class HiddenProfiles(object): - def getNonInstallableProfiles(self): """Hide uninstall profile from site-creation and quickinstaller""" return [ - 'rer.externalnews:uninstall', + "rer.externalnews:uninstall", ] diff --git a/src/rer/externalnews/testing.py b/src/rer/externalnews/testing.py index 01e1aa0..f81b9e1 100644 --- a/src/rer/externalnews/testing.py +++ b/src/rer/externalnews/testing.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- 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.app.testing import ( + applyProfile, + FunctionalTesting, + IntegrationTesting, + PloneSandboxLayer, +) from plone.testing import z2 import rer.externalnews class RerExternalnewsLayer(PloneSandboxLayer): - defaultBases = (PLONE_APP_CONTENTTYPES_FIXTURE,) def setUpZope(self, app, configurationContext): @@ -21,29 +22,23 @@ def setUpZope(self, app, configurationContext): self.loadZCML(package=rer.externalnews) def setUpPloneSite(self, portal): - applyProfile(portal, 'rer.externalnews:default') + applyProfile(portal, "rer.externalnews:default") RER_EXTERNALNEWS_FIXTURE = RerExternalnewsLayer() RER_EXTERNALNEWS_INTEGRATION_TESTING = IntegrationTesting( - bases=(RER_EXTERNALNEWS_FIXTURE,), - name='RerExternalnewsLayer:IntegrationTesting' + bases=(RER_EXTERNALNEWS_FIXTURE,), name="RerExternalnewsLayer:IntegrationTesting" ) RER_EXTERNALNEWS_FUNCTIONAL_TESTING = FunctionalTesting( - bases=(RER_EXTERNALNEWS_FIXTURE,), - name='RerExternalnewsLayer:FunctionalTesting' + bases=(RER_EXTERNALNEWS_FIXTURE,), name="RerExternalnewsLayer:FunctionalTesting" ) RER_EXTERNALNEWS_ACCEPTANCE_TESTING = FunctionalTesting( - bases=( - RER_EXTERNALNEWS_FIXTURE, - REMOTE_LIBRARY_BUNDLE_FIXTURE, - z2.ZSERVER_FIXTURE - ), - name='RerExternalnewsLayer:AcceptanceTesting' + bases=(RER_EXTERNALNEWS_FIXTURE, REMOTE_LIBRARY_BUNDLE_FIXTURE, z2.ZSERVER_FIXTURE), + name="RerExternalnewsLayer:AcceptanceTesting", ) diff --git a/src/rer/externalnews/tests/robot/test_example.robot b/src/rer/externalnews/tests/robot/test_example.robot deleted file mode 100644 index cc36da1..0000000 --- a/src/rer/externalnews/tests/robot/test_example.robot +++ /dev/null @@ -1,66 +0,0 @@ -# ============================================================================ -# EXAMPLE ROBOT TESTS -# ============================================================================ -# -# Run this robot test stand-alone: -# -# $ bin/test -s rer.externalnews -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 rer.externalnews.testing.RER_EXTERNALNEWS_ACCEPTANCE_TESTING -# -# 2) Run robot tests: -# -# $ bin/robot src/rer/externalnews/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/rer/externalnews/tests/robot/test_externalnews.robot b/src/rer/externalnews/tests/robot/test_externalnews.robot deleted file mode 100644 index fdac326..0000000 --- a/src/rer/externalnews/tests/robot/test_externalnews.robot +++ /dev/null @@ -1,87 +0,0 @@ -# ============================================================================ -# DEXTERITY ROBOT TESTS -# ============================================================================ -# -# Run this robot test stand-alone: -# -# $ bin/test -s rer.externalnews -t test_externalnews.robot --all -# -# Run this robot test with robot server (which is faster): -# -# 1) Start robot server: -# -# $ bin/robot-server --reload-path src rer.externalnews.testing.RER_EXTERNALNEWS_ACCEPTANCE_TESTING -# -# 2) Run robot tests: -# -# $ bin/robot src/plonetraining/testing/tests/robot/test_externalnews.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 site administrator I can add a ExternalNews - Given a logged-in site administrator - and an add externalnews form - When I type 'My ExternalNews' into the title field - and I submit the form - Then a externalnews with the title 'My ExternalNews' has been created - -Scenario: As a site administrator I can view a ExternalNews - Given a logged-in site administrator - and a externalnews 'My ExternalNews' - When I go to the externalnews view - Then I can see the externalnews title 'My ExternalNews' - - -*** Keywords ***************************************************************** - -# --- Given ------------------------------------------------------------------ - -a logged-in site administrator - Enable autologin as Site Administrator - -an add externalnews form - Go To ${PLONE_URL}/++add++ExternalNews - -a externalnews 'My ExternalNews' - Create content type=ExternalNews id=my-externalnews title=My ExternalNews - - -# --- WHEN ------------------------------------------------------------------- - -I type '${title}' into the title field - Input Text name=form.widgets.title ${title} - -I submit the form - Click Button Save - -I go to the externalnews view - Go To ${PLONE_URL}/my-externalnews - Wait until page contains Site Map - - -# --- THEN ------------------------------------------------------------------- - -a externalnews with the title '${title}' has been created - Wait until page contains Site Map - Page should contain ${title} - Page should contain Item created - -I can see the externalnews title '${title}' - Wait until page contains Site Map - Page should contain ${title} diff --git a/src/rer/externalnews/tests/test_externalnews.py b/src/rer/externalnews/tests/test_externalnews.py index 8a77173..26b0fd1 100644 --- a/src/rer/externalnews/tests/test_externalnews.py +++ b/src/rer/externalnews/tests/test_externalnews.py @@ -1,45 +1,26 @@ # -*- coding: utf-8 -*- from plone import api -from plone.app.testing import setRoles -from plone.app.testing import TEST_USER_ID -from plone.dexterity.interfaces import IDexterityFTI -from rer.externalnews.interfaces import IExternalNews -from rer.externalnews.testing import RER_EXTERNALNEWS_INTEGRATION_TESTING # noqa -from zope.component import createObject -from zope.component import queryUtility +from plone.app.testing import setRoles, TEST_USER_ID +from rer.externalnews.testing import RER_EXTERNALNEWS_INTEGRATION_TESTING import unittest class ExternalNewsIntegrationTest(unittest.TestCase): - layer = RER_EXTERNALNEWS_INTEGRATION_TESTING def setUp(self): """Custom shared utility setup for tests.""" - self.portal = self.layer['portal'] - setRoles(self.portal, TEST_USER_ID, ['Manager']) - self.installer = api.portal.get_tool('portal_quickinstaller') - - def test_schema(self): - fti = queryUtility(IDexterityFTI, name='ExternalNews') - schema = fti.lookupSchema() - self.assertEqual(IExternalNews, schema) - - def test_fti(self): - fti = queryUtility(IDexterityFTI, name='ExternalNews') - self.assertTrue(fti) + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) - def test_factory(self): - fti = queryUtility(IDexterityFTI, name='ExternalNews') - factory = fti.factory - obj = createObject(factory) - self.assertTrue(IExternalNews.providedBy(obj)) - - def test_adding(self): - obj = api.content.create( + def test_getRemoteUrl_metadata_set(self): + news = api.content.create( container=self.portal, - type='ExternalNews', - id='ExternalNews', + type="ExternalNews", + id="ExternalNews", + externalUrl="http://www.plone.org", ) - self.assertTrue(IExternalNews.providedBy(obj)) + + res = api.content.find(UID=news.UID())[0] + self.assertEqual(res.getRemoteUrl, news.externalUrl) diff --git a/src/rer/externalnews/tests/test_robot.py b/src/rer/externalnews/tests/test_robot.py deleted file mode 100644 index 6e1f07b..0000000 --- a/src/rer/externalnews/tests/test_robot.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from plone.app.testing import ROBOT_TEST_LEVEL -from plone.testing import layered -from rer.externalnews.testing import RER_EXTERNALNEWS_ACCEPTANCE_TESTING # noqa - -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=RER_EXTERNALNEWS_ACCEPTANCE_TESTING - ), - ]) - return suite diff --git a/src/rer/externalnews/tests/test_setup.py b/src/rer/externalnews/tests/test_setup.py deleted file mode 100644 index 4162ea3..0000000 --- a/src/rer/externalnews/tests/test_setup.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -"""Setup tests for this package.""" -from plone import api -from rer.externalnews.testing import RER_EXTERNALNEWS_INTEGRATION_TESTING # noqa - -import unittest - - -try: - from Products.CMFPlone.utils import get_installer -except ImportError: - get_installer = None - - -class TestSetup(unittest.TestCase): - """Test that rer.externalnews is properly installed.""" - - layer = RER_EXTERNALNEWS_INTEGRATION_TESTING - - def setUp(self): - """Custom shared utility setup for tests.""" - self.portal = self.layer['portal'] - if get_installer: - self.installer = get_installer(self.portal, self.layer['request']) - else: - self.installer = api.portal.get_tool('portal_quickinstaller') - - def test_product_installed(self): - """Test if rer.externalnews is installed.""" - self.assertTrue(self.installer.isProductInstalled( - 'rer.externalnews')) - - def test_browserlayer(self): - """Test that IRerExternalnewsLayer is registered.""" - from rer.externalnews.interfaces import ( - IRerExternalnewsLayer) - from plone.browserlayer import utils - self.assertIn( - IRerExternalnewsLayer, - utils.registered_layers()) - - -class TestUninstall(unittest.TestCase): - - layer = RER_EXTERNALNEWS_INTEGRATION_TESTING - - def setUp(self): - self.portal = self.layer['portal'] - if get_installer: - self.installer = get_installer(self.portal, self.layer['request']) - else: - self.installer = api.portal.get_tool('portal_quickinstaller') - self.installer.uninstallProducts(['rer.externalnews']) - - def test_product_uninstalled(self): - """Test if rer.externalnews is cleanly uninstalled.""" - self.assertFalse(self.installer.isProductInstalled( - 'rer.externalnews')) - - def test_browserlayer_removed(self): - """Test that IRerExternalnewsLayer is removed.""" - from rer.externalnews.interfaces import \ - IRerExternalnewsLayer - from plone.browserlayer import utils - self.assertNotIn( - IRerExternalnewsLayer, - utils.registered_layers()) diff --git a/src/rer/externalnews/upgrades/configure.zcml b/src/rer/externalnews/upgrades/configure.zcml index 06c680a..3e14f43 100644 --- a/src/rer/externalnews/upgrades/configure.zcml +++ b/src/rer/externalnews/upgrades/configure.zcml @@ -1,15 +1,24 @@ + i18n_domain="rer.externalnews" + > + title="Upgrade rer.externalnews to 1100" + description="Migrates rer.externalnews to 1100" + profile="rer.externalnews:default" + source="1000" + destination="1100" + handler=".upgrades.to_1100" + sortkey="1" + /> + diff --git a/src/rer/externalnews/upgrades/upgrades.py b/src/rer/externalnews/upgrades/upgrades.py index a8e16ab..c18f4ed 100644 --- a/src/rer/externalnews/upgrades/upgrades.py +++ b/src/rer/externalnews/upgrades/upgrades.py @@ -1,20 +1,33 @@ # -*- coding: utf-8 -*- +from plone import api + import logging logger = logging.getLogger(__name__) -default_profile = 'profile-rer.externalnews:default' +default_profile = "profile-rer.externalnews:default" def to_1100(context): - """ - """ - logger.info('Upgrading rer.externalnews to version 1100') - context.runImportStepFromProfile(default_profile, 'repositorytool') - context.runImportStepFromProfile(default_profile, 'difftool') - context.runImportStepFromProfile( - default_profile, - 'typeinfo', - run_dependencies=True) + """ """ + logger.info("Upgrading rer.externalnews to version 1100") + context.runImportStepFromProfile(default_profile, "repositorytool") + context.runImportStepFromProfile(default_profile, "difftool") + context.runImportStepFromProfile(default_profile, "typeinfo", run_dependencies=True) + + +def to_1200(context): + """ """ + logger.info("Upgrading rer.externalnews to version 1200") + brains = api.content.find(portal_type="ExternalNews") + tot = len(brains) + i = 0 + for brain in brains: + i += 1 + if i % 100 == 0: + logger.info(f"Progress: {i}/{tot}") + + news = brain.getObject() + news.reindexObject(idxs=["getRemoteUrl"]) diff --git a/test_plone52.cfg b/test_plone52.cfg index 0c2bca4..146d479 100644 --- a/test_plone52.cfg +++ b/test_plone52.cfg @@ -7,50 +7,27 @@ extends = update-versions-file = test_plone52.cfg -sources-dir = extras -auto-checkout = * -always-checkout = force - [versions] +# https://raw.githubusercontent.com/RedTurtle/dist.design.plone/main/versions.cfg +plone.app.iterate = 4.0.2 +plone.app.versioningbehavior = 1.4.6 +plone.app.vocabularies = 4.3.0 +plone.patternslib = 1.1.1 +plone.rest = 2.0.0 +plone.restapi = >=9.6.0 +plone.volto = 4.0.0 +pycountry = 19.8.18 +collective.purgebyid = 1.1.1 +Products.AdvancedQuery = 4.2.1 +dm.plone.advancedquery = 1.0 +# plone.app.caching > 3.0.0a13 requires plone.base +plone.app.caching = 3.0.0a13 + +# testing plone.testing = 7.0.1 - -# Added by buildout at 2019-04-14 16:01:26.533376 -PyYAML = 3.13 -argh = 0.26.2 -createcoverage = 1.5 -flake8-blind-except = 0.1.1 -flake8-coding = 1.3.1 -flake8-commas = 2.0.0 -flake8-debugger = 3.1.0 -flake8-deprecated = 1.3 -flake8-isort = 2.6.0 -flake8-pep3101 = 1.2.1 -flake8-plone-api = 1.4 -flake8-plone-hasattr = 0.2.post0 -flake8-print = 3.1.0 -flake8-quotes = 1.0.0 -flake8-string-format = 0.2.3 -flake8-todo = 0.7 -isort = 4.3.10 -mccabe = 0.6.1 -pathtools = 0.1.2 -plone.recipe.codeanalysis = 3.0.1 -prompt-toolkit = 1.0.15 -watchdog = 0.9.0 - -# Required by: -# plone.recipe.codeanalysis==3.0.1 -check-manifest = 0.37 - -# Required by: -# flake8-isort==2.6.0 -testfixtures = 6.6.0 - -# Required by: -# prompt-toolkit==1.0.15 -wcwidth = 0.1.7 - -# Added by buildout at 2021-08-24 11:11:01.831732 flake8 = 3.9.2 pycodestyle = 2.7.0 pyflakes = 2.3.1 +createcoverage = 1.5 +mccabe = 0.6.1 +plone.recipe.codeanalysis = 3.0.1 diff --git a/test_plone60.cfg b/test_plone60.cfg new file mode 100644 index 0000000..241cc1e --- /dev/null +++ b/test_plone60.cfg @@ -0,0 +1,20 @@ +[buildout] + +extends = + https://raw.github.com/collective/buildout.plonetest/master/test-6.0.x.cfg + https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg + base.cfg + +[versions] + +# Added by buildout at 2023-03-10 11:55:21.122842 +Products.AdvancedQuery = 4.2.1 +createcoverage = 1.5 +dm.plone.advancedquery = 1.0 +flake8 = 6.0.0 +mccabe = 0.7.0 +plone.recipe.codeanalysis = 3.0.1 +pycodestyle = 2.10.0 +pyflakes = 3.0.1 +plone.stringinterp = 2.0.0 +plone.restapi = >=9.6.0