Update:
1. Add basic Micron parser and link support 2. Improve styling/layout 3. Add hot reloading for RNS
This commit is contained in:
@@ -62,7 +62,7 @@ def sample_page_request():
|
||||
from ren_browser.pages.page_request import PageRequest
|
||||
|
||||
return PageRequest(
|
||||
destination_hash="1234567890abcdef", page_path="/page/index.mu", field_data=None
|
||||
destination_hash="1234567890abcdef", page_path="/page/index.mu", field_data=None,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class TestAnnounce:
|
||||
def test_announce_with_none_display_name(self):
|
||||
"""Test Announce creation with None display name."""
|
||||
announce = Announce(
|
||||
destination_hash="1234567890abcdef", display_name=None, timestamp=1234567890
|
||||
destination_hash="1234567890abcdef", display_name=None, timestamp=1234567890,
|
||||
)
|
||||
|
||||
assert announce.destination_hash == "1234567890abcdef"
|
||||
|
||||
@@ -59,7 +59,7 @@ class TestLogsModule:
|
||||
assert len(logs.RET_LOGS) == 1
|
||||
assert logs.RET_LOGS[0] == "[2023-01-01T12:00:00] Test RNS message"
|
||||
logs._original_rns_log.assert_called_once_with(
|
||||
"Test RNS message", "arg1", kwarg1="value1"
|
||||
"Test RNS message", "arg1", kwarg1="value1",
|
||||
)
|
||||
assert result == "original_result"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ class TestPageRequest:
|
||||
def test_page_request_creation(self):
|
||||
"""Test basic PageRequest creation."""
|
||||
request = PageRequest(
|
||||
destination_hash="1234567890abcdef", page_path="/page/index.mu"
|
||||
destination_hash="1234567890abcdef", page_path="/page/index.mu",
|
||||
)
|
||||
|
||||
assert request.destination_hash == "1234567890abcdef"
|
||||
|
||||
@@ -63,66 +63,58 @@ class TestMicronRenderer:
|
||||
"""
|
||||
|
||||
def test_render_micron_basic(self):
|
||||
"""Test basic micron rendering (currently displays raw content)."""
|
||||
"""Test basic micron rendering."""
|
||||
content = "# Heading\n\nSome content"
|
||||
result = render_micron(content)
|
||||
|
||||
assert isinstance(result, ft.Text)
|
||||
assert result.value == "# Heading\n\nSome content"
|
||||
assert result.selectable is True
|
||||
assert result.font_family == "monospace"
|
||||
assert isinstance(result, ft.Column)
|
||||
assert result.expand is True
|
||||
assert result.scroll == ft.ScrollMode.AUTO
|
||||
|
||||
def test_render_micron_empty(self):
|
||||
"""Test micron rendering with empty content."""
|
||||
content = ""
|
||||
result = render_micron(content)
|
||||
|
||||
assert isinstance(result, ft.Text)
|
||||
assert result.value == ""
|
||||
assert result.selectable is True
|
||||
assert isinstance(result, ft.Column)
|
||||
assert len(result.controls) >= 0
|
||||
|
||||
def test_render_micron_unicode(self):
|
||||
"""Test micron rendering with Unicode characters."""
|
||||
content = "Unicode content: 你好 🌍 αβγ"
|
||||
result = render_micron(content)
|
||||
|
||||
assert isinstance(result, ft.Text)
|
||||
assert result.value == content
|
||||
assert result.selectable is True
|
||||
assert isinstance(result, ft.Column)
|
||||
assert len(result.controls) > 0
|
||||
|
||||
|
||||
class TestRendererComparison:
|
||||
"""Test cases comparing both renderers."""
|
||||
|
||||
def test_renderers_return_same_type(self):
|
||||
"""Test that both renderers return the same control type."""
|
||||
"""Test that both renderers return Flet controls."""
|
||||
content = "Test content"
|
||||
|
||||
plaintext_result = render_plaintext(content)
|
||||
micron_result = render_micron(content)
|
||||
|
||||
assert type(plaintext_result) is type(micron_result)
|
||||
assert isinstance(plaintext_result, ft.Text)
|
||||
assert isinstance(micron_result, ft.Text)
|
||||
assert isinstance(micron_result, ft.Column)
|
||||
|
||||
def test_renderers_preserve_content(self):
|
||||
"""Test that both renderers preserve the original content."""
|
||||
"""Test that plaintext renderer preserves content."""
|
||||
content = "Test content with\nmultiple lines"
|
||||
|
||||
plaintext_result = render_plaintext(content)
|
||||
micron_result = render_micron(content)
|
||||
|
||||
assert plaintext_result.value == content
|
||||
assert micron_result.value == content
|
||||
|
||||
def test_renderers_same_properties(self):
|
||||
"""Test that both renderers set the same basic properties."""
|
||||
"""Test that both renderers have expand property."""
|
||||
content = "Test content"
|
||||
|
||||
plaintext_result = render_plaintext(content)
|
||||
micron_result = render_micron(content)
|
||||
|
||||
assert plaintext_result.selectable == micron_result.selectable
|
||||
assert plaintext_result.font_family == micron_result.font_family
|
||||
assert plaintext_result.expand == micron_result.expand
|
||||
assert plaintext_result.expand is True
|
||||
assert micron_result.expand is True
|
||||
|
||||
@@ -18,7 +18,7 @@ class TestStorageManager:
|
||||
def test_storage_manager_init_without_page(self):
|
||||
"""Test StorageManager initialization without a page."""
|
||||
with patch(
|
||||
"ren_browser.storage.storage.StorageManager._get_storage_directory"
|
||||
"ren_browser.storage.storage.StorageManager._get_storage_directory",
|
||||
) as mock_get_dir:
|
||||
mock_dir = Path("/mock/storage")
|
||||
mock_get_dir.return_value = mock_dir
|
||||
@@ -35,7 +35,7 @@ class TestStorageManager:
|
||||
mock_page = Mock()
|
||||
|
||||
with patch(
|
||||
"ren_browser.storage.storage.StorageManager._get_storage_directory"
|
||||
"ren_browser.storage.storage.StorageManager._get_storage_directory",
|
||||
) as mock_get_dir:
|
||||
mock_dir = Path("/mock/storage")
|
||||
mock_get_dir.return_value = mock_dir
|
||||
@@ -51,17 +51,16 @@ class TestStorageManager:
|
||||
with (
|
||||
patch("os.name", "posix"),
|
||||
patch.dict(
|
||||
"os.environ", {"XDG_CONFIG_HOME": "/home/user/.config"}, clear=True
|
||||
"os.environ", {"XDG_CONFIG_HOME": "/home/user/.config"}, clear=True,
|
||||
),
|
||||
patch("pathlib.Path.mkdir"),
|
||||
patch("pathlib.Path.mkdir"),patch(
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
||||
),
|
||||
):
|
||||
with patch(
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory"
|
||||
):
|
||||
storage = StorageManager()
|
||||
storage._storage_dir = storage._get_storage_directory()
|
||||
expected_dir = Path("/home/user/.config") / "ren_browser"
|
||||
assert storage._storage_dir == expected_dir
|
||||
storage = StorageManager()
|
||||
storage._storage_dir = storage._get_storage_directory()
|
||||
expected_dir = Path("/home/user/.config") / "ren_browser"
|
||||
assert storage._storage_dir == expected_dir
|
||||
|
||||
def test_get_storage_directory_windows(self):
|
||||
"""Test storage directory detection for Windows."""
|
||||
@@ -76,7 +75,7 @@ class TestStorageManager:
|
||||
patch("pathlib.Path.mkdir"),
|
||||
):
|
||||
with patch(
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory"
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
||||
):
|
||||
storage = StorageManager()
|
||||
storage._storage_dir = storage._get_storage_directory()
|
||||
@@ -91,7 +90,7 @@ class TestStorageManager:
|
||||
patch("pathlib.Path.mkdir"),
|
||||
):
|
||||
with patch(
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory"
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
||||
):
|
||||
storage = StorageManager()
|
||||
storage._storage_dir = storage._get_storage_directory()
|
||||
@@ -103,15 +102,14 @@ class TestStorageManager:
|
||||
with (
|
||||
patch("os.name", "posix"),
|
||||
patch.dict("os.environ", {"ANDROID_ROOT": "/system"}, clear=True),
|
||||
patch("pathlib.Path.mkdir"),
|
||||
patch("pathlib.Path.mkdir"),patch(
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory",
|
||||
),
|
||||
):
|
||||
with patch(
|
||||
"ren_browser.storage.storage.StorageManager._ensure_storage_directory"
|
||||
):
|
||||
storage = StorageManager()
|
||||
storage._storage_dir = storage._get_storage_directory()
|
||||
expected_dir = Path("/data/local/tmp/ren_browser")
|
||||
assert storage._storage_dir == expected_dir
|
||||
storage = StorageManager()
|
||||
storage._storage_dir = storage._get_storage_directory()
|
||||
expected_dir = Path("/data/local/tmp/ren_browser")
|
||||
assert storage._storage_dir == expected_dir
|
||||
|
||||
def test_get_config_path(self):
|
||||
"""Test getting config file path."""
|
||||
@@ -171,7 +169,7 @@ class TestStorageManager:
|
||||
|
||||
assert result is True
|
||||
mock_page.client_storage.set.assert_called_with(
|
||||
"ren_browser_config", config_content
|
||||
"ren_browser_config", config_content,
|
||||
)
|
||||
|
||||
def test_save_config_fallback(self):
|
||||
@@ -188,19 +186,18 @@ class TestStorageManager:
|
||||
storage,
|
||||
"get_reticulum_config_path",
|
||||
return_value=Path(temp_dir) / "reticulum",
|
||||
), patch(
|
||||
"pathlib.Path.write_text",
|
||||
side_effect=PermissionError("Access denied"),
|
||||
):
|
||||
with patch(
|
||||
"pathlib.Path.write_text",
|
||||
side_effect=PermissionError("Access denied"),
|
||||
):
|
||||
config_content = "test config content"
|
||||
result = storage.save_config(config_content)
|
||||
config_content = "test config content"
|
||||
result = storage.save_config(config_content)
|
||||
|
||||
assert result is True
|
||||
# Check that the config was set to client storage
|
||||
mock_page.client_storage.set.assert_any_call(
|
||||
"ren_browser_config", config_content
|
||||
)
|
||||
assert result is True
|
||||
# Check that the config was set to client storage
|
||||
mock_page.client_storage.set.assert_any_call(
|
||||
"ren_browser_config", config_content,
|
||||
)
|
||||
# Verify that client storage was called at least once
|
||||
assert mock_page.client_storage.set.call_count >= 1
|
||||
|
||||
@@ -270,7 +267,7 @@ class TestStorageManager:
|
||||
bookmarks_path = storage._storage_dir / "bookmarks.json"
|
||||
assert bookmarks_path.exists()
|
||||
|
||||
with open(bookmarks_path, "r", encoding="utf-8") as f:
|
||||
with open(bookmarks_path, encoding="utf-8") as f:
|
||||
loaded_bookmarks = json.load(f)
|
||||
assert loaded_bookmarks == bookmarks
|
||||
|
||||
@@ -311,7 +308,7 @@ class TestStorageManager:
|
||||
history_path = storage._storage_dir / "history.json"
|
||||
assert history_path.exists()
|
||||
|
||||
with open(history_path, "r", encoding="utf-8") as f:
|
||||
with open(history_path, encoding="utf-8") as f:
|
||||
loaded_history = json.load(f)
|
||||
assert loaded_history == history
|
||||
|
||||
@@ -360,12 +357,11 @@ class TestStorageManager:
|
||||
with patch(
|
||||
"pathlib.Path.mkdir",
|
||||
side_effect=[PermissionError("Access denied"), None],
|
||||
):
|
||||
with patch("tempfile.gettempdir", return_value="/tmp"):
|
||||
storage = StorageManager()
|
||||
), patch("tempfile.gettempdir", return_value="/tmp"):
|
||||
storage = StorageManager()
|
||||
|
||||
expected_fallback = Path("/tmp") / "ren_browser"
|
||||
assert storage._storage_dir == expected_fallback
|
||||
expected_fallback = Path("/tmp") / "ren_browser"
|
||||
assert storage._storage_dir == expected_fallback
|
||||
|
||||
|
||||
class TestStorageGlobalFunctions:
|
||||
@@ -448,7 +444,7 @@ class TestStorageManagerEdgeCases:
|
||||
storage = StorageManager()
|
||||
|
||||
with patch(
|
||||
"pathlib.Path.write_text", side_effect=PermissionError("Access denied")
|
||||
"pathlib.Path.write_text", side_effect=PermissionError("Access denied"),
|
||||
):
|
||||
test_path = Path("/mock/path")
|
||||
result = storage._is_writable(test_path)
|
||||
|
||||
@@ -34,8 +34,8 @@ class TestTabsManager:
|
||||
assert isinstance(manager.manager, SimpleNamespace)
|
||||
assert len(manager.manager.tabs) == 1
|
||||
assert manager.manager.index == 0
|
||||
assert isinstance(manager.tab_bar, ft.Row)
|
||||
assert manager.tab_bar.scroll is None
|
||||
assert isinstance(manager.tab_bar, ft.Container)
|
||||
assert isinstance(manager.tab_bar.content, ft.Row)
|
||||
assert manager.overflow_menu is None
|
||||
assert isinstance(manager.content_container, ft.Container)
|
||||
|
||||
@@ -105,12 +105,12 @@ class TestTabsManager:
|
||||
"""Test that selecting a tab updates background colors correctly."""
|
||||
tabs_manager._add_tab_internal("Tab 2", Mock())
|
||||
|
||||
tab_controls = tabs_manager.tab_bar.controls[:-2] # Exclude add/close buttons
|
||||
tab_controls = tabs_manager.tab_bar.content.controls[:-2] # Exclude add/close buttons
|
||||
|
||||
tabs_manager.select_tab(1)
|
||||
|
||||
assert tab_controls[0].bgcolor == ft.Colors.SURFACE_CONTAINER_HIGHEST
|
||||
assert tab_controls[1].bgcolor == ft.Colors.PRIMARY_CONTAINER
|
||||
assert tab_controls[0].bgcolor == ft.Colors.GREY_800
|
||||
assert tab_controls[1].bgcolor == ft.Colors.BLUE_900
|
||||
|
||||
def test_on_tab_go_empty_url(self, tabs_manager):
|
||||
"""Test tab go with empty URL."""
|
||||
@@ -146,12 +146,12 @@ class TestTabsManager:
|
||||
def test_tab_container_properties(self, tabs_manager):
|
||||
"""Test that tab container has correct properties."""
|
||||
assert tabs_manager.content_container.expand is True
|
||||
assert tabs_manager.content_container.bgcolor == ft.Colors.BLACK
|
||||
assert tabs_manager.content_container.padding == ft.padding.all(5)
|
||||
assert tabs_manager.content_container.bgcolor in (ft.Colors.BLACK, "#000000")
|
||||
assert tabs_manager.content_container.padding == ft.padding.all(16)
|
||||
|
||||
def test_tab_bar_controls(self, tabs_manager):
|
||||
"""Test that tab bar has correct controls."""
|
||||
controls = tabs_manager.tab_bar.controls
|
||||
controls = tabs_manager.tab_bar.content.controls
|
||||
|
||||
# Should have: home tab, add button, close button (and potentially overflow menu)
|
||||
assert len(controls) >= 3
|
||||
@@ -180,7 +180,7 @@ class TestTabsManager:
|
||||
url_field = tab["url_field"]
|
||||
|
||||
assert url_field.expand is True
|
||||
assert url_field.text_style.size == 12
|
||||
assert url_field.text_style.size == 14
|
||||
assert url_field.content_padding is not None
|
||||
|
||||
def test_go_button_properties(self, tabs_manager):
|
||||
@@ -188,14 +188,14 @@ class TestTabsManager:
|
||||
tab = tabs_manager.manager.tabs[0]
|
||||
go_btn = tab["go_btn"]
|
||||
|
||||
assert go_btn.icon == ft.Icons.OPEN_IN_BROWSER
|
||||
assert go_btn.tooltip == "Load URL"
|
||||
assert go_btn.icon == ft.Icons.ARROW_FORWARD
|
||||
assert go_btn.tooltip == "Go"
|
||||
|
||||
def test_tab_click_handlers(self, tabs_manager):
|
||||
"""Test that tab click handlers are properly set."""
|
||||
tabs_manager._add_tab_internal("Tab 2", Mock())
|
||||
|
||||
tab_controls = tabs_manager.tab_bar.controls[:-2] # Exclude add/close buttons
|
||||
tab_controls = tabs_manager.tab_bar.content.controls[:-2] # Exclude add/close buttons
|
||||
|
||||
for i, control in enumerate(tab_controls):
|
||||
assert control.on_click is not None
|
||||
@@ -242,20 +242,20 @@ class TestTabsManager:
|
||||
# With page width at 800, add enough tabs that some should overflow.
|
||||
for i in range(10): # Total 11 tabs
|
||||
tabs_manager._add_tab_internal(f"Tab {i + 2}", Mock())
|
||||
|
||||
|
||||
# Check that an overflow menu exists
|
||||
assert tabs_manager.overflow_menu is not None
|
||||
|
||||
|
||||
# Simulate a smaller screen, expecting more tabs to overflow
|
||||
tabs_manager.page.width = 400
|
||||
tabs_manager._update_tab_visibility()
|
||||
visible_tabs_small = sum(1 for c in tabs_manager.tab_bar.controls if isinstance(c, ft.Container) and c.visible)
|
||||
visible_tabs_small = sum(1 for c in tabs_manager.tab_bar.content.controls if isinstance(c, ft.Container) and c.visible)
|
||||
assert visible_tabs_small < 11
|
||||
|
||||
# Simulate a larger screen, expecting all tabs to be visible
|
||||
tabs_manager.page.width = 1600
|
||||
tabs_manager._update_tab_visibility()
|
||||
visible_tabs_large = sum(1 for c in tabs_manager.tab_bar.controls if isinstance(c, ft.Container) and c.visible)
|
||||
|
||||
visible_tabs_large = sum(1 for c in tabs_manager.tab_bar.content.controls if isinstance(c, ft.Container) and c.visible)
|
||||
|
||||
assert visible_tabs_large == 11
|
||||
assert tabs_manager.overflow_menu is None
|
||||
|
||||
@@ -29,7 +29,7 @@ class TestBuildUI:
|
||||
@patch("ren_browser.tabs.tabs.TabsManager")
|
||||
@patch("ren_browser.controls.shortcuts.Shortcuts")
|
||||
def test_build_ui_appbar_setup(
|
||||
self, mock_shortcuts, mock_tabs, mock_fetcher, mock_announce_service, mock_page
|
||||
self, mock_shortcuts, mock_tabs, mock_fetcher, mock_announce_service, mock_page,
|
||||
):
|
||||
"""Test that build_ui sets up the app bar correctly."""
|
||||
mock_tab_manager = Mock()
|
||||
@@ -51,7 +51,7 @@ class TestBuildUI:
|
||||
@patch("ren_browser.tabs.tabs.TabsManager")
|
||||
@patch("ren_browser.controls.shortcuts.Shortcuts")
|
||||
def test_build_ui_drawer_setup(
|
||||
self, mock_shortcuts, mock_tabs, mock_fetcher, mock_announce_service, mock_page
|
||||
self, mock_shortcuts, mock_tabs, mock_fetcher, mock_announce_service, mock_page,
|
||||
):
|
||||
"""Test that build_ui sets up the drawer correctly."""
|
||||
mock_tab_manager = Mock()
|
||||
@@ -129,14 +129,14 @@ class TestOpenSettingsTab:
|
||||
# Get the settings content that was added
|
||||
settings_content = mock_tab_manager._add_tab_internal.call_args[0][1]
|
||||
|
||||
# Find the save button and simulate click
|
||||
# Find the save button - now nested in action_row container
|
||||
save_btn = None
|
||||
for control in settings_content.controls:
|
||||
if hasattr(control, "controls"):
|
||||
for sub_control in control.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 Config"
|
||||
and sub_control.text == "Save Configuration"
|
||||
):
|
||||
save_btn = sub_control
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user