test_refreshable.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import asyncio
  2. from nicegui import ui
  3. from .screen import Screen
  4. def test_refreshable(screen: Screen) -> None:
  5. numbers = []
  6. @ui.refreshable
  7. def number_ui() -> None:
  8. ui.label('[' + ', '.join(str(n) for n in sorted(numbers)) + ']')
  9. number_ui()
  10. ui.button('Refresh', on_click=number_ui.refresh)
  11. screen.open('/')
  12. screen.should_contain('[]')
  13. numbers.append(1)
  14. screen.click('Refresh')
  15. screen.should_contain('[1]')
  16. numbers.append(2)
  17. screen.click('Refresh')
  18. screen.should_contain('[1, 2]')
  19. numbers.clear()
  20. screen.click('Refresh')
  21. screen.should_contain('[]')
  22. def test_async_refreshable(screen: Screen) -> None:
  23. numbers = []
  24. @ui.refreshable
  25. async def number_ui() -> None:
  26. await asyncio.sleep(0.1)
  27. ui.label('[' + ', '.join(str(n) for n in sorted(numbers)) + ']')
  28. @ui.page('/')
  29. async def index():
  30. with ui.column(): # this is necessary to reproduce bug #863
  31. await number_ui()
  32. ui.button('Refresh', on_click=number_ui.refresh)
  33. screen.open('/')
  34. screen.should_contain('[]')
  35. numbers.append(1)
  36. screen.click('Refresh')
  37. screen.should_not_contain('[]') # ensure bug #863 is fixed
  38. screen.should_contain('[1]')
  39. numbers.append(2)
  40. screen.click('Refresh')
  41. screen.should_not_contain('[]')
  42. screen.should_contain('[1, 2]')
  43. numbers.clear()
  44. screen.click('Refresh')
  45. screen.should_contain('[]')
  46. def test_multiple_targets(screen: Screen) -> None:
  47. count = 0
  48. class MyClass:
  49. def __init__(self, name: str) -> None:
  50. self.name = name
  51. self.state = 1
  52. @ui.refreshable
  53. def create_ui(self) -> None:
  54. nonlocal count
  55. count += 1
  56. ui.label(f'{self.name} = {self.state} ({count})')
  57. ui.button(f'increment {self.name}', on_click=self.increment)
  58. def increment(self) -> None:
  59. self.state += 1
  60. self.create_ui.refresh()
  61. a = MyClass('A')
  62. a.create_ui()
  63. b = MyClass('B')
  64. b.create_ui()
  65. screen.open('/')
  66. screen.should_contain('A = 1 (1)')
  67. screen.should_contain('B = 1 (2)')
  68. screen.click('increment A')
  69. screen.should_contain('A = 2 (3)')
  70. screen.should_contain('B = 1 (2)')
  71. screen.click('increment B')
  72. screen.should_contain('A = 2 (3)')
  73. screen.should_contain('B = 2 (4)')
  74. def test_refresh_with_arguments(screen: Screen):
  75. count = 0
  76. @ui.refreshable
  77. def some_ui(value: int):
  78. nonlocal count
  79. count += 1
  80. ui.label(f'{count=}, {value=}')
  81. some_ui(0)
  82. ui.button('refresh', on_click=some_ui.refresh)
  83. ui.button('refresh()', on_click=lambda: some_ui.refresh()) # pylint: disable=unnecessary-lambda
  84. ui.button('refresh(1)', on_click=lambda: some_ui.refresh(1))
  85. ui.button('refresh(2)', on_click=lambda: some_ui.refresh(2))
  86. ui.button('refresh(value=3)', on_click=lambda: some_ui.refresh(value=3))
  87. screen.open('/')
  88. screen.should_contain('count=1, value=0')
  89. screen.click('refresh')
  90. screen.should_contain('count=2, value=0')
  91. screen.click('refresh()')
  92. screen.should_contain('count=3, value=0')
  93. screen.click('refresh(1)')
  94. screen.should_contain('count=4, value=1')
  95. screen.click('refresh(2)')
  96. screen.should_contain('count=5, value=2')
  97. screen.click('refresh(value=3)')
  98. screen.assert_py_logger(
  99. 'ERROR', "'value' needs to be consistently passed to some_ui() either as positional or as keyword argument")
  100. def test_refresh_deleted_element(screen: Screen):
  101. @ui.refreshable
  102. def some_ui():
  103. ui.label('some text')
  104. with ui.card() as card:
  105. some_ui()
  106. ui.button('Refresh', on_click=some_ui.refresh)
  107. ui.button('Clear', on_click=card.clear)
  108. some_ui()
  109. screen.open('/')
  110. screen.should_contain('some text')
  111. screen.click('Clear')
  112. screen.click('Refresh')
  113. def test_refresh_with_function_reference(screen: Screen):
  114. # https://github.com/zauberzeug/nicegui/issues/1283
  115. class Test:
  116. def __init__(self, name):
  117. self.name = name
  118. self.ui()
  119. @ui.refreshable
  120. def ui(self):
  121. ui.notify(f'Refreshing {self.name}')
  122. ui.button(self.name, on_click=self.ui.refresh)
  123. Test('A')
  124. screen.assert_py_logger('WARNING', 'Ignoring notification "Refreshing A" because the client is not connected.')
  125. Test('B')
  126. screen.assert_py_logger('WARNING', 'Ignoring notification "Refreshing B" because the client is not connected.')
  127. screen.open('/')
  128. screen.click('A')
  129. screen.should_contain('Refreshing A')
  130. screen.click('B')
  131. screen.should_contain('Refreshing B')
  132. def test_refreshable_with_state(screen: Screen):
  133. @ui.refreshable
  134. def counter(title: str):
  135. count, set_count = ui.state(0)
  136. ui.label(f'{title}: {count}')
  137. ui.button(f'Increment {title}', on_click=lambda: set_count(count + 1))
  138. counter('A')
  139. counter('B')
  140. screen.open('/')
  141. screen.should_contain('A: 0')
  142. screen.should_contain('B: 0')
  143. screen.click('Increment A')
  144. screen.wait(0.5)
  145. screen.should_contain('A: 1')
  146. screen.should_contain('B: 0')
  147. screen.click('Increment B')
  148. screen.wait(0.5)
  149. screen.should_contain('A: 1')
  150. screen.should_contain('B: 1')
  151. def test_refreshable_with_return_value(screen: Screen):
  152. @ui.refreshable
  153. def number_ui() -> int:
  154. ui.label('42')
  155. return 42
  156. answer = number_ui()
  157. assert answer == 42
  158. screen.open('/')
  159. screen.should_contain('42')