Improve RNS management and settings interface in Ren Browser
- Introduced a new rns.py module to encapsulate Reticulum lifecycle management. - Simplified RNS initialization and error handling in app.py. - Enhanced settings.py to improve configuration management and user feedback. - Updated UI components for better interaction and status display. - Added tests for settings functionality and RNS integration.
This commit is contained in:
@@ -76,6 +76,11 @@ def mock_storage_manager():
|
||||
mock_storage.save_config.return_value = True
|
||||
mock_storage.get_config_path.return_value = Mock()
|
||||
mock_storage.get_reticulum_config_path.return_value = Mock()
|
||||
mock_storage.load_app_settings.return_value = {
|
||||
"horizontal_scroll": False,
|
||||
"page_bgcolor": "#000000",
|
||||
}
|
||||
mock_storage.save_app_settings.return_value = True
|
||||
mock_storage.get_storage_info.return_value = {
|
||||
"storage_dir": "/mock/storage",
|
||||
"config_path": "/mock/storage/config.txt",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import flet as ft
|
||||
import pytest
|
||||
|
||||
from ren_browser import app
|
||||
@@ -14,16 +15,21 @@ class TestAppIntegration:
|
||||
mock_page = Mock()
|
||||
mock_page.add = Mock()
|
||||
mock_page.update = Mock()
|
||||
mock_page.run_thread = Mock()
|
||||
mock_page.controls = Mock()
|
||||
mock_page.controls.clear = Mock()
|
||||
mock_page.width = 1024
|
||||
mock_page.window = Mock()
|
||||
mock_page.window.maximized = False
|
||||
mock_page.appbar = Mock()
|
||||
mock_page.drawer = Mock()
|
||||
mock_page.theme_mode = ft.ThemeMode.DARK
|
||||
|
||||
await app.main(mock_page)
|
||||
|
||||
# Verify that the main function sets up the loading screen
|
||||
mock_page.add.assert_called_once()
|
||||
assert mock_page.add.call_count >= 1
|
||||
loader_call = mock_page.add.call_args_list[0][0][0]
|
||||
assert isinstance(loader_call, ft.Container)
|
||||
mock_page.update.assert_called()
|
||||
mock_page.run_thread.assert_called_once()
|
||||
|
||||
def test_entry_points_exist(self):
|
||||
"""Test that all expected entry points exist and are callable."""
|
||||
|
||||
@@ -12,26 +12,34 @@ class TestApp:
|
||||
@pytest.mark.asyncio
|
||||
async def test_main_initializes_loader(self, mock_page, mock_rns):
|
||||
"""Test that main function initializes with loading screen."""
|
||||
with patch("ren_browser.ui.ui.build_ui"):
|
||||
with (
|
||||
patch("ren_browser.rns.initialize_reticulum", return_value=True),
|
||||
patch("ren_browser.rns.get_reticulum_instance"),
|
||||
patch("ren_browser.rns.get_config_path", return_value="/tmp/.reticulum"),
|
||||
patch("ren_browser.app.build_ui"),
|
||||
):
|
||||
await app.main(mock_page)
|
||||
|
||||
mock_page.add.assert_called_once()
|
||||
assert mock_page.add.call_count >= 1
|
||||
loader_call = mock_page.add.call_args_list[0][0][0]
|
||||
assert isinstance(loader_call, ft.Container)
|
||||
mock_page.update.assert_called()
|
||||
mock_page.run_thread.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_main_function_structure(self, mock_page, mock_rns):
|
||||
"""Test that main function sets up the expected structure."""
|
||||
await app.main(mock_page)
|
||||
with (
|
||||
patch("ren_browser.rns.initialize_reticulum", return_value=True),
|
||||
patch("ren_browser.rns.get_reticulum_instance"),
|
||||
patch("ren_browser.rns.get_config_path"),
|
||||
patch("ren_browser.app.build_ui"),
|
||||
):
|
||||
await app.main(mock_page)
|
||||
|
||||
# Verify that main function adds content and sets up threading
|
||||
mock_page.add.assert_called_once()
|
||||
assert mock_page.add.call_count >= 1
|
||||
loader_call = mock_page.add.call_args_list[0][0][0]
|
||||
assert isinstance(loader_call, ft.Container)
|
||||
mock_page.update.assert_called()
|
||||
mock_page.run_thread.assert_called_once()
|
||||
|
||||
# Verify that a function was passed to run_thread
|
||||
init_function = mock_page.run_thread.call_args[0][0]
|
||||
assert callable(init_function)
|
||||
|
||||
def test_run_with_default_args(self, mock_rns):
|
||||
"""Test run function with default arguments."""
|
||||
|
||||
@@ -93,28 +93,46 @@ class TestBuildUI:
|
||||
class TestOpenSettingsTab:
|
||||
"""Test cases for the open_settings_tab function."""
|
||||
|
||||
def test_open_settings_tab_basic(self, mock_page):
|
||||
def test_open_settings_tab_basic(self, mock_page, mock_storage_manager):
|
||||
"""Test opening settings tab with basic functionality."""
|
||||
mock_tab_manager = Mock()
|
||||
mock_tab_manager.manager.tabs = []
|
||||
mock_tab_manager._add_tab_internal = Mock()
|
||||
mock_tab_manager.select_tab = Mock()
|
||||
|
||||
with patch("pathlib.Path.read_text", return_value="config content"):
|
||||
mock_page.overlay = []
|
||||
|
||||
with (
|
||||
patch(
|
||||
"ren_browser.ui.settings.get_storage_manager",
|
||||
return_value=mock_storage_manager,
|
||||
),
|
||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
||||
patch("pathlib.Path.read_text", return_value="config content"),
|
||||
):
|
||||
open_settings_tab(mock_page, mock_tab_manager)
|
||||
|
||||
mock_tab_manager._add_tab_internal.assert_called_once()
|
||||
mock_tab_manager.select_tab.assert_called_once()
|
||||
mock_page.update.assert_called()
|
||||
|
||||
def test_open_settings_tab_config_error(self, mock_page):
|
||||
def test_open_settings_tab_config_error(self, mock_page, mock_storage_manager):
|
||||
"""Test opening settings tab when config file cannot be read."""
|
||||
mock_tab_manager = Mock()
|
||||
mock_tab_manager.manager.tabs = []
|
||||
mock_tab_manager._add_tab_internal = Mock()
|
||||
mock_tab_manager.select_tab = Mock()
|
||||
|
||||
with patch("pathlib.Path.read_text", side_effect=Exception("File not found")):
|
||||
mock_page.overlay = []
|
||||
|
||||
with (
|
||||
patch(
|
||||
"ren_browser.ui.settings.get_storage_manager",
|
||||
return_value=mock_storage_manager,
|
||||
),
|
||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
||||
patch("pathlib.Path.read_text", side_effect=Exception("File not found")),
|
||||
):
|
||||
open_settings_tab(mock_page, mock_tab_manager)
|
||||
|
||||
mock_tab_manager._add_tab_internal.assert_called_once()
|
||||
@@ -123,16 +141,23 @@ class TestOpenSettingsTab:
|
||||
args = mock_tab_manager._add_tab_internal.call_args
|
||||
assert args[0][0] == "Settings"
|
||||
|
||||
def test_settings_save_config_success(self, mock_page):
|
||||
def test_settings_save_config_success(self, mock_page, mock_storage_manager):
|
||||
"""Test saving config successfully in settings."""
|
||||
mock_tab_manager = Mock()
|
||||
mock_tab_manager.manager.tabs = []
|
||||
mock_tab_manager._add_tab_internal = Mock()
|
||||
mock_tab_manager.select_tab = Mock()
|
||||
|
||||
mock_page.overlay = []
|
||||
|
||||
with (
|
||||
patch(
|
||||
"ren_browser.ui.settings.get_storage_manager",
|
||||
return_value=mock_storage_manager,
|
||||
),
|
||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
||||
patch("pathlib.Path.read_text", return_value="config"),
|
||||
patch("pathlib.Path.write_text"),
|
||||
patch("pathlib.Path.write_text") as mock_write,
|
||||
):
|
||||
open_settings_tab(mock_page, mock_tab_manager)
|
||||
|
||||
@@ -152,40 +177,68 @@ class TestOpenSettingsTab:
|
||||
break
|
||||
|
||||
assert save_btn is not None
|
||||
save_btn.on_click(None)
|
||||
assert mock_write.called
|
||||
|
||||
def test_settings_save_config_error(self, mock_page, mock_storage_manager):
|
||||
"""Test saving config with error in settings."""
|
||||
"""Test saving config error path does not crash."""
|
||||
mock_tab_manager = Mock()
|
||||
mock_tab_manager.manager.tabs = []
|
||||
mock_tab_manager._add_tab_internal = Mock()
|
||||
mock_tab_manager.select_tab = Mock()
|
||||
|
||||
with patch(
|
||||
"ren_browser.ui.settings.get_storage_manager",
|
||||
return_value=mock_storage_manager,
|
||||
):
|
||||
open_settings_tab(mock_page, mock_tab_manager)
|
||||
|
||||
settings_content = mock_tab_manager._add_tab_internal.call_args[0][1]
|
||||
assert settings_content is not None
|
||||
|
||||
def test_settings_log_sections(self, mock_page, mock_storage_manager):
|
||||
"""Test that settings includes error logs and RNS logs sections."""
|
||||
mock_tab_manager = Mock()
|
||||
mock_tab_manager.manager.tabs = []
|
||||
mock_tab_manager._add_tab_internal = Mock()
|
||||
mock_tab_manager.select_tab = Mock()
|
||||
mock_page.overlay = []
|
||||
|
||||
with (
|
||||
patch(
|
||||
"ren_browser.ui.settings.get_storage_manager",
|
||||
return_value=mock_storage_manager,
|
||||
),
|
||||
patch("ren_browser.logs.ERROR_LOGS", ["Error 1", "Error 2"]),
|
||||
patch("ren_browser.logs.RET_LOGS", ["RNS log 1", "RNS log 2"]),
|
||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
||||
patch("pathlib.Path.read_text", return_value="config"),
|
||||
patch("pathlib.Path.write_text", side_effect=Exception("disk full")),
|
||||
):
|
||||
open_settings_tab(mock_page, mock_tab_manager)
|
||||
|
||||
mock_tab_manager._add_tab_internal.assert_called_once()
|
||||
args = mock_tab_manager._add_tab_internal.call_args
|
||||
assert args[0][0] == "Settings"
|
||||
settings_content = mock_tab_manager._add_tab_internal.call_args[0][1]
|
||||
save_btn = None
|
||||
for control in settings_content.controls:
|
||||
if hasattr(control, "content") and hasattr(control.content, "controls"):
|
||||
for sub_control in control.content.controls:
|
||||
if (
|
||||
hasattr(sub_control, "text")
|
||||
and sub_control.text == "Save Configuration"
|
||||
):
|
||||
save_btn = sub_control
|
||||
break
|
||||
assert save_btn is not None
|
||||
# Should not raise despite write failure
|
||||
save_btn.on_click(None)
|
||||
|
||||
def test_settings_status_section_present(self, mock_page, mock_storage_manager):
|
||||
"""Ensure the status navigation button is present."""
|
||||
mock_tab_manager = Mock()
|
||||
mock_tab_manager.manager.tabs = []
|
||||
mock_tab_manager._add_tab_internal = Mock()
|
||||
mock_tab_manager.select_tab = Mock()
|
||||
|
||||
mock_page.overlay = []
|
||||
|
||||
with (
|
||||
patch(
|
||||
"ren_browser.ui.settings.get_storage_manager",
|
||||
return_value=mock_storage_manager,
|
||||
),
|
||||
patch("ren_browser.ui.settings.rns.get_config_path", return_value="/tmp/rns"),
|
||||
patch("pathlib.Path.read_text", return_value="config"),
|
||||
):
|
||||
open_settings_tab(mock_page, mock_tab_manager)
|
||||
|
||||
settings_content = mock_tab_manager._add_tab_internal.call_args[0][1]
|
||||
nav_container = settings_content.controls[1]
|
||||
button_labels = [
|
||||
ctrl.text
|
||||
for ctrl in nav_container.content.controls
|
||||
if hasattr(ctrl, "text")
|
||||
]
|
||||
assert "Status" in button_labels
|
||||
|
||||
Reference in New Issue
Block a user