11import asyncio
2+ from tests .general_utils import assert_unordered_equal
3+
4+ import pytest
5+ from anyio .exceptions import ExceptionGroup
6+
7+ import idom
8+ from idom .core .layout import Layout , LayoutEvent
9+ from idom .core .render import SharedStateRenderer , AbstractRenderer
10+
11+
12+ import asyncio
13+ from asyncio .exceptions import CancelledError
214
315import pytest
416from anyio .exceptions import ExceptionGroup
1022
1123async def test_shared_state_renderer ():
1224 done = asyncio .Event ()
13- data_sent_1 = asyncio .Queue ()
14- data_sent_2 = []
25+ changes_1 = []
26+ changes_2 = []
27+ target_id = "an-event"
1528
16- async def send_1 (data ):
17- await data_sent_1 .put (data )
29+ events_to_inject = [LayoutEvent (target = target_id , data = [])] * 4
1830
19- async def recv_1 ( ):
20- sent = await data_sent_1 . get ( )
31+ async def send_1 ( patch ):
32+ changes_1 . append ( patch . changes )
2133
22- element_id = sent ["root" ]
23- element_data = sent ["new" ][element_id ]
24- if element_data ["attributes" ]["count" ] == 4 :
34+ async def recv_1 ():
35+ await asyncio .sleep (0 )
36+ try :
37+ return events_to_inject .pop (0 )
38+ except IndexError :
2539 done .set ()
26- raise asyncio .CancelledError ()
27-
28- return LayoutEvent (target = "an-event" , data = [])
40+ raise CancelledError ()
2941
30- async def send_2 (data ):
31- element_id = data ["root" ]
32- element_data = data ["new" ][element_id ]
33- data_sent_2 .append (element_data ["attributes" ]["count" ])
42+ async def send_2 (patch ):
43+ changes_2 .append (patch .changes )
3444
3545 async def recv_2 ():
3646 await done .wait ()
37- raise asyncio . CancelledError ()
47+ raise CancelledError ()
3848
3949 @idom .element
40- async def Clickable (count = 0 ):
41- count , set_count = idom .hooks .use_state (count )
50+ async def Clickable ():
51+ count , set_count = idom .hooks .use_state (0 )
4252
43- @idom .event (target_id = "an-event" )
53+ @idom .event (target_id = target_id )
4454 async def an_event ():
4555 set_count (count + 1 )
4656
@@ -50,10 +60,34 @@ async def an_event():
5060 await renderer .run (send_1 , recv_1 , "1" )
5161 await renderer .run (send_2 , recv_2 , "2" )
5262
53- assert data_sent_2 == [0 , 1 , 2 , 3 , 4 ]
54-
55-
56- async def test_renderer_run_does_not_supress_non_stop_rendering_errors ():
63+ expected_changes = [
64+ [
65+ {
66+ "op" : "add" ,
67+ "path" : "/eventHandlers" ,
68+ "value" : {
69+ "anEvent" : {
70+ "target" : "an-event" ,
71+ "preventDefault" : False ,
72+ "stopPropagation" : False ,
73+ }
74+ },
75+ },
76+ {"op" : "add" , "path" : "/attributes" , "value" : {"count" : 0 }},
77+ {"op" : "add" , "path" : "/tagName" , "value" : "div" },
78+ ],
79+ [{"op" : "replace" , "path" : "/attributes/count" , "value" : 1 }],
80+ [{"op" : "replace" , "path" : "/attributes/count" , "value" : 2 }],
81+ [{"op" : "replace" , "path" : "/attributes/count" , "value" : 3 }],
82+ ]
83+
84+ for c_2 , expected_c in zip (changes_2 , expected_changes ):
85+ assert_unordered_equal (c_2 , expected_c )
86+
87+ assert changes_1 == changes_2
88+
89+
90+ async def test_renderer_run_does_not_supress_non_cancel_errors ():
5791 class RendererWithBug (AbstractRenderer ):
5892 async def _outgoing (self , layout , context ):
5993 raise ValueError ("this is a bug" )
@@ -76,40 +110,24 @@ async def recv():
76110 await renderer .run (send , recv , None )
77111
78112
79- async def test_shared_state_renderer_deletes_old_elements ():
80- sent = []
81- target_id = "some-id"
82-
83- async def send (data ):
84- if len (sent ) == 2 :
85- raise asyncio .CancelledError ()
86- sent .append (data )
87-
88- async def recv ():
89- # If we don't sleep here recv callback will clog the event loop.
90- # In practice this isn't a problem because you'll usually be awaiting
91- # something here that would take the place of sleep()
92- await asyncio .sleep (0 )
93- return LayoutEvent (target_id , [])
94-
95- @idom .element
96- async def Outer ():
97- hook = idom .hooks .current_hook ()
98-
99- @idom .event (target_id = target_id )
100- async def an_event ():
101- hook .schedule_render ()
113+ async def test_renderer_run_does_not_supress_non_stop_rendering_errors ():
114+ class RendererWithBug (AbstractRenderer ):
115+ async def _outgoing (self , layout , context ):
116+ raise ValueError ("this is a bug" )
102117
103- return idom .html .div ({"onEvent" : an_event }, Inner ())
118+ async def _incoming (self , layout , context , message ):
119+ raise ValueError ("this is a bug" )
104120
105121 @idom .element
106- async def Inner ():
122+ async def AnyElement ():
107123 return idom .html .div ()
108124
109- layout = Layout (Outer ())
110- async with SharedStateRenderer (layout ) as renderer :
111- await renderer .run (send , recv , "1" )
125+ async def send (data ):
126+ pass
127+
128+ async def recv ():
129+ return {}
112130
113- root = sent [ 0 ][ "new" ][ layout . root ]
114- first_inner_id = root [ "children" ][ 0 ][ "data" ]
115- assert sent [ 1 ][ "old" ] == [ first_inner_id ]
131+ with pytest . raises ( ExceptionGroup , match = "this is a bug" ):
132+ async with RendererWithBug ( idom . Layout ( AnyElement ())) as renderer :
133+ await renderer . run ( send , recv , None )
0 commit comments