Make routes for all components available to templates
authorKarl O. Pinc <kop@karlpinc.com>
Tue, 24 Nov 2020 22:07:02 +0000 (16:07 -0600)
committerKarl O. Pinc <kop@karlpinc.com>
Tue, 24 Nov 2020 22:07:02 +0000 (16:07 -0600)
README.rst
src/pgwui_common/pgwui_common.py
tests/test_pgwui_common.py

index 938b2ba3f771c471a0fcb81196e4c5da12393bcc..589842b8818000c270f29d562d5c7e6aee97f742 100644 (file)
@@ -78,6 +78,28 @@ PGWUI modules all have the following configuration settings:
    The label for PGWUI_Menu to display, when different from the default
 
 
+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:
+   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:
+
+     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.
+
+
 Configuration By Python Code
 ----------------------------
 
index 22acaf702a43c9225d705ddbaa417b7130bda9c2..cba4c906acfb158f1d5fdf3672a0f8bf19c315b3 100644 (file)
 '''Provide a way to configure PGWUI.
 '''
 
+from .plugin import find_pgwui_components
+
+
 DEFAULT_HOME_ROUTE = '/'
 
 
+def set_menu_route(request, routes):
+    '''Add routes for pgwui_menu, return non-menu components
+    '''
+    try:
+        menu_url = request.route_url('pgwui_menu')
+    except KeyError:
+        pass
+    else:
+        if menu_url != request.route_url('pgwui_home'):
+            routes['pgwui_menu'] = request.route_path('pgwui_menu')
+
+
+def set_component_routes(request, routes):
+    '''Add routes for each pgwui component to the 'routes' dict
+    '''
+    set_menu_route(request, routes)
+    components = find_pgwui_components()
+    if 'pgwui_menu' in components:
+        components.remove('pgwui_menu')
+
+    for component in components:
+        try:
+            route = request.route_path(component)
+        except KeyError:
+            pass         # In case a component has no route
+        else:
+            routes.setdefault(component, route)
+
+
+def set_routes(request, routes):
+    '''Build 'routes' dict with all the routes
+    '''
+    home_route = request.route_path('pgwui_home')
+    routes.setdefault('pgwui_home', home_route)
+    set_component_routes(request, routes)
+
+
 def base_view(wrapped):
     '''Decorator for any view which includes base.mk.
     '''
@@ -33,17 +73,8 @@ def base_view(wrapped):
         '''
         response = wrapped(request)
         pgwui = response.get('pgwui', {})
-
         routes = pgwui.setdefault('routes', dict())
-        routes.setdefault('pgwui_home',
-                          request.route_url('pgwui_home'))
-        try:
-            logout_route = request.route_url('pgwui_logout')
-        except KeyError:
-            pass         # A logout route is not required
-        else:
-            routes.setdefault('pgwui_logout', logout_route)
-
+        set_routes(request, routes)
         response['pgwui'] = pgwui
         return response
     return wrapper
index 73e7faab29dd6ec2f38524e6dd1787e3b1b8aa52..d735ee015bf22ca82c576a8f9ba4ff6a0bc704c3 100644 (file)
@@ -35,6 +35,12 @@ pytest_plugins = ("pgwui",)
 
 FOO_URL = 'foo://bar/'
 
+mock_find_pgwui_components = testing.make_mock_fixture(
+    pgwui_common, 'find_pgwui_components')
+
+mock_route_path = testing.instance_method_mock_fixture('route_path')
+mock_route_url = testing.instance_method_mock_fixture('route_url')
+
 
 def mock_view(request):
     return {'pgwui': {'foo': FOO_URL}}
@@ -46,59 +52,131 @@ def check_base_view_results(request, pgwui):
 
 # Unit tests
 
-# base_view()
-def test_base_view_add(pyramid_request_config):
-    '''The response adds all expected variables'''
-    def mock_view(request):
-        return {}
+# set_menu_route()
 
-    request = get_current_request()
-    url = (request.application_url
-           + pgwui_common.DEFAULT_HOME_ROUTE)
 
-    pgwui_common.includeme(pyramid_request_config)
-    wrapper = pgwui_common.base_view(mock_view)
-    response = wrapper(request)
-    assert response['pgwui']['routes']['pgwui_home'] == url
+@pytest.mark.unittest
+@pytest.mark.parametrize(
+    "test_routes,expected",
+    [
+        # menu and home have identical routes, no route is added for menu
+        ({'pgwui_menu': '/', 'pgwui_home': '/'},
+         {}),
+        # No menu route, no route is added for menu
+        ({'pgwui_home': '/'},
+         {}),
+        # menu and home have different urls, route 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 path_func(name):
+        return test_routes[name]
 
+    def url_func(name):
+        return f'{request.application_url}{test_routes[name]}'
 
-def test_base_view_default(pyramid_request_config):
-    '''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)
-    pgwui = response['pgwui']
-    check_base_view_results(request, pgwui)
+    mocked_route_path = mock_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)
 
