2024-07-13 20:03:44 -05:00
|
|
|
# -*- coding: utf-8; -*-
|
|
|
|
|
|
|
|
from unittest import TestCase
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
|
|
|
|
from wuttaweb import menus as mod
|
2024-08-14 21:20:00 -05:00
|
|
|
from tests.util import WebTestCase
|
2024-07-13 20:03:44 -05:00
|
|
|
|
|
|
|
|
2024-08-14 21:20:00 -05:00
|
|
|
class TestMenuHandler(WebTestCase):
|
2024-07-13 20:03:44 -05:00
|
|
|
|
|
|
|
def setUp(self):
|
2024-08-14 21:20:00 -05:00
|
|
|
self.setup_web()
|
2024-07-13 20:03:44 -05:00
|
|
|
self.handler = mod.MenuHandler(self.config)
|
|
|
|
|
|
|
|
def test_make_admin_menu(self):
|
|
|
|
menus = self.handler.make_admin_menu(self.request)
|
|
|
|
self.assertIsInstance(menus, dict)
|
|
|
|
|
|
|
|
def test_make_menus(self):
|
|
|
|
menus = self.handler.make_menus(self.request)
|
|
|
|
self.assertIsInstance(menus, list)
|
|
|
|
|
|
|
|
def test_is_allowed(self):
|
2024-08-14 21:20:00 -05:00
|
|
|
model = self.app.model
|
|
|
|
auth = self.app.get_auth_handler()
|
|
|
|
|
|
|
|
# user with perms
|
|
|
|
barney = model.User(username='barney')
|
|
|
|
self.session.add(barney)
|
|
|
|
blokes = model.Role(name="Blokes")
|
|
|
|
self.session.add(blokes)
|
|
|
|
barney.roles.append(blokes)
|
|
|
|
auth.grant_permission(blokes, 'appinfo.list')
|
|
|
|
self.request.user = barney
|
|
|
|
|
|
|
|
# perm not granted to user
|
|
|
|
item = {'perm': 'appinfo.configure'}
|
|
|
|
self.assertFalse(self.handler._is_allowed(self.request, item))
|
|
|
|
|
|
|
|
# perm *is* granted to user
|
|
|
|
item = {'perm': 'appinfo.list'}
|
|
|
|
self.assertTrue(self.handler._is_allowed(self.request, item))
|
|
|
|
|
|
|
|
# perm not required
|
2024-07-13 20:03:44 -05:00
|
|
|
item = {}
|
|
|
|
self.assertTrue(self.handler._is_allowed(self.request, item))
|
|
|
|
|
|
|
|
def test_mark_allowed(self):
|
|
|
|
|
|
|
|
def make_menus():
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
mock_is_allowed = MagicMock()
|
|
|
|
with patch.object(self.handler, '_is_allowed', new=mock_is_allowed):
|
|
|
|
|
|
|
|
# all should be allowed
|
|
|
|
mock_is_allowed.return_value = True
|
|
|
|
menus = make_menus()
|
|
|
|
self.handler._mark_allowed(self.request, menus)
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertTrue(menu['allowed'])
|
|
|
|
foo, bar = menu['items']
|
|
|
|
self.assertTrue(foo['allowed'])
|
|
|
|
self.assertTrue(bar['allowed'])
|
|
|
|
|
|
|
|
# none should be allowed
|
|
|
|
mock_is_allowed.return_value = False
|
|
|
|
menus = make_menus()
|
|
|
|
self.handler._mark_allowed(self.request, menus)
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertFalse(menu['allowed'])
|
|
|
|
foo, bar = menu['items']
|
|
|
|
self.assertFalse(foo['allowed'])
|
|
|
|
self.assertFalse(bar['allowed'])
|
|
|
|
|
|
|
|
def test_mark_allowed_submenu(self):
|
|
|
|
|
|
|
|
def make_menus():
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
{
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
mock_is_allowed = MagicMock()
|
|
|
|
with patch.object(self.handler, '_is_allowed', new=mock_is_allowed):
|
|
|
|
|
|
|
|
# all should be allowed
|
|
|
|
mock_is_allowed.return_value = True
|
|
|
|
menus = make_menus()
|
|
|
|
self.handler._mark_allowed(self.request, menus)
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertTrue(menu['allowed'])
|
|
|
|
foo, submenu = menu['items']
|
|
|
|
self.assertTrue(foo['allowed'])
|
|
|
|
self.assertTrue(submenu['allowed'])
|
|
|
|
subitem = submenu['items'][0]
|
|
|
|
self.assertTrue(subitem['allowed'])
|
|
|
|
|
|
|
|
# none should be allowed
|
|
|
|
mock_is_allowed.return_value = False
|
|
|
|
menus = make_menus()
|
|
|
|
self.handler._mark_allowed(self.request, menus)
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertFalse(menu['allowed'])
|
|
|
|
foo, submenu = menu['items']
|
|
|
|
self.assertFalse(foo['allowed'])
|
|
|
|
self.assertFalse(submenu['allowed'])
|
|
|
|
subitem = submenu['items'][0]
|
|
|
|
self.assertFalse(subitem['allowed'])
|
|
|
|
|
|
|
|
def test_make_menu_key(self):
|
|
|
|
self.assertEqual(self.handler._make_menu_key('foo'), 'foo')
|
|
|
|
self.assertEqual(self.handler._make_menu_key('FooBar'), 'foobar')
|
|
|
|
self.assertEqual(self.handler._make_menu_key('Foo - $#Bar'), 'foobar')
|
|
|
|
self.assertEqual(self.handler._make_menu_key('Foo__Bar'), 'foo__bar')
|
|
|
|
|
|
|
|
def test_make_menu_entry_item(self):
|
|
|
|
item = {'title': "Foo", 'url': '#'}
|
|
|
|
entry = self.handler._make_menu_entry(self.request, item)
|
|
|
|
self.assertEqual(entry['type'], 'item')
|
|
|
|
self.assertEqual(entry['title'], "Foo")
|
|
|
|
self.assertEqual(entry['url'], '#')
|
|
|
|
self.assertTrue(entry['is_link'])
|
|
|
|
|
|
|
|
def test_make_menu_entry_item_with_no_url(self):
|
|
|
|
item = {'title': "Foo"}
|
|
|
|
entry = self.handler._make_menu_entry(self.request, item)
|
|
|
|
self.assertEqual(entry['type'], 'item')
|
|
|
|
self.assertEqual(entry['title'], "Foo")
|
|
|
|
self.assertNotIn('url', entry)
|
|
|
|
# nb. still sets is_link = True; basically it's <a> with no href
|
|
|
|
self.assertTrue(entry['is_link'])
|
|
|
|
|
|
|
|
def test_make_menu_entry_item_with_known_route(self):
|
|
|
|
item = {'title': "Foo", 'route': 'home'}
|
|
|
|
with patch.object(self.request, 'route_url', return_value='/something'):
|
|
|
|
entry = self.handler._make_menu_entry(self.request, item)
|
|
|
|
self.assertEqual(entry['type'], 'item')
|
|
|
|
self.assertEqual(entry['url'], '/something')
|
|
|
|
self.assertTrue(entry['is_link'])
|
|
|
|
|
|
|
|
def test_make_menu_entry_item_with_unknown_route(self):
|
|
|
|
item = {'title': "Foo", 'route': 'home'}
|
|
|
|
with patch.object(self.request, 'route_url', side_effect=KeyError):
|
|
|
|
entry = self.handler._make_menu_entry(self.request, item)
|
|
|
|
self.assertEqual(entry['type'], 'item')
|
|
|
|
# nb. fake url is used, based on (bad) route name
|
|
|
|
self.assertEqual(entry['url'], 'home')
|
|
|
|
self.assertTrue(entry['is_link'])
|
|
|
|
|
|
|
|
def test_make_menu_entry_sep(self):
|
|
|
|
item = {'type': 'sep'}
|
|
|
|
entry = self.handler._make_menu_entry(self.request, item)
|
|
|
|
self.assertEqual(entry['type'], 'sep')
|
|
|
|
self.assertTrue(entry['is_sep'])
|
|
|
|
self.assertFalse(entry['is_menu'])
|
|
|
|
|
2024-07-14 10:47:14 -05:00
|
|
|
def test_make_raw_menus(self):
|
|
|
|
# minimal test to ensure it calls the other method
|
|
|
|
with patch.object(self.handler, 'make_menus') as make_menus:
|
|
|
|
self.handler._make_raw_menus(self.request, foo='bar')
|
|
|
|
make_menus.assert_called_once_with(self.request, foo='bar')
|
|
|
|
|
2024-07-13 20:03:44 -05:00
|
|
|
def test_do_make_menus_prune_unallowed_item(self):
|
|
|
|
test_menus = [
|
|
|
|
{
|
|
|
|
'title': "First Menu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
def is_allowed(request, item):
|
|
|
|
if item.get('title') == 'Bar':
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
with patch.object(self.handler, 'make_menus', return_value=test_menus):
|
|
|
|
with patch.object(self.handler, '_is_allowed', side_effect=is_allowed):
|
|
|
|
menus = self.handler.do_make_menus(self.request)
|
|
|
|
|
|
|
|
# Foo remains but Bar is pruned
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertEqual(len(menu['items']), 1)
|
|
|
|
item = menu['items'][0]
|
|
|
|
self.assertEqual(item['title'], 'Foo')
|
|
|
|
|
|
|
|
def test_do_make_menus_prune_unallowed_menu(self):
|
|
|
|
test_menus = [
|
|
|
|
{
|
|
|
|
'title': "First Menu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'title': "Second Menu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Baz", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
def is_allowed(request, item):
|
|
|
|
if item.get('title') == 'Baz':
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
with patch.object(self.handler, 'make_menus', return_value=test_menus):
|
|
|
|
with patch.object(self.handler, '_is_allowed', side_effect=is_allowed):
|
|
|
|
menus = self.handler.do_make_menus(self.request)
|
|
|
|
|
|
|
|
# Second/Baz remains but First/Foo/Bar are pruned
|
|
|
|
self.assertEqual(len(menus), 1)
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertEqual(menu['title'], 'Second Menu')
|
|
|
|
self.assertEqual(len(menu['items']), 1)
|
|
|
|
item = menu['items'][0]
|
|
|
|
self.assertEqual(item['title'], 'Baz')
|
|
|
|
|
|
|
|
def test_do_make_menus_with_top_link(self):
|
|
|
|
test_menus = [
|
|
|
|
{
|
|
|
|
'title': "First Menu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'title': "Second Link",
|
|
|
|
'type': 'link',
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
with patch.object(self.handler, 'make_menus', return_value=test_menus):
|
|
|
|
with patch.object(self.handler, '_is_allowed', return_value=True):
|
|
|
|
menus = self.handler.do_make_menus(self.request)
|
|
|
|
|
|
|
|
# ensure top link remains
|
|
|
|
self.assertEqual(len(menus), 2)
|
|
|
|
menu = menus[1]
|
|
|
|
self.assertEqual(menu['title'], "Second Link")
|
|
|
|
|
|
|
|
def test_do_make_menus_with_trailing_sep(self):
|
|
|
|
test_menus = [
|
|
|
|
{
|
|
|
|
'title': "First Menu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
{'type': 'sep'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
with patch.object(self.handler, 'make_menus', return_value=test_menus):
|
|
|
|
with patch.object(self.handler, '_is_allowed', return_value=True):
|
|
|
|
menus = self.handler.do_make_menus(self.request)
|
|
|
|
|
|
|
|
# ensure trailing sep was pruned
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertEqual(len(menu['items']), 2)
|
|
|
|
foo, bar = menu['items']
|
|
|
|
self.assertEqual(foo['title'], 'Foo')
|
|
|
|
self.assertEqual(bar['title'], 'Bar')
|
|
|
|
|
|
|
|
def test_do_make_menus_with_submenu(self):
|
|
|
|
test_menus = [
|
|
|
|
{
|
|
|
|
'title': "First Menu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{
|
|
|
|
'title': "First Submenu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Foo", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'title': "Second Submenu",
|
|
|
|
'type': 'menu',
|
|
|
|
'items': [
|
|
|
|
{'title': "Bar", 'url': '#'},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
def is_allowed(request, item):
|
|
|
|
if item.get('title') == 'Bar':
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
with patch.object(self.handler, 'make_menus', return_value=test_menus):
|
|
|
|
with patch.object(self.handler, '_is_allowed', side_effect=is_allowed):
|
|
|
|
menus = self.handler.do_make_menus(self.request)
|
|
|
|
|
|
|
|
# first submenu remains, second is pruned
|
|
|
|
menu = menus[0]
|
|
|
|
self.assertEqual(len(menu['items']), 1)
|
|
|
|
submenu = menu['items'][0]
|
|
|
|
self.assertEqual(submenu['type'], 'submenu')
|
|
|
|
self.assertEqual(submenu['title'], 'First Submenu')
|
|
|
|
self.assertEqual(len(submenu['items']), 1)
|
|
|
|
item = submenu['items'][0]
|
|
|
|
self.assertEqual(item['title'], 'Foo')
|