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

GitHub Viewer

"""Unit tests for StyleSheet, resolve_style, theming, and typed Style.""" import dataclasses from typing import Any, List import pytest from pythonnative import diagnostics from pythonnative.style import ( DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, Style, StyleSheet, Theme, ThemeContext, resolve_style, style, validate_style_keys, ) @pytest.fixture() def dev_mode() -> Any: """Enable dev diagnostics for one test and reset state afterwards.""" diagnostics.set_dev_mode(True) diagnostics.clear_warnings() yield None diagnostics.clear_warnings() diagnostics.set_dev_mode(False) def _warnings() -> List[str]: return diagnostics.get_warnings() def test_resolve_style_none() -> None: assert resolve_style(None) == {} def test_resolve_style_dict() -> None: result = resolve_style({"font_size": 20, "color": "#000"}) assert result == {"font_size": 20, "color": "#000"} def test_resolve_style_list() -> None: base = {"font_size": 16, "color": "#000"} override = {"color": "#FFF", "bold": True} result = resolve_style([base, override]) assert result == {"font_size": 16, "color": "#FFF", "bold": True} def test_resolve_style_list_with_none_entries() -> None: result = resolve_style([None, {"a": 1}, None, {"b": 2}]) assert result == {"a": 1, "b": 2} def test_stylesheet_create() -> None: styles = StyleSheet.create( heading={"font_size": 28, "bold": True}, body={"font_size": 16}, ) assert "heading" in styles assert styles["heading"]["font_size"] == 28 assert styles["body"]["font_size"] == 16 def test_stylesheet_compose() -> None: base = {"font_size": 16, "color": "#000"} override = {"color": "#FFF", "bold": True} merged = StyleSheet.compose(base, override) assert merged["font_size"] == 16 assert merged["color"] == "#FFF" assert merged["bold"] is True def test_stylesheet_compose_none_safe() -> None: result = StyleSheet.compose(None, {"a": 1}, None) assert result == {"a": 1} def test_stylesheet_flatten_dict() -> None: result = StyleSheet.flatten({"font_size": 20}) assert result == {"font_size": 20} def test_stylesheet_flatten_list() -> None: result = StyleSheet.flatten([{"a": 1}, {"b": 2}]) assert result == {"a": 1, "b": 2} def test_stylesheet_flatten_none() -> None: result = StyleSheet.flatten(None) assert result == {} def test_theme_context_defaults_to_follow_system_sentinel() -> None: # Without a Provider, the raw context value is the follow-system # sentinel; `use_theme` resolves it against the color scheme. from pythonnative.style import _FOLLOW_SYSTEM_THEME assert ThemeContext.current() is _FOLLOW_SYSTEM_THEME def test_light_and_dark_themes_differ() -> None: assert DEFAULT_LIGHT_THEME.background_color != DEFAULT_DARK_THEME.background_color assert DEFAULT_LIGHT_THEME.text_color != DEFAULT_DARK_THEME.text_color def test_theme_is_immutable_and_replaceable() -> None: brand = DEFAULT_LIGHT_THEME.replace(primary_color="#FF2D55", spacing=12) assert isinstance(brand, Theme) assert brand.primary_color == "#FF2D55" assert brand.spacing == 12 assert brand.text_color == DEFAULT_LIGHT_THEME.text_color assert DEFAULT_LIGHT_THEME.primary_color == "#007AFF" with pytest.raises(dataclasses.FrozenInstanceError): brand.primary_color = "#000000" # type: ignore[misc] with pytest.raises(TypeError): DEFAULT_LIGHT_THEME.replace(primary_colour="#000000") # --------------------------------------------------------------------------- # Typed Style + style() helper # --------------------------------------------------------------------------- def test_style_helper_returns_dict() -> None: s = style(font_size=18, color="#FF0000") assert s == {"font_size": 18, "color": "#FF0000"} assert isinstance(s, dict) def test_style_helper_empty() -> None: assert style() == {} def test_style_typeddict_is_runtime_dict() -> None: """Style is a TypedDict: at runtime values are plain dicts.""" title: Style = {"font_size": 24, "bold": True, "color": "#000"} assert isinstance(title, dict) # ``Style`` is total=False so any subset is valid; here we just want # to confirm the values flow through resolve_style unchanged. assert resolve_style(title) == title def test_style_helper_used_with_text_factory() -> None: from pythonnative.components import Text el = Text("Hello", style=style(font_size=18, bold=True)) assert el.props["text"] == "Hello" assert el.props["font_size"] == 18 assert el.props["bold"] is True def test_style_helper_used_with_view_factory() -> None: from pythonnative.components import View el = View(style=style(padding=16, background_color="#fff")) assert el.props["padding"] == 16 assert el.props["background_color"] == "#fff" assert el.props["flex_direction"] == "column" def test_stylesheet_compose_flattens_lists() -> None: base = style(font_size=16, color="#000") override = style(color="#FFF", bold=True) merged = StyleSheet.compose([base, override]) assert merged["font_size"] == 16 assert merged["color"] == "#FFF" assert merged["bold"] is True def test_stylesheet_compose_mixed_dict_and_list() -> None: merged = StyleSheet.compose( style(font_size=14), [None, style(color="#0A84FF")], style(bold=True), ) assert merged == {"font_size": 14, "color": "#0A84FF", "bold": True} def test_stylesheet_absolute_fill() -> None: fill = StyleSheet.absolute_fill() assert fill == {"position": "absolute", "top": 0, "right": 0, "bottom": 0, "left": 0} def test_stylesheet_absolute_fill_returns_fresh_dict() -> None: fill_a = StyleSheet.absolute_fill() fill_b = StyleSheet.absolute_fill() fill_a["top"] = 99 assert fill_b["top"] == 0 def test_resolve_style_list_with_typed_styles() -> None: """resolve_style accepts a list mixing Style TypedDict entries and ``None``.""" base: Style = {"font_size": 16} override: Style = {"color": "#FFF"} result = resolve_style([base, None, override, None]) assert result == {"font_size": 16, "color": "#FFF"} def test_resolve_style_returns_fresh_dict() -> None: """resolve_style never mutates the caller's dict.""" src = {"font_size": 16} out = resolve_style(src) out["font_size"] = 99 assert src["font_size"] == 16 # --------------------------------------------------------------------------- # validate_style_keys: new layout / text keys # --------------------------------------------------------------------------- def test_validate_accepts_new_layout_and_text_keys(dev_mode: Any) -> None: validate_style_keys( { "display": "none", "margin": "auto", "margin_left": "auto", "align_items": "baseline", "align_self": "baseline", "border_width": 2, "text_transform": "uppercase", "text_shadow_color": "#00000080", "text_shadow_offset": {"width": 0, "height": 2}, "text_shadow_radius": 4, }, owner="Text", ) assert _warnings() == [] def test_validate_accepts_tuple_text_shadow_offset(dev_mode: Any) -> None: validate_style_keys({"text_shadow_offset": (1, 2)}, owner="Text") validate_style_keys({"text_shadow_offset": [0.5, 1.5]}, owner="Text") assert _warnings() == [] def test_validate_warns_on_bad_display_value(dev_mode: Any) -> None: validate_style_keys({"display": "block"}, owner="View") assert len(_warnings()) == 1 assert "display" in _warnings()[0] assert "'flex'" in _warnings()[0] and "'none'" in _warnings()[0] def test_validate_warns_on_bad_text_transform_value(dev_mode: Any) -> None: validate_style_keys({"text_transform": "title"}, owner="Text") assert len(_warnings()) == 1 assert "text_transform" in _warnings()[0] @pytest.mark.parametrize("value", ["none", "uppercase", "lowercase", "capitalize"]) def test_validate_accepts_every_text_transform(dev_mode: Any, value: str) -> None: validate_style_keys({"text_transform": value}, owner="Text") assert _warnings() == [] def test_validate_warns_on_bad_text_shadow_values(dev_mode: Any) -> None: validate_style_keys({"text_shadow_offset": "2px", "text_shadow_radius": -1}, owner="Text") messages = _warnings() assert len(messages) == 2 assert any("text_shadow_offset" in m for m in messages) assert any("text_shadow_radius" in m for m in messages) def test_validate_bad_value_warns_once_per_value(dev_mode: Any) -> None: validate_style_keys({"display": "block"}, owner="View") validate_style_keys({"display": "block"}, owner="View") validate_style_keys({"display": "inline"}, owner="View") assert len(_warnings()) == 2 def test_validate_ignores_none_values(dev_mode: Any) -> None: validate_style_keys({"display": None, "text_transform": None, "text_decoration": None}, owner="Text") assert _warnings() == [] def test_validate_still_warns_on_unknown_keys(dev_mode: Any) -> None: validate_style_keys({"text_transfrom": "uppercase"}, owner="Text") assert len(_warnings()) == 1 assert "Did you mean 'text_transform'" in _warnings()[0] def test_validate_is_noop_outside_dev_mode() -> None: diagnostics.set_dev_mode(False) diagnostics.clear_warnings() validate_style_keys({"display": "block", "bogus": 1}, owner="View") assert _warnings() == []

Back | FazBrowse Home | New Git URL