Mercurial > libervia-web
view tests/browser/unit/test_chat.py @ 1637:29dd52585984 default tip
tests (browser/unit/chat): Add tests for forwarding, rich editing and extra recipients:
fix 461
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 04 Jul 2025 18:15:48 +0200 |
parents | |
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 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(Confirm=MagicMock()) sys.modules["errors"] = MagicMock() sys.modules["file_uploader"] = MagicMock(FileUploader=MagicMock()) sys.modules["javascript"] = MagicMock() sys.modules["jid"] = MagicMock(JID=MagicMock()) sys.modules["jid_search"] = MagicMock(JidSearch=MagicMock()) sys.modules["loading"] = MagicMock() sys.modules["popup"] = MagicMock(create_popup=MagicMock()) sys.modules["js_modules"] = MagicMock() sys.modules["js_modules.emoji_picker_element"] = MagicMock() sys.modules["js_modules.tippy_js"] = MagicMock() sys.modules["js_modules.quill"] = MagicMock(Quill=MagicMock()) sys.modules["tools"] = MagicMock() sys.modules["interpreter"] = MagicMock(Inspector=MagicMock()) sys.modules["components"] = MagicMock(init_collapsible_cards=MagicMock()) class FakeElement: """FakeElement, with methods to similuate child appending and some attributes.""" scrollHeight = 0 scrollTop = 0 clientHeight = 0 def __init__(self): self.attrs = defaultdict(MagicMock) self.children = [] def __getattr__(self, attr) -> MagicMock: return self.attrs[attr] def __le__(self, child): child.parent = self self.children.append(child) return True 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 document = FakeDocument() browser = sys.modules["browser"] = MagicMock( aio=MagicMock(), console=MagicMock(), document=document, window=MagicMock(), navigator=MagicMock(mediaDevices=MagicMock(getDisplayMedia=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.chat import _browser as chat_module class TestLiberviaWebChat: @pytest.fixture def chat(self) -> chat_module.LiberviaWebChat: """Fixture to create a LiberviaWebChat instance with mocks.""" chat = chat_module.LiberviaWebChat() chat.rich_editor = MagicMock( getSelection=MagicMock(return_value=MagicMock(index=0, length=1)), getFormat=MagicMock(return_value={}), format=MagicMock(), getContents=MagicMock(return_value={"ops": []}), ) return chat @pytest.fixture def bridge(self, monkeypatch) -> AsyncMock: bridge = AsyncMock() monkeypatch.setattr(chat_module, "bridge", bridge) return bridge @pytest.fixture def message_elt(self) -> MagicMock: """Fixture to create a mock message element.""" msg_mock = MagicMock() msg_mock.__getitem__.side_effect = lambda key: "msg123" if key == "id" else None return msg_mock @pytest.mark.asyncio async def test_forward_action(self, chat, bridge, message_elt) -> None: """Forward action launches bridge.message_forward with correct parameters.""" message_elt.classList.contains.return_value = True with patch("browser.window.prompt", return_value="recipient@example.org"): await chat.on_action_forward(None, message_elt) bridge.message_forward.assert_awaited_once_with("msg123", "recipient@example.org") @pytest.mark.asyncio async def test_rich_editor_actions(self, chat) -> None: """Rich editor formatting actions modify editor state correctly.""" test_cases = [ ("bold", ["bold", True]), ("italic", ["italic", True]), ("underline", ["underline", True]), ("link", ["link", "https://example.org"]), ] for action, expected_call in test_cases: evt = MagicMock() evt.currentTarget.dataset = {"action": action} if action == "link": with patch("browser.window.prompt", return_value="https://example.org"): chat.on_rich_action(evt) else: chat.on_rich_action(evt) chat.rich_editor.format.assert_called_with(*expected_call) @pytest.mark.asyncio async def test_extra_recipients(self, monkeypatch, chat) -> None: """Adding extra recipient calls "insertBefore".""" toolbar_elt = MagicMock() monkeypatch.setitem(document, "rich-edit-toolbar", toolbar_elt) await chat.on_action_extra_recipients(selected="to") await chat.on_action_extra_recipients(selected="cc") assert toolbar_elt.parent.insertBefore.call_count == 2