FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

"""Unit tests for the typed imperative handles in ``pythonnative.handles``.""" from __future__ import annotations import asyncio from typing import Any, Dict, List, Optional, Tuple import pytest import pythonnative as pn from pythonnative.handles import HANDLE_TYPES, make_handle class _RecordingBackend: """Minimal backend: records ``command`` calls and answers from a canned table.""" def __init__(self, answers: Optional[Dict[str, Any]] = None) -> None: self.commands: List[Tuple[int, str, Dict[str, Any]]] = [] self.answers = dict(answers or {}) def command(self, tag: int, name: str, args: Optional[Dict[str, Any]] = None) -> Any: self.commands.append((tag, name, dict(args or {}))) return self.answers.get(name) def _run(coro: Any) -> Any: return asyncio.run(coro) # ====================================================================== # make_handle / ViewHandle # ====================================================================== def test_make_handle_picks_the_type_specific_class() -> None: backend = _RecordingBackend() assert type(make_handle("TextInput", 1, backend)) is pn.TextInputHandle assert type(make_handle("ScrollView", 2, backend)) is pn.ScrollViewHandle assert type(make_handle("WebView", 3, backend)) is pn.WebViewHandle assert type(make_handle("View", 4, backend)) is pn.ViewHandle assert type(make_handle("Text", 5, backend)) is pn.ViewHandle assert set(HANDLE_TYPES) == {"TextInput", "ScrollView", "WebView"} for cls in HANDLE_TYPES.values(): assert issubclass(cls, pn.ViewHandle) def test_view_handle_exposes_tag_type_and_writable_frame() -> None: handle = pn.ViewHandle(7, "View", _RecordingBackend()) assert handle.tag == 7 assert handle.type_name == "View" assert handle.frame is None handle.frame = pn.LayoutEvent(x=1, y=2, width=30, height=40) assert handle.frame.width == 30 assert "View #7" in repr(handle) def test_view_handle_command_forwards_tag_name_and_args() -> None: backend = _RecordingBackend({"custom": {"ok": True}}) handle = pn.ViewHandle(9, "Badge", backend) assert handle.command("custom", level=2, label="x") == {"ok": True} assert backend.commands == [(9, "custom", {"level": 2, "label": "x"})] # ====================================================================== # TextInputHandle # ====================================================================== def test_text_input_handle_commands() -> None: backend = _RecordingBackend({"get_value": "hello"}) handle = pn.TextInputHandle(3, "TextInput", backend) handle.focus() handle.blur() handle.clear() handle.select_all() handle.set_selection(2) handle.set_selection(1, 4) assert [(name, args) for _tag, name, args in backend.commands] == [ ("focus", {}), ("blur", {}), ("clear", {}), ("select_all", {}), ("set_selection", {"start": 2, "end": 2}), ("set_selection", {"start": 1, "end": 4}), ] assert _run(handle.get_value()) == "hello" def test_text_input_handle_get_value_defaults_to_empty_string() -> None: handle = pn.TextInputHandle(3, "TextInput", _RecordingBackend()) assert _run(handle.get_value()) == "" # ====================================================================== # ScrollViewHandle # ====================================================================== def test_scroll_view_handle_commands() -> None: backend = _RecordingBackend({"get_scroll_offset": {"x": 0, "y": 120.5}}) handle = pn.ScrollViewHandle(4, "ScrollView", backend) handle.scroll_to(y=200) handle.scroll_to(x=10, y=20, animated=False) handle.scroll_to_end() handle.flash_scroll_indicators() assert [(name, args) for _tag, name, args in backend.commands] == [ ("scroll_to_offset", {"animated": True, "y": 200.0}), ("scroll_to_offset", {"animated": False, "x": 10.0, "y": 20.0}), ("scroll_to_end", {"animated": True}), ("flash_scroll_indicators", {}), ] offset = _run(handle.get_scroll_offset()) assert offset == pn.ScrollOffset(x=0.0, y=120.5) assert isinstance(offset.y, float) def test_scroll_offset_is_frozen() -> None: offset = pn.ScrollOffset(x=1.0, y=2.0) with pytest.raises(Exception): offset.x = 5.0 # type: ignore[misc] # ====================================================================== # WebViewHandle # ====================================================================== def test_web_view_handle_commands() -> None: backend = _RecordingBackend({"can_go_back": True, "can_go_forward": False, "get_url": "https://example.com/"}) handle = pn.WebViewHandle(5, "WebView", backend) handle.reload() handle.go_back() handle.go_forward() handle.stop_loading() handle.load_url("https://example.com/next") handle.inject_javascript("document.title = 'x'") assert [(name, args) for _tag, name, args in backend.commands] == [ ("reload", {}), ("go_back", {}), ("go_forward", {}), ("stop_loading", {}), ("load_url", {"url": "https://example.com/next"}), ("inject_javascript", {"script": "document.title = 'x'"}), ] assert _run(handle.can_go_back()) is True assert _run(handle.can_go_forward()) is False assert _run(handle.get_url()) == "https://example.com/" def test_handle_commands_match_the_native_contract() -> None: """Every handle method issues a command the component's contract declares with valid arguments.""" from pythonnative.sdk.schema import COMPONENTS, ModuleSchema cases = [ ("TextInput", pn.TextInputHandle, lambda h: (h.focus(), h.set_selection(1, 2), _run(h.get_value()))), ( "ScrollView", pn.ScrollViewHandle, lambda h: (h.scroll_to(y=5), h.scroll_to_end(), h.flash_scroll_indicators()), ), ( "WebView", pn.WebViewHandle, lambda h: (h.reload(), h.load_url("https://a.b"), h.inject_javascript("1")), ), ] for type_name, cls, drive in cases: backend = _RecordingBackend() drive(cls(1, type_name, backend)) schema = COMPONENTS[type_name] contract = ModuleSchema(schema.name, schema.commands) for _tag, name, args in backend.commands: contract.validate_call(name, args) def test_web_view_eval_js_awaits_the_webviews_module() -> None: from pythonnative.native_modules.registry import register_python_module calls = [] class Page: def eval_js(self, tag: int, script: str) -> str: calls.append((tag, script)) return "42" register_python_module("WebViews", Page()) try: backend = _RecordingBackend() handle = pn.WebViewHandle(5, "WebView", backend) assert _run(handle.eval_js("6 * 7")) == "42" assert calls == [(5, "6 * 7")] assert backend.commands == [], "eval_js is a module call, not a view command" finally: from pythonnative.native_modules import fallback register_python_module("WebViews", fallback.FallbackWebViews()) def test_web_view_eval_js_answers_empty_off_device() -> None: handle = pn.WebViewHandle(5, "WebView", _RecordingBackend()) assert _run(handle.eval_js("1 + 1")) == ""

Back | FazBrowse Home | New Git URL