Skip to content

Commit a835254

Browse files
committed
add test coverage
1 parent 36b216c commit a835254

File tree

10 files changed

+138
-60
lines changed

10 files changed

+138
-60
lines changed

examples/introduction.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@
782782
"metadata": {},
783783
"outputs": [],
784784
"source": [
785-
"display = display_shared(DragDropBoxes)"
785+
"view = display_shared(DragDropBoxes)"
786786
]
787787
},
788788
{
@@ -792,7 +792,7 @@
792792
"outputs": [],
793793
"source": [
794794
"# display 1\n",
795-
"display"
795+
"view"
796796
]
797797
},
798798
{
@@ -802,7 +802,7 @@
802802
"outputs": [],
803803
"source": [
804804
"# display 2\n",
805-
"display"
805+
"view"
806806
]
807807
},
808808
{

idom/core/element.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
Union,
1717
)
1818

19-
from .utils import bound_id
20-
2119

2220
if TYPE_CHECKING:
2321
from .layout import AbstractLayout
@@ -65,6 +63,10 @@ def element(
6563
"""
6664

6765
def setup(func: ElementRenderFunction) -> ElementConstructor:
66+
67+
if not inspect.iscoroutinefunction(func):
68+
raise TypeError(f"Expected a coroutine function, not {func}")
69+
6870
@wraps(func)
6971
def constructor(*args: Any, **kwargs: Any) -> Element:
7072
element = Element(func, state, run_in_executor)
@@ -81,19 +83,18 @@ def constructor(*args: Any, **kwargs: Any) -> Element:
8183

8284
class AbstractElement(abc.ABC):
8385

84-
__slots__ = ["_element_id", "_layout"]
86+
__slots__ = ["_layout"]
8587

8688
if not hasattr(abc.ABC, "__weakref__"):
8789
__slots__.append("__weakref__")
8890

8991
def __init__(self) -> None:
9092
self._layout: Optional["AbstractLayout"] = None
91-
self._element_id = bound_id(self)
9293

9394
@property
9495
def id(self) -> str:
9596
"""The unique ID of the element."""
96-
return self._element_id
97+
return str(id(self))
9798

9899
@abc.abstractmethod
99100
async def render(self) -> Any:

idom/core/events.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
Awaitable,
1212
Union,
1313
)
14-
15-
from .utils import bound_id
1614
from .element import AbstractElement
1715

1816

@@ -174,7 +172,7 @@ def __init__(
174172
target_id: Optional[str] = None,
175173
) -> None:
176174
self._handlers: List[EventHandlerFunction] = []
177-
self._target_id = target_id or bound_id(self)
175+
self._target_id = target_id or str(id(self))
178176
self._stop_propogation = stop_propagation
179177
self._prevent_default = prevent_default
180178

idom/core/layout.py

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
from .element import AbstractElement
2222
from .events import EventHandler
2323

24-
try:
25-
import vdom
26-
except ImportError:
27-
vdom = None
28-
2924

3025
class LayoutUpdate(NamedTuple):
3126
"""An object describing an update to a :class:`Layout`"""
@@ -61,7 +56,7 @@ class AbstractLayout(abc.ABC):
6156

6257
__slots__ = ["_loop", "_root"]
6358

64-
if not hasattr(abc.ABC, "__weakref__"):
59+
if not hasattr(abc.ABC, "__weakref__"): # pragma: no cover
6560
__slots__.append("__weakref__")
6661

6762
def __init__(
@@ -192,8 +187,6 @@ async def _render_model(
192187
to_visit.extend(value)
193188
elif isinstance(value, (Mapping, AbstractElement)):
194189
to_visit.append(value)
195-
elif vdom is not None and isinstance(node, vdom.VDOM):
196-
to_visit.append(_from_vdom(node))
197190
index += 1
198191
yield element_id, self._load_model(model, element_id)
199192

@@ -327,18 +320,3 @@ async def get(self) -> _FQT:
327320
"""Get the result of a queued awaitable that has completed."""
328321
future = await self._queue.get()
329322
return await future
330-
331-
332-
def _from_vdom(node: Any) -> Dict[str, Any]:
333-
data = {
334-
"tagName": node.tag_name,
335-
"children": node.children,
336-
"attributes": node.attributes,
337-
}
338-
if node.style:
339-
data["attributes"]["style"] = node.style
340-
if node.event_handlers:
341-
data["eventHandlers"] = node.event_handlers
342-
if node.key:
343-
data["key"] = node.key
344-
return data

idom/core/utils.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

requirements/extras.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ sanic-cors >=0.9.9, <1.0
55
# extra=matplotlib
66
matplotlib
77

8-
# extra=vdom
9-
vdom
10-
118
# extra=dialect
129
htm
1310
pyalect

setup.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ xfail_strict = True
2929
addopts = --cov=idom
3030

3131
[coverage:report]
32-
fail_under = 83
32+
fail_under = 86
3333
show_missing = True
3434
skip_covered = True
3535
sort = Miss
36+
exclude_lines =
37+
pragma: no cover
38+
\.\.\.

tests/test_idom/test_core/test_element.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
import pytest
12
import idom
23

34

5+
def test_element_function_is_coroutine():
6+
with pytest.raises(TypeError, match="Expected a coroutine function"):
7+
8+
@idom.element
9+
def non_coroutine_func(self):
10+
pass
11+
12+
413
async def test_simple_element():
514
@idom.element
615
async def simple_div(self):

tests/test_idom/test_core/test_layout.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
1+
import pytest
2+
13
import idom
4+
from idom.core.layout import RenderError, LayoutUpdate
25

36
from .utils import RenderHistory
47

58

9+
def test_layout_expects_abstract_element():
10+
with pytest.raises(TypeError, match="Expected an AbstractElement"):
11+
idom.Layout(None)
12+
with pytest.raises(TypeError, match="Expected an AbstractElement"):
13+
idom.Layout(idom.html.div())
14+
15+
16+
async def test_layout_has_event_loop(event_loop):
17+
@idom.element
18+
async def my_element(self):
19+
...
20+
21+
layout = idom.Layout(my_element())
22+
assert layout.loop is event_loop
23+
24+
625
async def test_simple_layout():
726
@idom.element
827
async def simple_element(self, tag):
@@ -65,3 +84,96 @@ async def child_element(self):
6584
history.child_2.id: {"tagName": "div"},
6685
}
6786
assert old == [history.child_1.id]
87+
88+
89+
async def test_layout_render_error_has_partial_update():
90+
history = RenderHistory()
91+
92+
@history.track("main")
93+
@idom.element
94+
async def main(self):
95+
return idom.html.div([ok_child(), bad_child()])
96+
97+
@history.track("ok_child")
98+
@idom.element
99+
async def ok_child(self):
100+
return idom.html.div(["hello"])
101+
102+
@idom.element
103+
async def bad_child(self):
104+
raise ValueError("Something went wrong :(")
105+
106+
layout = idom.Layout(main())
107+
108+
try:
109+
await layout.render()
110+
except RenderError as e:
111+
error = e
112+
113+
assert error.partial_render == LayoutUpdate(
114+
src=history.main_1.id,
115+
new={
116+
history.ok_child_1.id: {
117+
"tagName": "div",
118+
"children": [{"type": "str", "data": "hello"}],
119+
}
120+
},
121+
old=[],
122+
)
123+
124+
125+
async def test_render_raw_vdom_dict_with_single_element_object_as_children():
126+
history = RenderHistory()
127+
128+
@history.track("main")
129+
@idom.element
130+
async def main(self):
131+
return {"tagName": "div", "children": child()}
132+
133+
@history.track("child")
134+
@idom.element
135+
async def child(self):
136+
return {"tagName": "div", "children": {"tagName": "h1"}}
137+
138+
render = await idom.Layout(main()).render()
139+
140+
assert render == LayoutUpdate(
141+
src=history.main_1.id,
142+
new={
143+
history.child_1.id: {
144+
"tagName": "div",
145+
"children": [{"type": "obj", "data": {"tagName": "h1"}}],
146+
},
147+
history.main_1.id: {
148+
"tagName": "div",
149+
"children": [{"type": "ref", "data": history.child_1.id}],
150+
},
151+
},
152+
old=[],
153+
)
154+
155+
156+
async def test_element_parents_must_exist_unless_is_root():
157+
history = RenderHistory()
158+
159+
@history.track("main")
160+
@idom.element
161+
async def main(self):
162+
return child()
163+
164+
@history.track("child")
165+
@idom.element
166+
async def child(self):
167+
return idom.html.div()
168+
169+
layout = idom.Layout(main())
170+
await layout.render()
171+
172+
assert layout._element_parent(history.main_1) is None
173+
174+
@idom.element
175+
async def element_not_in_layout(self):
176+
...
177+
178+
with pytest.raises(KeyError):
179+
layout._element_parent(element_not_in_layout())

tests/test_idom/test_core/test_vdom.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_make_vdom_constructor():
7676

7777
no_children = make_vdom_constructor("no-children", allow_children=False)
7878

79-
with pytest.raises(TypeError):
80-
no_children(1, 2, 3)
79+
with pytest.raises(TypeError, match="cannot have children"):
80+
no_children([1, 2, 3])
8181

8282
assert no_children() == {"tagName": "no-children"}

0 commit comments

Comments
 (0)