-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_ref.py
More file actions
130 lines (96 loc) · 4.38 KB
/
test_ref.py
File metadata and controls
130 lines (96 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""Tests for the reconciler's ``ref`` prop support."""
from __future__ import annotations
from typing import Any, Dict, List
from pythonnative.element import Element
from pythonnative.hooks import component, use_ref
from pythonnative.reconciler import Reconciler
class MockView:
_next_id = 0
def __init__(self, type_name: str, props: Dict[str, Any]) -> None:
MockView._next_id += 1
self.id = MockView._next_id
self.type_name = type_name
self.props = dict(props)
self.children: List["MockView"] = []
class MockBackend:
def __init__(self) -> None:
self.last_create_props: Dict[str, Any] = {}
self.last_update_changes: Dict[str, Any] = {}
def create_view(self, type_name: str, props: Dict[str, Any]) -> MockView:
# Track props the backend was asked to apply on create — ref
# must NOT appear here; it's reconciler-owned.
self.last_create_props = dict(props)
return MockView(type_name, props)
def update_view(self, view: MockView, type_name: str, changed: Dict[str, Any]) -> None:
self.last_update_changes = dict(changed)
view.props.update(changed)
def add_child(self, parent: MockView, child: MockView, parent_type: str) -> None:
parent.children.append(child)
def remove_child(self, parent: MockView, child: MockView, parent_type: str) -> None:
parent.children = [c for c in parent.children if c.id != child.id]
def insert_child(self, parent: MockView, child: MockView, parent_type: str, index: int) -> None:
parent.children.insert(index, child)
def test_ref_populated_on_mount() -> None:
ref: Dict[str, Any] = {"current": None}
el = Element("Text", {"text": "hi", "ref": ref}, [])
backend = MockBackend()
Reconciler(backend).mount(el)
assert ref["current"] is not None
assert isinstance(ref["current"], MockView)
assert ref["current"].type_name == "Text"
def test_ref_not_passed_to_backend() -> None:
ref: Dict[str, Any] = {"current": None}
el = Element("Text", {"text": "hi", "ref": ref}, [])
backend = MockBackend()
Reconciler(backend).mount(el)
assert "ref" not in backend.last_create_props
def test_ref_cleared_on_unmount() -> None:
ref: Dict[str, Any] = {"current": None}
el = Element("Text", {"text": "hi", "ref": ref}, [])
backend = MockBackend()
rec = Reconciler(backend)
rec.mount(el)
assert ref["current"] is not None
# Replace with a different element type — destroys the old tree.
rec.reconcile(Element("Button", {"title": "ok"}, []))
assert ref["current"] is None
def test_ref_repointed_when_ref_dict_swapped() -> None:
"""Swapping the ref dict on update should populate the new and clear the old."""
old_ref: Dict[str, Any] = {"current": None}
new_ref: Dict[str, Any] = {"current": None}
backend = MockBackend()
rec = Reconciler(backend)
rec.mount(Element("Text", {"text": "a", "ref": old_ref}, []))
assert old_ref["current"] is not None
first_view = old_ref["current"]
rec.reconcile(Element("Text", {"text": "a", "ref": new_ref}, []))
assert old_ref["current"] is None
assert new_ref["current"] is first_view
def test_ref_ignored_when_not_a_dict() -> None:
"""Non-dict ``ref`` values are silently ignored — no crashes."""
el = Element("Text", {"text": "hi", "ref": "not-a-dict"}, [])
backend = MockBackend()
Reconciler(backend).mount(el)
def test_ref_diff_does_not_trigger_native_update() -> None:
"""Changing only the ref dict identity should NOT call update_view."""
backend = MockBackend()
rec = Reconciler(backend)
ref_a: Dict[str, Any] = {"current": None}
ref_b: Dict[str, Any] = {"current": None}
rec.mount(Element("Text", {"text": "x", "ref": ref_a}, []))
backend.last_update_changes = {}
rec.reconcile(Element("Text", {"text": "x", "ref": ref_b}, []))
assert backend.last_update_changes == {}
assert ref_a["current"] is None
assert ref_b["current"] is not None
def test_use_ref_in_component_populated_after_mount() -> None:
captured: Dict[str, Any] = {}
@component
def Comp() -> Element:
ref = use_ref(None)
captured["ref"] = ref
return Element("Text", {"text": "hi", "ref": ref}, [])
Reconciler(MockBackend()).mount(Comp())
ref = captured["ref"]
assert ref["current"] is not None
assert isinstance(ref["current"], MockView)