programming language.
+Configuration Settings Common to All PGWUI Components
+-----------------------------------------------------
+
+PGWUI components all have the following configuration settings:
+
+ menu_label
+ The label for PGWUI_Menu to display, when different from the default
+
+Note that the ``menu_label`` setting appears within the settings for
+the component, not at top-level.
+
+
Usage
-----
+This section is of interest to application developers.
+
When utilizing PGWUI modules in your own `Pyramid`_ application,
modules which require the PGWUI_Common module, PGWUI_Common must be
-explicitly configured. This can be done with any of `Pyramid's
-<Pyramid_>`_ configuration mechanisms, ``pyramid_includes =
-pgwui_common`` in a ``.ini`` file or
-``config.include('pgwui_common')`` in Python code.
+explicitly configured. This can be done with any of
+`Pyramid's`_ configuration mechanisms,
-Configuration Settings Common to All PGWUI Components
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-PGWUI modules all have the following configuration settings:
+``pyramid_includes = pgwui_common`` in a ``.ini`` file or
+``config.include('pgwui_common')`` in Python code.
- menu_label
- The label for PGWUI_Menu to display, when different from the default
+Unless PGWUI_Server is used, PGWUI_Common (and PGWUI_Core) expect all
+required settings exist, as well as those which have defaults
+established by PGWUI_Server.
Common Template Variables
The `@base_view` decorator, and it's decendents, makes the following variables
available in templates:
- pgwui:: A dict, containing the following keys:
+ pgwui
+ A dict, containing the following keys:
- routes:: A dict, keyed by PGWUI component name. Each value is the
- URL used to reach the component. There are the following special
- component names:
+ urls
+ A dict, keyed by PGWUI component name. Each value is the
+ URL used to reach the component. There are the following special
+ component names:
- pgwui_home:: The URL to the pgwui.home_page setting. This key
- is always available.
+ pgwui_home
+ The URL to the pgwui.home_page setting. This key
+ is always available.
- pgwui_menu:: The URL to the menu of PGWUI components. This
- obtains its value from the ``pgwui.menu_page`` configuration
- setting, if present. Otherwise it is the URL to the PGWUI_Menu
- component, if the component is present. Otherwise the key
- does not exist.
+ pgwui_menu
+ The URL to the menu of PGWUI components. This
+ obtains its value from the ``pgwui.menu_page`` configuration
+ setting, if present. Otherwise it is the URL to the PGWUI_Menu
+ component, if the component is present. Otherwise the key
+ does not exist.
Configuration By Python Code
-----------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you are writing a complete `Pyramid`_ application, or simply want full
control over configuration you will need to make a Python
.. _PostgreSQL: https://www.postgresql.org/
.. _Pyramid: https://trypyramid.com/
+.. _Pyramid's: `Pyramid`_
'pyramid',
'pyramid_beaker',
'pyramid_mako',
+ 'attrs',
],
# List additional groups of dependencies here (e.g. development
for setting in required_settings:
if setting not in conf:
errors.append(exceptions.MissingSettingError(
- '{}.{}'.format(component, setting)))
+ '{}:{}'.format(component, setting)))
return errors
for setting in conf:
if setting not in settings:
errors.append(exceptions.UnknownSettingKeyError(
- '{}.{}'.format(component, setting)))
+ '{}:{}'.format(component, setting)))
return errors
if (val is not True
and val is not False):
errors.append(exceptions.NotBooleanSettingError(
- '{}.{}'.format(component, setting), conf[setting]))
+ '{}:{}'.format(component, setting), conf[setting]))
return errors
super().__init__('Missing PGWUI setting: {}'.format(key))
+class BadPageTypeError(Error):
+ def __init__(self, key, val):
+ super().__init__(f'Bad {key} setting ({val})')
+
+
+class BadPageSourceError(Error):
+ def __init__(self, msg):
+ super().__init__(msg)
+
+
+class BadURLSourceError(BadPageSourceError):
+ def __init__(self, key, val):
+ super().__init__(
+ f'Bad {key} setting for the "URL" type, ({val}) '
+ 'does not look like an URL')
+
+
+class BadFileSourceError(BadPageSourceError):
+ def __init__(self, key, val):
+ super().__init__(
+ f'Bad {key} setting for a "file" type, ({val}) '
+ 'does not look like a file system path beginning with "/"')
+
+
+class BadFileURLPathError(BadPageSourceError):
+ def __init__(self, key, val):
+ super().__init__(
+ f'Bad {key} setting for a "file" type, ({val}) '
+ 'does not look like a "path" component of an URL '
+ 'that begins with "/"')
+
+
+class BadRouteSourceError(BadPageSourceError):
+ def __init__(self, key, val):
+ super().__init__(
+ f'Bad {key} setting for a "route" type, ({val}) '
+ 'does not look like a Pyramid route name')
+
+
+class BadAssetSourceError(BadPageSourceError):
+ def __init__(self, key, val):
+ super().__init__(
+ f'Bad {key} setting for an "asset" type, ({val}) '
+ 'does not look like a Pyramid asset specification')
+
+
class NotBooleanSettingError(Error):
def __init__(self, key, value):
super().__init__(
'The "{}" PGWUI setting must be "True" or "False"'
.format(key))
+
+
+class ViewError(Error):
+ pass
+
+
+class BadPageError(ViewError):
+ def __init__(self, page, ex, msg):
+ self.page = page
+ self.ex = ex
+ super().__init__(msg)
+
+
+class BadPageFileNotFoundError(BadPageError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ f'a file ({ex.filename}) that does not exist')
+
+
+class BadPageFilePermissionError(BadPageError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ f'a file ({ex.filename}) which cannot be read due to file '
+ 'system permissions')
+
+
+class BadPageIsADirectoryError(BadPageError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ f'a directory ({ex.filename}), not a file')
+
+
+class BadRouteError(BadPageError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ 'a route that does not exist')
+
+
+class BadAssetError(BadPageError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ 'an asset that does not exist')
# Karl O. Pinc <kop@karlpinc.com>
-'''Provide a way to configure PGWUI.
+'''Configure supporting modules and other common elements
+View decorators to expose useful variables to templates
'''
+import pgwui_common.views.page_views
from .plugin import find_pgwui_components
+from . import exceptions as ex
-DEFAULT_HOME_ROUTE = '/'
+def route_path(request, page_name, source):
+ '''Return the route path of the page's "source"
+ '''
+ try:
+ return request.route_path(source)
+ except KeyError as old_ex:
+ raise ex.BadRouteError(page_name, old_ex)
-def set_menu_route(request, routes):
- '''Add routes for pgwui_menu, return non-menu components
+def asset_path(request, page_name, source):
+ '''Return the static path to the asset's "source"
+ '''
+ try:
+ return request.static_path(source)
+ except ValueError as old_ex:
+ raise ex.BadAssetError(page_name, old_ex)
+
+
+def url_of_page(request, page_name):
+ '''Return a url to the page. This may or may not be fully
+ qualified, depending on what the user specifies in the settings.
+ '''
+ page_conf = request.registry.settings['pgwui'][page_name]
+ type = page_conf['type']
+ source = page_conf['source']
+ if type == 'URL':
+ return source
+ if type == 'file':
+ return request.route_path(f'pgwui_common.{page_name}')
+ if type == 'route':
+ return route_path(request, page_name, source)
+ if type == 'asset':
+ return asset_path(request, page_name, source)
+
+
+def set_menu_url(request, urls):
+ '''Add urls for pgwui_menu, return non-menu components
'''
try:
- menu_url = request.route_url('pgwui_menu')
+ menu_url = url_of_page(request, 'menu_page')
except KeyError:
- pass
- else:
- if menu_url != request.route_url('pgwui_home'):
- routes['pgwui_menu'] = request.route_path('pgwui_menu')
+ try:
+ menu_url = request.route_path('pgwui_menu')
+ except KeyError:
+ return
+ if menu_url != urls['pgwui_home']:
+ urls['pgwui_menu'] = menu_url
-def set_component_routes(request, routes):
- '''Add routes for each pgwui component to the 'routes' dict
+def set_component_urls(request, urls):
+ '''Add urls for each pgwui component to the 'urls' dict
'''
- set_menu_route(request, routes)
+ set_menu_url(request, urls)
components = find_pgwui_components()
if 'pgwui_menu' in components:
components.remove('pgwui_menu')
for component in components:
try:
- route = request.route_path(component)
+ url = request.route_path(component)
except KeyError:
pass # In case a component has no route
else:
- routes.setdefault(component, route)
+ urls.setdefault(component, url)
-def set_routes(request, routes):
- '''Build 'routes' dict with all the routes
+def set_urls(request, urls):
+ '''Build 'urls' dict with all the urls
'''
- home_route = request.route_path('pgwui_home')
- routes.setdefault('pgwui_home', home_route)
- set_component_routes(request, routes)
+ home_url = url_of_page(request, 'home_page')
+ urls.setdefault('pgwui_home', home_url)
+ set_component_urls(request, urls)
def base_view(wrapped):
'''
response = wrapped(request)
pgwui = response.get('pgwui', {})
- routes = pgwui.setdefault('routes', dict())
- set_routes(request, routes)
+ urls = pgwui.setdefault('urls', dict())
+ set_urls(request, urls)
response['pgwui'] = pgwui
return response
return wrapper
return wrapper
+def configure_page(config, pgwui_settings, page_name):
+ '''Setup route and view for a file given in pgwui."page_name" setting,
+ which is the name of the new route.
+
+ Only files need anything done. URLs are used as written into the
+ config, and routes and assets already exist.
+ '''
+ if page_name in pgwui_settings:
+ page_settings = pgwui_settings[page_name]
+ type = page_settings['type']
+ if type == 'file':
+ route_name = f'pgwui_common.{page_name}'
+ with config.route_prefix_context(None):
+ config.add_route(route_name, page_settings['url_path'])
+ config.add_view(pgwui_common.views.page_views.PageViewer,
+ attr=page_name, route_name=route_name)
+
+
+def configure_pages(config):
+ '''Setup routes and views for "pgwui.xxxx_page" settings
+ '''
+ pgwui_settings = config.get_settings()['pgwui']
+ configure_page(config, pgwui_settings, 'home_page')
+ configure_page(config, pgwui_settings, 'menu_page')
+
+
def includeme(config):
'''Pyramid configuration for PGWUI_Common
'''
'static/pgwui_common',
'pgwui_common:static/',
cache_max_age=3600)
- config.add_route('pgwui_home', DEFAULT_HOME_ROUTE)
+ configure_pages(config)
+ config.scan()
<%def name="navbar_content()">
${parent.navbar_content()}
- % if 'pgwui_logout' in pgwui['routes']:
- | <a href="${pgwui['routes']['pgwui_logout']}">Logout</a>
+ % if 'pgwui_logout' in pgwui['urls']:
+ | <a href="${pgwui['urls']['pgwui_logout']}">Logout</a>
% endif
</%def>
This template uses the following variables in it's context:
pgwui Dict
- routes Dict of routes, keyed by pgwui component name
+ urls Dict of urls, keyed by pgwui component name
</%doc>
<%def name="navbar_content()">
- <a href="${pgwui['routes']['pgwui_home']}">HOME</a>
- % if 'pgwui_menu' in pgwui['routes']:
- | <a href="${pgwui['routes']['pgwui_menu']}">Menu</a>
+ <a href="${pgwui['urls']['pgwui_home']}">HOME</a>
+ % if 'pgwui_menu' in pgwui['urls']:
+ | <a href="${pgwui['urls']['pgwui_menu']}">Menu</a>
% endif
</%def>
--- /dev/null
+# Copyright (C) 2020 The Meme Factory, Inc. http://www.karlpinc.com/
+
+# This file is part of PGWUI_Common.
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+# Karl O. Pinc <kop@karlpinc.com>
+
+'''Views for exceptions that are raised
+'''
+
+from pyramid.view import exception_view_config
+
+from pgwui_common import exceptions as ex
+
+NOT_FOUND = '404 Not Found'
+
+
+@exception_view_config(ex.BadPageError, renderer='string')
+def bad_config_view(ex, request):
+ request.response.status_code = 404
+ request.response.status = NOT_FOUND
+ return f'PGWUI Configuration Error:\n{ex}:\n{ex.ex}'
--- /dev/null
+# Copyright (C) 2020 The Meme Factory, Inc. http://www.karlpinc.com/
+
+# This file is part of PGWUI_Common.
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+# Karl O. Pinc <kop@karlpinc.com>
+
+'''Return a page that's a file in the file system
+'''
+
+import attr
+from pyramid.response import FileResponse
+
+from pgwui_common import exceptions as ex
+
+
+@attr.s
+class PageViewer():
+ '''A class of views that return file content
+ '''
+ request = attr.ib()
+
+ def page(self, page_name):
+ try:
+ return FileResponse(
+ self.request.registry.settings['pgwui'][page_name]['source'],
+ request=self.request,
+ content_type='text/html',
+ cache_max_age=3600)
+ except FileNotFoundError as old_ex:
+ raise ex.BadPageFileNotFoundError(page_name, old_ex)
+ except PermissionError as old_ex:
+ raise ex.BadPageFilePermissionError(page_name, old_ex)
+ except IsADirectoryError as old_ex:
+ raise ex.BadPageIsADirectoryError(page_name, old_ex)
+
+ def menu_page(self):
+ return self.page('menu_page')
+
+ def home_page(self):
+ return self.page('home_page')
import pytest
import pyramid.config
+import pyramid.testing
from pyramid.threadlocal import get_current_request
import pgwui_common.pgwui_common as pgwui_common
+import pgwui_common.exceptions as common_ex
from pgwui_testing import testing
mock_find_pgwui_components = testing.make_mock_fixture(
pgwui_common, 'find_pgwui_components')
-mock_route_path = testing.instance_method_mock_fixture('route_path')
+mock_method_route_path = testing.instance_method_mock_fixture('route_path')
mock_route_url = testing.instance_method_mock_fixture('route_url')
+mock_include = testing.instance_method_mock_fixture('include')
+mock_add_static_view = testing.instance_method_mock_fixture('add_static_view')
+mock_add_route = testing.instance_method_mock_fixture('add_route')
+mock_add_view = testing.instance_method_mock_fixture('add_view')
+mock_static_path = testing.instance_method_mock_fixture('static_path')
def mock_view(request):
+ if (hasattr(request, 'registry')
+ and 'pgwui' in request.registry.settings):
+ return request.registry.settings
return {'pgwui': {'foo': FOO_URL}}
# Unit tests
-# set_menu_route()
+# route_path()
+@pytest.mark.unittest
+def test_route_path_with_path(pyramid_request_config, mock_method_route_path):
+ '''static_path() result is returned
+ '''
+ expected = 'route'
+
+ request = get_current_request()
+ mocked_route_path = mock_method_route_path(request)
+ mocked_route_path.return_value = expected
+
+ result = pgwui_common.route_path(request, None, None)
+
+ assert result == expected
+
+
+@pytest.mark.unittest
+def test_route_path_no_path(pyramid_request_config, mock_method_route_path):
+ '''BadRouteError() raised when there's no path
+ '''
+ request = get_current_request()
+ mocked_route_path = mock_method_route_path(request)
+ mocked_route_path.side_effect = KeyError
+
+ with pytest.raises(common_ex.BadRouteError):
+ pgwui_common.route_path(request, None, None)
+
+ assert True
+
+
+mock_route_path = testing.make_mock_fixture(
+ pgwui_common, 'route_path')
+
+
+# asset_path()
+
+@pytest.mark.unittest
+def test_asset_path_with_path(pyramid_request_config, mock_static_path):
+ '''static_path() result is returned
+ '''
+ expected = 'static'
+
+ request = get_current_request()
+ mocked_static_path = mock_static_path(request)
+ mocked_static_path.return_value = expected
+
+ result = pgwui_common.asset_path(request, None, None)
+
+ assert result == expected
+
+
+@pytest.mark.unittest
+def test_asset_path_no_path(pyramid_request_config, mock_static_path):
+ '''BadAssetError() raised when there's no path
+ '''
+ request = get_current_request()
+ mocked_static_path = mock_static_path(request)
+ mocked_static_path.side_effect = ValueError
+
+ with pytest.raises(common_ex.BadAssetError):
+ pgwui_common.asset_path(request, None, None)
+
+ assert True
+
+
+mock_asset_path = testing.make_mock_fixture(
+ pgwui_common, 'asset_path')
+
+
+# url_of_page()
+
+@pytest.mark.parametrize(
+ ('pgwui', 'page_name', 'expected'), [
+ ({'test_page': {'type': 'URL',
+ 'source': 'somesource'}},
+ 'test_page',
+ 'somesource'),
+ ({'test_page': {'type': 'file',
+ 'source': 'somesource'}},
+ 'test_page',
+ 'pgwui_common.test_page'),
+ ({'test_page': {'type': 'route',
+ 'source': 'somesource'}},
+ 'test_page',
+ 'routepath'),
+ ({'test_page': {'type': 'asset',
+ 'source': 'somesource'}},
+ 'test_page',
+ 'static'),
+ ({'test_page': {'type': 'impossible',
+ 'source': 'somesource'}},
+ 'test_page',
+ None)])
+@pytest.mark.unittest
+def test_url_of_page(
+ pyramid_request_config, mock_method_route_path,
+ mock_route_path, mock_asset_path, pgwui, page_name, expected):
+ '''The right results and calls are made
+ '''
+ mock_asset_path.return_value = 'static'
+ mock_route_path.return_value = 'routepath'
+
+ request = get_current_request()
+ mocked_route_path = mock_method_route_path(request)
+ mocked_route_path.side_effect = lambda x: x
+
+ request.registry.settings['pgwui'] = pgwui
+ result = pgwui_common.url_of_page(request, page_name)
+
+ assert result == expected
+
+
+mock_url_of_page = testing.make_mock_fixture(
+ pgwui_common, 'url_of_page')
+
+
+# set_menu_url()
@pytest.mark.unittest
@pytest.mark.parametrize(
- "test_routes,expected",
+ "test_urls,expected",
[
- # menu and home have identical routes, no route is added for menu
+ # menu and home have identical urls, no url is added for menu
({'pgwui_menu': '/', 'pgwui_home': '/'},
{}),
- # No menu route, no route is added for menu
+ # No menu url, no url is added for menu
({'pgwui_home': '/'},
{}),
- # menu and home have different urls, route is added for menu
+ # menu and home have different urls, url is added for menu
({'pgwui_menu': '/menu', 'pgwui_home': '/'},
{'pgwui_menu': '/menu'})])
-def test_set_menu_route(
- pyramid_request_config, mock_route_path, mock_route_url,
- test_routes, expected):
- '''The expected routes are returned
+def test_set_menu_url(
+ pyramid_request_config, mock_method_route_path, mock_url_of_page,
+ test_urls, expected):
+ '''The expected urls are returned
'''
def path_func(name):
- return test_routes[name]
-
- def url_func(name):
- return f'{request.application_url}{test_routes[name]}'
+ return test_urls[name]
+ mock_url_of_page.side_effect = lambda *args: test_urls['pgwui_menu']
request = get_current_request()
- mocked_route_path = mock_route_path(request)
+ mocked_route_path = mock_method_route_path(request)
mocked_route_path.side_effect = path_func
- mocked_route_url = mock_route_url(request)
- mocked_route_url.side_effect = url_func
- routes = dict()
- pgwui_common.set_menu_route(request, routes)
+ urls = {'pgwui_home': test_urls['pgwui_home']}
+ expected.update(urls)
+ pgwui_common.set_menu_url(request, urls)
- assert routes == expected
+ assert urls == expected
-mock_set_menu_route = testing.make_mock_fixture(
- pgwui_common, 'set_menu_route')
+mock_set_menu_url = testing.make_mock_fixture(
+ pgwui_common, 'set_menu_url')
-# set_component_routes()
+# set_component_urls()
+@pytest.mark.parametrize(
+ 'test_urls', [
+ # With a pgwui_menu
+ {'pgwui_menu': '/menu',
+ 'pgwui_logout': '/logout',
+ 'pgwui_foo': '/foo',
+ 'pgwui_home': '/'},
+ # Without a pgwui_menu
+ {'pgwui_logout': '/logout',
+ 'pgwui_foo': '/foo',
+ 'pgwui_home': '/'}])
@pytest.mark.unittest
-def test_set_component_routes(
- pyramid_request_config, mock_route_path, mock_set_menu_route,
- mock_find_pgwui_components):
- '''Routes are set for every component which has a route, except for
+def test_set_component_urls(
+ pyramid_request_config, mock_method_route_path, mock_set_menu_url,
+ mock_find_pgwui_components, test_urls):
+ '''Urls are set for every component which has a route, except for
pgwui_menu
'''
- test_routes = {'pgwui_menu': '/menu',
- 'pgwui_logout': '/logout',
- 'pgwui_foo': '/foo',
- 'pgwui_home': '/'}
- test_components = list(test_routes) + ['pgwui_noroute']
+ test_components = list(test_urls) + ['pgwui_noroute']
- def route_func(route):
- return test_routes[route]
+ def url_func(url):
+ return test_urls[url]
request = get_current_request()
- mocked_route_path = mock_route_path(request)
- mocked_route_path.side_effect = route_func
+ mocked_route_path = mock_method_route_path(request)
+ mocked_route_path.side_effect = url_func
mock_find_pgwui_components.return_value = test_components
- routes = dict()
- pgwui_common.set_component_routes(request, routes)
+ urls = dict()
+ pgwui_common.set_component_urls(request, urls)
- expected_routes = test_routes.copy()
- del expected_routes['pgwui_menu']
+ expected_urls = test_urls.copy()
+ if 'pgwui_menu' in expected_urls:
+ del expected_urls['pgwui_menu']
- assert routes == expected_routes
+ mock_set_menu_url.assert_called_once()
+ assert urls == expected_urls
-mock_set_component_routes = testing.make_mock_fixture(
- pgwui_common, 'set_component_routes')
+mock_set_component_urls = testing.make_mock_fixture(
+ pgwui_common, 'set_component_urls')
-# set_routes()
+# set_urls()
@pytest.mark.unittest
-def test_set_routes(
- pyramid_request_config, mock_set_component_routes, mock_route_path):
- '''The 'home' route is added and set_component_routes() called
+def test_set_urls(
+ pyramid_request_config, mock_url_of_page, mock_set_component_urls):
+ '''The 'home' url is added and set_component_urls() called
'''
+ test_home_route = '/'
request = get_current_request()
- mocked_route_path = mock_route_path(request)
- mocked_route_path.return_value = pgwui_common.DEFAULT_HOME_ROUTE
+ mock_url_of_page.return_value = test_home_route
- routes = dict()
- pgwui_common.set_routes(request, routes)
+ urls = dict()
+ pgwui_common.set_urls(request, urls)
- assert routes['pgwui_home'] == pgwui_common.DEFAULT_HOME_ROUTE
- mock_set_component_routes.assert_called_once()
+ assert urls['pgwui_home'] == test_home_route
+ mock_set_component_urls.assert_called_once()
-mock_set_routes = testing.make_mock_fixture(
- pgwui_common, 'set_routes')
+mock_set_urls = testing.make_mock_fixture(
+ pgwui_common, 'set_urls')
# base_view()
@pytest.mark.unittest
-def test_base_view_routes(pyramid_request_config, mock_set_routes):
- '''The response has the 'pgwui['routes']' dict added to it'''
+def test_base_view_urls(mock_set_urls):
+ '''The response has the 'pgwui['urls']' dict added to it'''
def mock_view(request):
return {}
- pgwui_common.includeme(pyramid_request_config)
wrapper = pgwui_common.base_view(mock_view)
response = wrapper(get_current_request())
assert 'pgwui' in response
pgwui = response['pgwui']
- assert 'routes' in pgwui
- assert isinstance(pgwui['routes'], dict)
+ assert 'urls' in pgwui
+ assert isinstance(pgwui['urls'], dict)
@pytest.mark.unittest
-def test_base_view_default(pyramid_request_config):
+def test_base_view_default(mock_set_urls):
'''The response retains the mock view's variables'''
- pgwui_common.includeme(pyramid_request_config)
wrapper = pgwui_common.base_view(mock_view)
request = get_current_request()
response = wrapper(request)
# auth_base_view()
@pytest.mark.unittest
-def test_auth_base_view(pyramid_request_config, mock_base_view):
+def test_auth_base_view(mock_base_view):
'''Wrapper calls base_view()
'''
- pgwui_common.includeme(pyramid_request_config)
-
wrapper = pgwui_common.auth_base_view(mock_view)
request = get_current_request()
wrapper(request)
mock_base_view.assert_called_once()
-# includeme()
+# configure_page()
@pytest.mark.unittest
-def test_includeme_configurecalled():
- '''Pyramid Configure() methods are called'''
- class MockConfig():
- def __init__(self):
- self.include_called = False
- self.add_static_view_called = False
- self.home_route = None
+def test_configure_page_no_page():
+ '''When there's no setting for the page, nothing is done
+ '''
+ pgwui_common.configure_page(None, {}, 'test_page')
- def include(self, *args):
- self.include_called = True
- def add_static_view(self, *args, **kwargs):
- self.add_static_view_called = True
+@pytest.mark.unittest
+def test_configure_page_not_file():
+ '''When the type of the page is not "file",nothing is done
+ '''
+ pgwui_common.configure_page(
+ None, {'test_page': {'type': 'other'}}, 'test_page')
- def add_route(self, name, route):
- if name == 'pgwui_home':
- self.home_route = route
- config = MockConfig()
- pgwui_common.includeme(config)
- assert config.include_called
- assert config.add_static_view_called
- assert config.home_route == '/'
+@pytest.mark.unittest
+def test_configure_page_file(
+ pyramid_request_config, mock_add_route, mock_add_view):
+ '''When the type of the page is "file", a route and view are added
+ '''
+ mocked_add_route = mock_add_route(pyramid_request_config)
+ mocked_add_view = mock_add_view(pyramid_request_config)
+ pgwui_common.configure_page(
+ pyramid_request_config,
+ {'test_page': {'type': 'file', 'url_path': 'somepath'}},
+ 'test_page')
+
+ mocked_add_route.assert_called_once()
+ mocked_add_view.assert_called_once()
+
+
+mock_configure_page = testing.make_mock_fixture(
+ pgwui_common, 'configure_page')
+
+
+# configure_pages()
+
+@pytest.mark.unittest
+def test_configure_pages(pyramid_request_config, mock_configure_page):
+ '''Calls configure_page() with all the pages
+ '''
+ pgwui = 'pgwui'
+ pyramid_request_config.get_settings()['pgwui'] = pgwui
+ pgwui_common.configure_pages(pyramid_request_config)
+
+ assert (set([call[0] for call in mock_configure_page.call_args_list])
+ == set([(pyramid_request_config, pgwui, 'home_page'),
+ (pyramid_request_config, pgwui, 'menu_page')]))
+
+
+mock_configure_pages = testing.make_mock_fixture(
+ pgwui_common, 'configure_pages')
+
+
+# includeme()
+
+@pytest.mark.unittest
+def test_includeme_configurecalled(
+ mock_add_static_view, mock_include, mock_configure_pages):
+ '''Pyramid Configure() methods are called'''
+ with pyramid.testing.testConfig() as config:
+ mocked_include = mock_include(config)
+ mocked_add_static_view = mock_add_static_view(config)
+ pgwui_common.includeme(config)
+ assert mocked_include.call_count == 2
+ mocked_add_static_view.assert_called_once()
+ mock_configure_pages.assert_called_once()
# Integration tests
@pytest.mark.integrationtest
def test_auth_base_view_integration(
pyramid_request_config, mock_find_pgwui_components):
- '''There are routes for every component
+ '''There are urls for every component
'''
- test_routes = {
+ test_pgwui = {'home_page': {'type': 'URL', 'source': '/'}}
+
+ test_urls = {
'pgwui_menu': '/menu',
'pgwui_logout': '/logout',
'pgwui_foo': '/foo'}
- mock_find_pgwui_components.return_value = list(test_routes)
+ mock_find_pgwui_components.return_value = list(test_urls)
+ pyramid_request_config.add_settings(pgwui=test_pgwui)
pgwui_common.includeme(pyramid_request_config)
- for name, route in test_routes.items():
- pyramid_request_config.add_route(name, route)
+ for name, url in test_urls.items():
+ pyramid_request_config.add_route(name, url)
wrapper = pgwui_common.auth_base_view(mock_view)
request = get_current_request()
result = wrapper(request)
- assert result['pgwui']['routes'] == dict(test_routes, pgwui_home='/')
+ assert result['pgwui']['urls'] == dict(test_urls, pgwui_home='/')
# includeme()
@pytest.mark.integrationtest
def test_includeme():
+ pgwui = {'home_page': {'type': 'file',
+ 'url_path': '/'}}
+
config = pyramid.config.Configurator()
+ config.registry.settings['pgwui'] = pgwui
pgwui_common.includeme(config)
--- /dev/null
+# Copyright (C) 2020 The Meme Factory, Inc. http://www.karlpinc.com/
+
+# This file is part of PGWUI_Common.
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+# Karl O. Pinc <kop@karlpinc.com>
+
+import pytest
+from pyramid.threadlocal import get_current_request
+
+import pgwui_common.views.ex_views as ex_views
+import pgwui_common.exceptions as common_ex
+
+import pgwui_testing.testing as testing
+
+# Activiate our pytest plugin
+# pytest_plugins = ("pgwui",)
+
+
+mock_exception_view_config = testing.make_mock_fixture(
+ ex_views, 'exception_view_config')
+
+
+# Unit tests
+
+# bad_config_view()
+
+@pytest.mark.unittest
+def test_bad_config_view(pyramid_request_config, mock_exception_view_config):
+ '''Modifies the request, returns an expected response
+ '''
+ request = get_current_request()
+ result = ex_views.bad_config_view(
+ common_ex.BadPageError(None, None, None), request)
+
+ assert isinstance(result, str)
+ assert request.response.status_code == 404
+ assert request.response.status == ex_views.NOT_FOUND
--- /dev/null
+# Copyright (C) 2020 The Meme Factory, Inc. http://www.karlpinc.com/
+
+# This file is part of PGWUI_Common.
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+# Karl O. Pinc <kop@karlpinc.com>
+
+import pytest
+from pyramid.threadlocal import get_current_request
+
+import pgwui_common.exceptions as common_ex
+import pgwui_common.views.page_views as page_views
+
+from pgwui_testing import testing
+
+# Activiate our pytest plugin
+# pytest_plugins = ("pgwui",)
+
+
+# Helper functions and constants
+
+mock_page = testing.instance_method_mock_fixture('page')
+mock_FileResponse = testing.make_mock_fixture(
+ page_views, 'FileResponse')
+
+
+# Unit tests
+
+# PageViewer.home_page()
+
+@pytest.mark.unittest
+def test_home_page(pyramid_request_config, mock_page):
+ '''Called with correct name
+ '''
+ request = get_current_request()
+ view = page_views.PageViewer(request)
+ mocked_page = mock_page(view)
+ view.home_page()
+ assert mocked_page.call_args[0][0] == 'home_page'
+
+
+# PageViewer.menu_page()
+
+@pytest.mark.unittest
+def test_menu_page(pyramid_request_config, mock_page):
+ '''Called with correct name
+ '''
+ request = get_current_request()
+ view = page_views.PageViewer(request)
+ mocked_page = mock_page(view)
+ view.menu_page()
+ assert mocked_page.call_args[0][0] == 'menu_page'
+
+
+# PageViewer.page()
+
+@pytest.mark.unittest
+def test_page_success(pyramid_request_config, mock_FileResponse):
+ '''FileResponse() called
+ '''
+ expected = 'some value'
+ mock_FileResponse.return_value = expected
+
+ pgwui = {'test_page': {'source': 'anything'}}
+ request = get_current_request()
+ request.registry.settings['pgwui'] = pgwui
+ view = page_views.PageViewer(request)
+ result = view.page('test_page')
+
+ mock_FileResponse.assert_called_once()
+ assert result == expected
+
+
+@pytest.mark.parametrize(
+ ('ex', 'expected'), [
+ (FileNotFoundError, common_ex.BadPageFileNotFoundError),
+ (PermissionError, common_ex.BadPageFilePermissionError),
+ (IsADirectoryError, common_ex.BadPageIsADirectoryError)])
+@pytest.mark.unittest
+def test_page_exception(pyramid_request_config, mock_FileResponse,
+ ex, expected):
+ '''The correct exception is raised
+ '''
+ mock_FileResponse.side_effect = ex
+
+ pgwui = {'test_page': {'source': 'anything'}}
+ request = get_current_request()
+ request.registry.settings['pgwui'] = pgwui
+ view = page_views.PageViewer(request)
+ with pytest.raises(expected):
+ view.page('test_page')
+
+ mock_FileResponse.assert_called_once()