-def test_base_view_logout(pyramid_request_config):
-    '''The response contains base_view variables
-    when there is a logout route
+    assert routes == expected
+
+
+mock_set_menu_route = testing.make_mock_fixture(
+    pgwui_common, 'set_menu_route')
+
+
+# set_component_routes()
+
+@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
+    pgwui_menu
     '''
-    pgwui_common.includeme(pyramid_request_config)
+    test_routes = {'pgwui_menu': '/menu',
+                   'pgwui_logout': '/logout',
+                   'pgwui_foo': '/foo',
+                   'pgwui_home': '/'}
+    test_components = list(test_routes) + ['pgwui_noroute']
+
+    def route_func(route):
+        return test_routes[route]
 
-    logout_route = '/logout'
-    pyramid_request_config.add_route('pgwui_logout', logout_route)
-    wrapper = pgwui_common.base_view(mock_view)
     request = get_current_request()
+    mocked_route_path = mock_route_path(request)
+    mocked_route_path.side_effect = route_func
+    mock_find_pgwui_components.return_value = test_components
 
-    response = wrapper(request)
-    pgwui = response['pgwui']
-    check_base_view_results(request, pgwui)
-    assert pgwui['routes']['pgwui_logout'] == (request.application_url
-                                               + logout_route)
+    routes = dict()
+    pgwui_common.set_component_routes(request, routes)
+
+    expected_routes = test_routes.copy()
+    del expected_routes['pgwui_menu']
+
+    assert routes == expected_routes
 
 
-def test_base_view_nologout(pyramid_request_config):
-    '''The response contains base_view and auth_base_view variables
-    when there is no logout route
+mock_set_component_routes = testing.make_mock_fixture(
+    pgwui_common, 'set_component_routes')
+
+
+# set_routes()
+
+@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
     '''
+    request = get_current_request()
+
+    mocked_route_path = mock_route_path(request)
+    mocked_route_path.return_value = pgwui_common.DEFAULT_HOME_ROUTE
+
+    routes = dict()
+    pgwui_common.set_routes(request, routes)
+
+    assert routes['pgwui_home'] == pgwui_common.DEFAULT_HOME_ROUTE
+    mock_set_component_routes.assert_called_once()
+
+
+mock_set_routes = testing.make_mock_fixture(
+    pgwui_common, 'set_routes')
+
+
+# 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 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)
+
+
+@pytest.mark.unittest
+def test_base_view_default(pyramid_request_config):
+    '''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)
     pgwui = response['pgwui']
     check_base_view_results(request, pgwui)
@@ -152,6 +230,34 @@ def test_includeme_configurecalled():
 
 # Integration tests
 
+# auth_base_view()
+
+@pytest.mark.integrationtest
+def test_auth_base_view_integration(
+        pyramid_request_config, mock_find_pgwui_components):
+    '''There are routes for every component
+    '''
+    test_routes = {
+        'pgwui_menu': '/menu',
+        'pgwui_logout': '/logout',
+        'pgwui_foo': '/foo'}
+
+    mock_find_pgwui_components.return_value = list(test_routes)
+
+    pgwui_common.includeme(pyramid_request_config)
+    for name, route in test_routes.items():
+        pyramid_request_config.add_route(name, route)
+
+    wrapper = pgwui_common.auth_base_view(mock_view)
+    request = get_current_request()
+    result = wrapper(request)
+
+    assert result['pgwui']['routes'] == dict(test_routes, pgwui_home='/')
+
+
+# includeme()
+
+@pytest.mark.integrationtest
 def test_includeme():
     config = pyramid.config.Configurator()
     pgwui_common.includeme(config)