@@ -66,3 +66,117 @@ def test_message_calls_with_arg(client):
6666 response = post_and_get_response (client , url = FAKE_CALLS_COMPONENT_URL , action_queue = action_queue )
6767
6868 assert response .get ("calls" ) == [{"args" : ["hello" ], "fn" : "testCall3" }]
69+
70+
71+ class FakeChildComponent (UnicornView ):
72+ template_name = "templates/test_component.html"
73+
74+ def child_method (self ):
75+ self .call ("createChart" , {"data" : "test" })
76+
77+
78+ class FakeParentComponent (UnicornView ):
79+ template_name = "templates/test_component.html"
80+
81+ def __init__ (self , ** kwargs ):
82+ super ().__init__ (** kwargs )
83+ # Create a child component
84+ child = FakeChildComponent (component_name = "fake-child" , id = "child-123" )
85+ child .parent = self
86+ self .children .append (child )
87+
88+ def call_child_method (self ):
89+ # Parent calls child's method
90+ for child in self .children :
91+ if hasattr (child , "child_method" ) and callable (child .child_method ):
92+ child .child_method () # type: ignore[call-top-callable]
93+
94+
95+ FAKE_PARENT_COMPONENT_URL = "/message/tests.views.message.test_calls.FakeParentComponent"
96+
97+
98+ def test_message_child_calls_from_parent (client ):
99+ """Test that child component calls are included when parent calls child method."""
100+ action_queue = [
101+ {
102+ "payload" : {"name" : "call_child_method" },
103+ "type" : "callMethod" ,
104+ "target" : None ,
105+ }
106+ ]
107+
108+ response = post_and_get_response (client , url = FAKE_PARENT_COMPONENT_URL , action_queue = action_queue )
109+
110+ # Verify child's JavaScript call is in the response
111+ calls = response .get ("calls" , [])
112+ assert any (call ["fn" ] == "createChart" for call in calls ), f"Expected 'createChart' in calls, got: { calls } "
113+ assert len (calls ) == 1
114+ assert calls [0 ]["args" ] == [{"data" : "test" }]
115+
116+
117+ class FakeGrandchildComponent (UnicornView ):
118+ template_name = "templates/test_component.html"
119+
120+ def grandchild_method (self ):
121+ self .call ("grandchildFunction" , "nested" )
122+
123+
124+ class FakeChildWithGrandchildComponent (UnicornView ):
125+ template_name = "templates/test_component.html"
126+
127+ def __init__ (self , ** kwargs ):
128+ super ().__init__ (** kwargs )
129+ # Create a grandchild component
130+ grandchild = FakeGrandchildComponent (component_name = "fake-grandchild" , id = "grandchild-456" )
131+ grandchild .parent = self
132+ self .children .append (grandchild )
133+
134+ def child_method_with_call (self ):
135+ self .call ("childFunction" )
136+ # Also call grandchild's method
137+ for child in self .children :
138+ if hasattr (child , "grandchild_method" ) and callable (child .grandchild_method ):
139+ child .grandchild_method () # type: ignore[call-top-callable]
140+
141+
142+ class FakeParentWithNestedChildren (UnicornView ):
143+ template_name = "templates/test_component.html"
144+
145+ def __init__ (self , ** kwargs ):
146+ super ().__init__ (** kwargs )
147+ # Create a child component with its own child
148+ child = FakeChildWithGrandchildComponent (component_name = "fake-child-nested" , id = "child-nested-789" )
149+ child .parent = self
150+ self .children .append (child )
151+
152+ def call_nested_children (self ):
153+ self .call ("parentFunction" )
154+ # Call child's method which will also call grandchild
155+ for child in self .children :
156+ if hasattr (child , "child_method_with_call" ) and callable (child .child_method_with_call ):
157+ child .child_method_with_call () # type: ignore[call-top-callable]
158+
159+
160+ FAKE_NESTED_COMPONENT_URL = "/message/tests.views.message.test_calls.FakeParentWithNestedChildren"
161+
162+
163+ def test_message_nested_child_calls (client ):
164+ """Test that grandchild component calls are included in deeply nested scenarios."""
165+ action_queue = [
166+ {
167+ "payload" : {"name" : "call_nested_children" },
168+ "type" : "callMethod" ,
169+ "target" : None ,
170+ }
171+ ]
172+
173+ response = post_and_get_response (client , url = FAKE_NESTED_COMPONENT_URL , action_queue = action_queue )
174+
175+ # Verify all levels of calls are collected: parent, child, and grandchild
176+ calls = response .get ("calls" , [])
177+ call_functions = [call ["fn" ] for call in calls ]
178+
179+ assert "parentFunction" in call_functions , f"Expected 'parentFunction' in calls, got: { call_functions } "
180+ assert "childFunction" in call_functions , f"Expected 'childFunction' in calls, got: { call_functions } "
181+ assert "grandchildFunction" in call_functions , f"Expected 'grandchildFunction' in calls, got: { call_functions } "
182+ assert len (calls ) == 3
0 commit comments