77import nicegui
88import pytest
99from fastapi import FastAPI
10- from helpers import assert_contains_text
10+ from helpers import assert_contains_text , assert_not_contains_text
1111from nicegui import APIRouter , ui
1212from playwright .async_api import Page
1313from pydantic import NonNegativeInt
@@ -90,7 +90,7 @@ def rerender_on_type_change(self) -> set[str]:
9090
9191
9292class FriendComponent (BaseUpdatableComponent [Friend ]):
93- def add_to_ui (self ) -> None :
93+ def _draw_ui (self ) -> None :
9494 ui .label ().bind_text_from (
9595 self .display_model ,
9696 "name" ,
@@ -104,7 +104,7 @@ def add_to_ui(self) -> None:
104104
105105
106106class PetComponent (BaseUpdatableComponent [Pet ]):
107- def add_to_ui (self ) -> None :
107+ def _draw_ui (self ) -> None :
108108 ui .label ().bind_text_from (
109109 self .display_model ,
110110 "name" ,
@@ -118,7 +118,7 @@ def add_to_ui(self) -> None:
118118
119119
120120class PersonComponent (BaseUpdatableComponent [Person ]):
121- def add_to_ui (self ) -> None :
121+ def _draw_ui (self ) -> None :
122122 # NOTE:
123123 # There are 3 ways to bind the UI to the model changes:
124124 # 1. using nicegui builting facilties
@@ -147,18 +147,18 @@ def _person_age_ui() -> None:
147147 @ui .refreshable
148148 def _friend_or_pet_ui () -> None :
149149 if isinstance (self .display_model .companion , Friend ):
150- FriendComponent (self .display_model .companion ).add_to_ui ()
150+ FriendComponent (self .display_model .companion ).display ()
151151
152152 elif isinstance (self .display_model .companion , Pet ):
153- PetComponent (self .display_model .companion ).add_to_ui ()
153+ PetComponent (self .display_model .companion ).display ()
154154
155155 _friend_or_pet_ui ()
156156 self .display_model .on_type_change ("companion" , _friend_or_pet_ui .refresh )
157157
158158
159159def _index_page_ui (person : Person ) -> None :
160160 ui .label ("BEFORE_LABEL" )
161- PersonComponent (person ).add_to_ui ()
161+ PersonComponent (person ).display ()
162162 ui .label ("AFTER_LABEL" )
163163
164164
@@ -198,30 +198,57 @@ async def _ensure_index_page(async_page: Page, person: Person) -> None:
198198 await _ensure_after_label (async_page )
199199
200200
201+ async def _ensure_companion_not_present (async_page : Page ) -> None :
202+ await assert_not_contains_text (async_page , "Pet Name: " )
203+ await assert_not_contains_text (async_page , "Pet Species: " )
204+
205+ await assert_not_contains_text (async_page , "Friend Name: " )
206+ await assert_not_contains_text (async_page , "Friend Age: " )
207+
208+
209+ async def _ensure_person_not_present (async_page : Page ) -> None :
210+ await assert_not_contains_text (async_page , "Name: " )
211+ await assert_not_contains_text (async_page , "Age: " )
212+
213+ await _ensure_companion_not_present (async_page )
214+
215+
216+ def _get_updatable_display_model_ids (obj : BaseUpdatableDisplayModel ) -> dict [int , str ]:
217+ result : dict [int , str ] = {id (obj ): obj .__class__ .__name__ }
218+ for value in obj .__dict__ .values ():
219+ if isinstance (value , BaseUpdatableDisplayModel ):
220+ result [id (value )] = value .__class__ .__name__
221+ return result
222+
223+
201224@pytest .mark .parametrize (
202- "person, person_update, expected_callbacks_count" ,
225+ "person, person_update, expect_same_companion_object, expected_callbacks_count" ,
203226 [
204227 pytest .param (
205228 Person (name = "Alice" , age = 30 , companion = Pet (name = "Fluffy" , species = "cat" )),
206229 Person (name = "Alice" , age = 30 , companion = Pet (name = "Buddy" , species = "dog" )),
230+ True ,
207231 0 ,
208232 id = "update-pet-via-attribute-biding-no-rerender" ,
209233 ),
210234 pytest .param (
211235 Person (name = "Alice" , age = 30 , companion = Pet (name = "Fluffy" , species = "cat" )),
212236 Person (name = "Alice" , age = 30 , companion = Friend (name = "Marta" , age = 30 )),
237+ False ,
213238 1 ,
214239 id = "update-pet-ui-via-rerednder-due-to-type-change" ,
215240 ),
216241 pytest .param (
217242 Person (name = "Alice" , age = 30 , companion = Pet (name = "Fluffy" , species = "cat" )),
218243 Person (name = "Bob" , age = 30 , companion = Pet (name = "Fluffy" , species = "cat" )),
244+ True ,
219245 0 ,
220246 id = "change-person-name-via-bindings" ,
221247 ),
222248 pytest .param (
223249 Person (name = "Alice" , age = 30 , companion = Pet (name = "Fluffy" , species = "cat" )),
224250 Person (name = "Alice" , age = 31 , companion = Pet (name = "Fluffy" , species = "cat" )),
251+ True ,
225252 1 ,
226253 id = "change-person-age-via-rerender-due-to-value-change" ,
227254 ),
@@ -234,15 +261,35 @@ async def test_updatable_component(
234261 server_host_port : str ,
235262 person : Person ,
236263 person_update : Person ,
264+ expect_same_companion_object : bool ,
237265 expected_callbacks_count : NonNegativeInt ,
238266):
239267 await async_page .goto (f"{ server_host_port } { mount_path } " )
268+ print ("✅ index page loaded" )
240269
241270 # check initial page layout
242271 await _ensure_index_page (async_page , person )
243272
273+ before_update = _get_updatable_display_model_ids (person )
244274 callbacks_count = person .update (person_update )
275+ after_update = _get_updatable_display_model_ids (person )
276+ assert (before_update == after_update ) is expect_same_companion_object
277+
245278 assert callbacks_count == expected_callbacks_count
246279
247280 # change layout after update
248281 await _ensure_index_page (async_page , person_update )
282+
283+ # REMOVE only the companion form UI
284+ person .companion .on_remove_from_ui ()
285+ await _ensure_companion_not_present (async_page )
286+
287+ # TODO: remove below check screenshto margins
288+ await _ensure_index_page (async_page , person_update )
289+
290+ # REMOVE the person form UI
291+ person .on_remove_from_ui ()
292+ await _ensure_person_not_present (async_page )
293+
294+ await _ensure_before_label (async_page )
295+ await _ensure_after_label (async_page )
0 commit comments