Mercurial > libervia-web
view tests/browser/unit/test_forums.py @ 1644:8a09eea9003f default tip
doc (user): Add doc for forum feature:
fix 463
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 06 Sep 2025 17:28:07 +0200 |
parents | 0e11dbbcefe7 |
children |
line wrap: on
line source
#!/usr/bin/env python3 # Libervia Web # Copyright (C) 2009-2025 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from collections import defaultdict import sys from libervia.backend.tools.common import data_format import pytest from unittest.mock import MagicMock, AsyncMock, patch # We mock all non-stdlib modules, so the module can be imported. sys.modules["bridge"] = MagicMock(AsyncBridge=AsyncMock) sys.modules["cache"] = MagicMock(cache=MagicMock()) sys.modules["dialog"] = MagicMock() sys.modules["editor"] = MagicMock(TagsEditor=MagicMock()) sys.modules["file_uploader"] = MagicMock(FileUploader=MagicMock()) sys.modules["javascript"] = MagicMock() sys.modules["jid"] = MagicMock(JID=MagicMock()) sys.modules["loading"] = MagicMock() sys.modules["popup"] = MagicMock(create_popup=MagicMock()) sys.modules["js_modules"] = MagicMock() sys.modules["js_modules.quill"] = MagicMock(Quill=MagicMock()) sys.modules["js_modules.quill_mention"] = MagicMock() sys.modules["template"] = MagicMock() sys.modules["identities"] = MagicMock() class FakeElement: def __init__(self): self.attrs = defaultdict(MagicMock) def __getattr__(self, attr) -> MagicMock: return self.attrs[attr] class FakeDocument: def __init__(self): self.attrs = defaultdict(MagicMock) self.items = defaultdict(FakeElement) def __getattr__(self, attr) -> MagicMock: return self.attrs[attr] def __getitem__(self, item) -> FakeElement: return self.items[item] def __setitem__(self, key, item) -> None: self.items[key] = item class FakeEvent: """Fake event object with preventDefault and stopPropagation methods.""" def __init__(self): self.defaultPrevented = False self.currentTarget = None def preventDefault(self): self.defaultPrevented = True def stopPropagation(self): pass document = FakeDocument() browser = sys.modules["browser"] = MagicMock( aio=MagicMock(), console=MagicMock(), document=document, window=MagicMock( service="test_service", node="test_node", subscribed=True, location=MagicMock(href="http://example.com/test"), URL=MagicMock( new=MagicMock( return_value=MagicMock(href="http://example.com/test?search=test") ) ), RegExp=MagicMock(), ), ) class FakeTemplate(MagicMock): def get_elt(self, *args, **kwargs): return FakeElement() sys.modules["template"] = MagicMock(Template=FakeTemplate) # We can now import the actual module from libervia.web.pages.forums.view import _browser as forums_module class TestLiberviaForumView: @pytest.fixture def forum_view(self) -> forums_module.LiberviaForumView: """Fixture to create a LiberviaForumView instance with mocks.""" # Create a mock for the Quill editor quill_mock = MagicMock() quill_mock.getSemanticHTML.return_value = "<p>test content</p>" quill_mock.getContents.return_value = {"ops": []} quill_mock.getModule.return_value = MagicMock(container=MagicMock()) # Mock the Quill constructor with patch("js_modules.quill.Quill.new", return_value=quill_mock): forum_view = forums_module.LiberviaForumView() # Mock the tag editor forum_view.tag_editor = MagicMock(current_tags=set()) return forum_view @pytest.fixture def bridge(self, monkeypatch) -> AsyncMock: bridge = AsyncMock() monkeypatch.setattr(forums_module, "bridge", bridge) return bridge @pytest.mark.asyncio async def test_toggle_subscription_subscribed(self, forum_view, bridge) -> None: """Toggle subscription calls unsubscribe when already subscribed.""" with patch.object(browser.window, "subscribed", True): button = FakeElement() document["subscribe-button"] = button await forum_view.toggle_subscription() bridge.ps_unsubscribe.assert_awaited_once_with("test_service", "test_node") @pytest.mark.asyncio async def test_toggle_subscription_not_subscribed(self, forum_view, bridge) -> None: """Toggle subscription calls subscribe when not subscribed.""" with patch.object(browser.window, "subscribed", False): button = FakeElement() document["subscribe-button"] = button await forum_view.toggle_subscription() bridge.ps_subscribe.assert_awaited_once_with("test_service", "test_node", "") @pytest.mark.asyncio async def test_send_message_with_mentions(self, forum_view, bridge) -> None: """Sending message with mentions updates ``mb_data``.""" # Mock Quill content with mentions quill_mock = MagicMock() quill_mock.getSemanticHTML.return_value = "<p>test message</p>" quill_mock.getContents.return_value = { "ops": [{"insert": {"mention": {"id": "user@example.com"}}}] } forum_view.message_editor = quill_mock forum_view.tag_editor.current_tags = set() await forum_view.send_message() bridge.mb_send.assert_awaited() mb_data = data_format.deserialise(bridge.mb_send.call_args[0][0]) assert mb_data["mentions"] == ["user@example.com"] @pytest.mark.asyncio async def test_send_message_error_handling(self, forum_view, bridge) -> None: """Error in ``bridge.mb_send`` is reported.""" bridge.mb_send.side_effect = Exception("Send failed") quill_mock = MagicMock() quill_mock.getSemanticHTML.return_value = "<p>test message</p>" quill_mock.getContents.return_value = {"ops": []} forum_view.message_editor = quill_mock forum_view.tag_editor.current_tags = set() with patch("dialog.notification.show") as mock_show: await forum_view.send_message() # Verify that error notification was shown mock_show.assert_called_with("Can't send message: Send failed.", "error") def test_on_add_tag(self, forum_view) -> None: """``tags_container`` become visible when ``on_add_tag``is called.""" tags_container = FakeElement() tags_container.classList = MagicMock() document["tags_container"] = tags_container forum_view.on_add_tag(FakeEvent()) # Verify that the class list was modified tags_container.classList.remove.assert_called_once_with("is-hidden") def test_on_add_attachment(self, forum_view) -> None: """``file_input`` click is called from ``on_add_attachment``.""" file_input = FakeElement() document["forum-file-input"] = file_input file_input.click = MagicMock() forum_view.on_add_attachment(FakeEvent()) file_input.click.assert_called_once()