test_binding.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import weakref
  2. from typing import Dict, Optional, Tuple
  3. from selenium.webdriver.common.keys import Keys
  4. from nicegui import binding, ui
  5. from nicegui.testing import Screen
  6. def test_ui_select_with_tuple_as_key(screen: Screen):
  7. class Model:
  8. selection: Optional[Tuple[int, int]] = None
  9. data = Model()
  10. options = {
  11. (2, 1): 'option A',
  12. (1, 2): 'option B',
  13. }
  14. data.selection = next(iter(options))
  15. ui.select(options).bind_value(data, 'selection')
  16. screen.open('/')
  17. screen.should_not_contain('option B')
  18. element = screen.click('option A')
  19. screen.click_at_position(element, x=20, y=100)
  20. screen.wait(0.3)
  21. screen.should_contain('option B')
  22. screen.should_not_contain('option A')
  23. assert data.selection == (1, 2)
  24. def test_ui_select_with_list_of_tuples(screen: Screen):
  25. class Model:
  26. selection = None
  27. data = Model()
  28. options = [(1, 1), (2, 2), (3, 3)]
  29. data.selection = options[0]
  30. ui.select(options).bind_value(data, 'selection')
  31. screen.open('/')
  32. screen.should_not_contain('2,2')
  33. element = screen.click('1,1')
  34. screen.click_at_position(element, x=20, y=100)
  35. screen.wait(0.3)
  36. screen.should_contain('2,2')
  37. screen.should_not_contain('1,1')
  38. assert data.selection == (2, 2)
  39. def test_ui_select_with_list_of_lists(screen: Screen):
  40. class Model:
  41. selection = None
  42. data = Model()
  43. options = [[1, 1], [2, 2], [3, 3]]
  44. data.selection = options[0]
  45. ui.select(options).bind_value(data, 'selection')
  46. screen.open('/')
  47. screen.should_not_contain('2,2')
  48. element = screen.click('1,1')
  49. screen.click_at_position(element, x=20, y=100)
  50. screen.wait(0.3)
  51. screen.should_contain('2,2')
  52. screen.should_not_contain('1,1')
  53. assert data.selection == [2, 2]
  54. def test_binding_to_input(screen: Screen):
  55. class Model:
  56. text = 'one'
  57. data = Model()
  58. element = ui.input().bind_value(data, 'text')
  59. screen.open('/')
  60. screen.should_contain_input('one')
  61. screen.type(Keys.TAB)
  62. screen.type('two')
  63. screen.should_contain_input('two')
  64. screen.wait(0.1)
  65. assert data.text == 'two'
  66. data.text = 'three'
  67. screen.should_contain_input('three')
  68. element.set_value('four')
  69. screen.should_contain_input('four')
  70. assert data.text == 'four'
  71. element.value = 'five'
  72. screen.should_contain_input('five')
  73. assert data.text == 'five'
  74. def test_binding_refresh_before_page_delivery(screen: Screen):
  75. state = {'count': 0}
  76. @ui.page('/')
  77. def main_page() -> None:
  78. ui.label().bind_text_from(state, 'count')
  79. state['count'] += 1
  80. screen.open('/')
  81. screen.should_contain('1')
  82. def test_missing_target_attribute(screen: Screen):
  83. data: Dict = {}
  84. ui.label('Hello').bind_text_to(data)
  85. ui.label().bind_text_from(data, 'text', lambda text: f'{text=}')
  86. screen.open('/')
  87. screen.should_contain("text='Hello'")
  88. def test_bindable_dataclass(screen: Screen):
  89. @binding.bindable_dataclass(bindable_fields=['bindable'])
  90. class TestClass:
  91. not_bindable: str = 'not_bindable_text'
  92. bindable: str = 'bindable_text'
  93. instance = TestClass()
  94. ui.label().bind_text_from(instance, 'not_bindable')
  95. ui.label().bind_text_from(instance, 'bindable')
  96. screen.open('/')
  97. screen.should_contain('not_bindable_text')
  98. screen.should_contain('bindable_text')
  99. assert len(binding.bindings) == 2
  100. assert len(binding.active_links) == 1
  101. assert binding.active_links[0][1] == 'not_bindable'
  102. def test_automatic_cleanup(screen: Screen):
  103. class Model:
  104. value = binding.BindableProperty()
  105. def __init__(self, value: str) -> None:
  106. self.value = value
  107. def create_model_and_label(value: str) -> Tuple[Model, weakref.ref, ui.label]:
  108. model = Model(value)
  109. label = ui.label(value).bind_text(model, 'value')
  110. return id(model), weakref.ref(model), label
  111. model_id1, ref1, label1 = create_model_and_label('first label')
  112. model_id2, ref2, _label2 = create_model_and_label('second label')
  113. def is_alive(ref: weakref.ref) -> bool:
  114. return ref() is not None
  115. def has_bindable_property(model_id: int) -> bool:
  116. return any(obj_id == model_id for obj_id, _ in binding.bindable_properties)
  117. screen.open('/')
  118. screen.should_contain('first label')
  119. screen.should_contain('second label')
  120. assert is_alive(ref1) and has_bindable_property(model_id1)
  121. assert is_alive(ref2) and has_bindable_property(model_id2)
  122. binding.remove([label1])
  123. assert not is_alive(ref1) and not has_bindable_property(model_id1)
  124. assert is_alive(ref2) and has_bindable_property(model_id2)