test_user_simulation.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. from typing import Callable, Dict, Type
  2. import pytest
  3. from fastapi.responses import PlainTextResponse
  4. from nicegui import app, ui
  5. from nicegui.testing import User
  6. # pylint: disable=missing-function-docstring
  7. async def test_auto_index_page(user: User) -> None:
  8. ui.label('Main page')
  9. await user.open('/')
  10. await user.should_see('Main page')
  11. async def test_multiple_pages(create_user: Callable[[], User]) -> None:
  12. @ui.page('/')
  13. def index():
  14. ui.label('Main page')
  15. @ui.page('/other')
  16. def other():
  17. ui.label('Other page')
  18. userA = create_user()
  19. userB = create_user()
  20. await userA.open('/')
  21. await userA.should_see('Main page')
  22. await userA.should_not_see('Other page')
  23. await userB.open('/other')
  24. await userB.should_see('Other page')
  25. await userB.should_not_see('Main page')
  26. async def test_source_element(user: User) -> None:
  27. @ui.page('/')
  28. def index():
  29. ui.image('https://via.placeholder.com/150')
  30. await user.open('/')
  31. await user.should_see('placeholder.com')
  32. async def test_button_click(user: User) -> None:
  33. @ui.page('/')
  34. def index():
  35. ui.button('click me', on_click=lambda: ui.label('clicked'))
  36. await user.open('/')
  37. user.find('click me').click()
  38. await user.should_see('clicked')
  39. async def test_assertion_raised_when_no_nicegui_page_is_returned(user: User) -> None:
  40. @app.get('/plain')
  41. def index() -> PlainTextResponse:
  42. return PlainTextResponse('Hello')
  43. with pytest.raises(ValueError):
  44. await user.open('/plain')
  45. async def test_assertion_raised_when_element_not_found(user: User) -> None:
  46. @ui.page('/')
  47. def index():
  48. ui.label('Hello')
  49. await user.open('/')
  50. with pytest.raises(AssertionError):
  51. await user.should_see('World')
  52. @pytest.mark.parametrize('storage_builder', [lambda: app.storage.browser, lambda: app.storage.user])
  53. async def test_storage(user: User, storage_builder: Callable[[], Dict]) -> None:
  54. @ui.page('/')
  55. def page():
  56. storage = storage_builder()
  57. storage['count'] = storage.get('count', 0) + 1
  58. ui.label().bind_text_from(storage, 'count')
  59. await user.open('/')
  60. await user.should_see('1')
  61. await user.open('/')
  62. await user.should_see('2')
  63. async def test_navigation(user: User) -> None:
  64. @ui.page('/')
  65. def page():
  66. ui.label('Main page')
  67. ui.button('go to other', on_click=lambda: ui.navigate.to('/other'))
  68. ui.button('forward', on_click=ui.navigate.forward)
  69. @ui.page('/other')
  70. def other():
  71. ui.label('Other page')
  72. ui.button('back', on_click=ui.navigate.back)
  73. await user.open('/')
  74. await user.should_see('Main page')
  75. user.find('go to other').click()
  76. await user.should_see('Other page')
  77. user.find('back').click()
  78. await user.should_see('Main page')
  79. user.find('forward').click()
  80. await user.should_see('Other page')
  81. async def test_multi_user_navigation(create_user: Callable[[], User]) -> None:
  82. @ui.page('/')
  83. def page():
  84. ui.label('Main page')
  85. ui.button('go to other', on_click=lambda: ui.navigate.to('/other'))
  86. ui.button('forward', on_click=ui.navigate.forward)
  87. @ui.page('/other')
  88. def other():
  89. ui.label('Other page')
  90. ui.button('back', on_click=ui.navigate.back)
  91. userA = create_user()
  92. userB = create_user()
  93. await userA.open('/')
  94. await userA.should_see('Main page')
  95. await userB.open('/')
  96. await userB.should_see('Main page')
  97. userA.find('go to other').click()
  98. await userA.should_see('Other page')
  99. await userB.should_see('Main page')
  100. userA.find('back').click()
  101. await userA.should_see('Main page')
  102. await userB.should_see('Main page')
  103. userA.find('forward').click()
  104. await userA.should_see('Other page')
  105. await userB.should_see('Main page')
  106. async def test_reload(user: User) -> None:
  107. @ui.page('/')
  108. def page():
  109. ui.input('test input')
  110. ui.button('reload', on_click=ui.navigate.reload)
  111. await user.open('/')
  112. await user.should_not_see('Hello')
  113. user.find('test input').type('Hello')
  114. await user.should_see('Hello')
  115. user.find('reload').click()
  116. await user.should_not_see('Hello')
  117. async def test_notification(user: User) -> None:
  118. @ui.page('/')
  119. def page():
  120. ui.button('notify', on_click=lambda: ui.notify('Hello'))
  121. await user.open('/')
  122. user.find('notify').click()
  123. await user.should_see('Hello')
  124. @pytest.mark.parametrize('kind', [ui.checkbox, ui.switch])
  125. async def test_checkbox_and_switch(user: User, kind: Type) -> None:
  126. element = kind('my element', on_change=lambda e: ui.notify(f'Changed: {e.value}'))
  127. ui.label().bind_text_from(element, 'value', lambda v: 'enabled' if v else 'disabled')
  128. await user.open('/')
  129. await user.should_see('disabled')
  130. user.find('element').click()
  131. await user.should_see('enabled')
  132. await user.should_see('Changed: True')
  133. user.find('element').click()
  134. await user.should_see('disabled')
  135. await user.should_see('Changed: False')
  136. @pytest.mark.parametrize('kind', [ui.input, ui.editor, ui.codemirror])
  137. async def test_input(user: User, kind: Type) -> None:
  138. element = kind(on_change=lambda e: ui.notify(f'Changed: {e.value}'))
  139. ui.label().bind_text_from(element, 'value', lambda v: f'Value: {v}')
  140. await user.open('/')
  141. await user.should_see('Value: ')
  142. user.find(kind).type('Hello')
  143. await user.should_see('Value: Hello')
  144. await user.should_see('Changed: Hello')
  145. user.find(kind).type(' World')
  146. await user.should_see('Value: Hello World')
  147. await user.should_see('Changed: Hello World')
  148. async def test_should_not_see(user: User) -> None:
  149. @ui.page('/')
  150. def page():
  151. ui.label('Hello')
  152. await user.open('/')
  153. await user.should_not_see('World')
  154. await user.should_see('Hello')
  155. async def test_should_not_see_notification(user: User) -> None:
  156. @ui.page('/')
  157. def page():
  158. ui.button('Notify', on_click=lambda: ui.notification('Hello'))
  159. await user.open('/')
  160. await user.should_not_see('Hello')
  161. user.find('Notify').click()
  162. await user.should_see('Hello')
  163. with pytest.raises(AssertionError):
  164. await user.should_not_see('Hello')
  165. user.find('Hello').trigger('dismiss')
  166. await user.should_not_see('Hello')
  167. async def test_trigger_event(user: User) -> None:
  168. @ui.page('/')
  169. def page():
  170. ui.input().on('keydown.enter', lambda: ui.notify('Enter pressed'))
  171. await user.open('/')
  172. user.find(ui.input).trigger('keydown.enter')
  173. await user.should_see('Enter pressed')
  174. async def test_click_link(user: User) -> None:
  175. @ui.page('/')
  176. def page():
  177. ui.link('go to other', '/other')
  178. @ui.page('/other')
  179. def other():
  180. ui.label('Other page')
  181. await user.open('/')
  182. user.find('go to other').click()
  183. await user.should_see('Other page')
  184. async def test_kind_content_marker_combinations(user: User) -> None:
  185. @ui.page('/')
  186. def page():
  187. ui.label('One')
  188. ui.button('Two')
  189. ui.button('Three').mark('three')
  190. await user.open('/')
  191. await user.should_see(content='One')
  192. await user.should_see(kind=ui.button)
  193. await user.should_see(kind=ui.button, content='Two')
  194. with pytest.raises(AssertionError):
  195. await user.should_see(kind=ui.button, content='One')
  196. await user.should_see(marker='three')
  197. await user.should_see(kind=ui.button, marker='three')
  198. with pytest.raises(AssertionError):
  199. await user.should_see(marker='three', content='One')
  200. async def test_page_to_string_output_used_in_error_messages(user: User) -> None:
  201. @ui.page('/')
  202. def page():
  203. ui.label('Hello').mark('first')
  204. with ui.row():
  205. with ui.column():
  206. ui.button('World').mark('second')
  207. ui.icon('thumbs-up').mark('third')
  208. ui.avatar('star')
  209. ui.input('some input', placeholder='type here', value='typed')
  210. ui.markdown('''## Markdown
  211. - A
  212. - B
  213. - C
  214. ''')
  215. with ui.card().tight():
  216. ui.image('https://via.placeholder.com/150')
  217. await user.open('/')
  218. output = str(user.current_layout)
  219. assert output == '''
  220. q-layout
  221. q-page-container
  222. q-page
  223. div
  224. Label [markers=first, text=Hello]
  225. Row
  226. Column
  227. Button [markers=second, label=World]
  228. Icon [markers=third, name=thumbs-up]
  229. Avatar [icon=star]
  230. Input [value=typed, label=some input, placeholder=type here, type=text]
  231. Markdown [content=## Markdown...]
  232. Card
  233. Image [src=https://via.placehol...]
  234. '''.strip()
  235. async def test_combined_filter_parameters(user: User) -> None:
  236. ui.input(placeholder='x', value='y')
  237. await user.open('/')
  238. await user.should_see('x')
  239. await user.should_see('y')
  240. await user.should_not_see('x y')
  241. async def test_typing(user: User) -> None:
  242. @ui.page('/')
  243. def page():
  244. ui.label('Hello!')
  245. ui.button('World!')
  246. await user.open('/')
  247. # NOTE we have not yet found a way to test the typing suggestions automatically
  248. # to test, hover over the variable and verify that your IDE inferres the correct type
  249. _ = user.find(kind=ui.label).elements # Set[ui.label]
  250. _ = user.find(ui.label).elements # Set[ui.label]
  251. _ = user.find('World').elements # Set[ui.element]
  252. _ = user.find('Hello').elements # Set[ui.element]
  253. _ = user.find('!').elements # Set[ui.element]
  254. async def test_select(user: User) -> None:
  255. ui.select(options=['A', 'B', 'C'], on_change=lambda e: ui.notify(f'Value: {e.value}'))
  256. await user.open('/')
  257. await user.should_not_see('A')
  258. await user.should_not_see('B')
  259. await user.should_not_see('C')
  260. user.find(ui.select).click()
  261. await user.should_see('B')
  262. await user.should_see('C')
  263. user.find('A').click()
  264. await user.should_see('Value: A')
  265. await user.should_see('A')
  266. await user.should_not_see('B')
  267. await user.should_not_see('C')