menu: Avoid reversing URL during Menu component construction

This allows app initialization to happen without Django being configured.

Tests:

- Update unit tests.

- Visit /app, /system and /help pages. All the icons are listed properly.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2021-11-15 12:51:12 -08:00 committed by James Valleroy
parent fa48fafa6b
commit 00e42a42b9
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
2 changed files with 23 additions and 55 deletions

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
from django.urls import reverse, reverse_lazy
from django.urls import reverse_lazy
from plinth import app
@ -8,7 +8,7 @@ from plinth import app
class Menu(app.FollowerComponent):
"""Component to manage a single menu item."""
_all_menus = {}
_all_menus = set()
def __init__(self, component_id, name=None, short_description=None,
icon=None, url_name=None, url_args=None, url_kwargs=None,
@ -57,21 +57,22 @@ class Menu(app.FollowerComponent):
self.url = url
self.order = order
self.advanced = advanced
self.items = []
# Add self to parent menu item
if parent_url_name:
parent_menu = self.get(parent_url_name)
parent_menu.items.append(self)
self.url_name = url_name
self.url_args = url_args
self.url_kwargs = url_kwargs
self.parent_url_name = parent_url_name
# Add self to global list of menu items
self._all_menus[url] = self
# Add self to global list of menu items.
self._all_menus.add(self)
@classmethod
def get(cls, urlname, url_args=None, url_kwargs=None):
"""Return a menu item with given URL name."""
url = reverse(urlname, args=url_args, kwargs=url_kwargs)
return cls._all_menus[url]
@property
def items(self):
"""Return the list of children for this menu item."""
return [
item for item in self._all_menus
if item.parent_url_name == self.url_name
]
def sorted_items(self):
"""Return menu items in sorted order according to current locale."""

View File

@ -47,20 +47,20 @@ def build_menu(size=5):
@pytest.fixture(name='empty_menus', autouse=True)
def fixture_empty_menus():
"""Remove all menu entries before starting a test."""
Menu._all_menus = {}
Menu._all_menus = set()
def test_init():
def test_init(rf):
"""Verify that main_menu and essential items are created."""
menu_module.init()
main_menu = menu_module.main_menu
assert isinstance(main_menu, Menu)
apps_menu = main_menu.get('apps')
apps_menu = main_menu.active_item(rf.get('/apps/foo/'))
assert apps_menu.icon == 'fa-download'
assert str(apps_menu.url) == '/apps/'
system_menu = main_menu.get('system')
system_menu = main_menu.active_item(rf.get('/sys/bar/'))
assert system_menu.icon == 'fa-cog'
assert str(system_menu.url) == '/sys/'
@ -95,6 +95,7 @@ def test_menu_creation_with_arguments():
expected_icon, url_name, url_kwargs=url_kwargs,
parent_url_name='index', order=expected_order, advanced=True)
assert menu.parent_url_name == 'index'
assert len(parent_menu.items) == 1
assert parent_menu.items[0] == menu
assert expected_name == menu.name
@ -103,46 +104,12 @@ def test_menu_creation_with_arguments():
assert expected_url == menu.url
assert expected_order == menu.order
assert menu.advanced
assert url_name == menu.url_name
assert menu.url_args is None
assert url_kwargs == menu.url_kwargs
assert not menu.items
def test_get():
"""Verify that a menu item can be correctly retrieved."""
expected_name = 'Name2'
expected_short_description = 'ShortDescription2'
expected_icon = 'Icon2'
expected_url = 'index'
url_name = 'index'
reversed_url = reverse(url_name)
expected_order = 2
menu = Menu('menu-test', expected_name, expected_short_description,
expected_icon, url_name, order=expected_order, advanced=True)
actual_item = menu.get(expected_url)
assert actual_item is not None
assert expected_name == actual_item.name
assert expected_short_description == actual_item.short_description
assert expected_icon == actual_item.icon
assert reversed_url == actual_item.url
assert expected_order == actual_item.order
assert actual_item.advanced
assert not actual_item.items
def test_get_with_item_not_found():
"""Verify that a KeyError is raised if a menu item is not found."""
expected_name = 'Name3'
expected_short_description = 'ShortDescription3'
expected_icon = 'Icon3'
url_name = 'index'
expected_order = 3
menu = Menu('menu-test', expected_name, expected_short_description,
expected_icon, url_name, order=expected_order)
with pytest.raises(KeyError):
menu.get('apps')
def test_sort_items():
"""Verify that menu items are sorted correctly."""
size = 1000