test_aggrid.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. from datetime import datetime, timedelta, timezone
  2. from typing import List
  3. import pandas as pd
  4. from selenium.webdriver.common.action_chains import ActionChains
  5. from selenium.webdriver.common.keys import Keys
  6. from nicegui import ui
  7. from nicegui.testing import Screen
  8. def test_update_table(screen: Screen):
  9. grid = ui.aggrid({
  10. 'columnDefs': [{'field': 'name'}, {'field': 'age'}],
  11. 'rowData': [{'name': 'Alice', 'age': 18}],
  12. })
  13. screen.open('/')
  14. screen.should_contain('Name')
  15. screen.should_contain('Age')
  16. screen.should_contain('Alice')
  17. screen.should_contain('18')
  18. grid.options['rowData'][0]['age'] = 42
  19. screen.wait(0.5) # HACK: try to fix flaky test
  20. grid.update()
  21. screen.wait(0.5) # HACK: try to fix flaky test
  22. screen.should_contain('42')
  23. def test_add_row(screen: Screen):
  24. grid = ui.aggrid({
  25. 'columnDefs': [{'field': 'name'}, {'field': 'age'}],
  26. 'rowData': [],
  27. })
  28. ui.button('Update', on_click=grid.update)
  29. screen.open('/')
  30. grid.options['rowData'].append({'name': 'Alice', 'age': 18})
  31. screen.click('Update')
  32. screen.wait(0.5)
  33. screen.should_contain('Alice')
  34. screen.should_contain('18')
  35. grid.options['rowData'].append({'name': 'Bob', 'age': 21})
  36. screen.click('Update')
  37. screen.wait(0.5)
  38. screen.should_contain('Alice')
  39. screen.should_contain('18')
  40. screen.should_contain('Bob')
  41. screen.should_contain('21')
  42. def test_click_cell(screen: Screen):
  43. grid = ui.aggrid({
  44. 'columnDefs': [{'field': 'name'}, {'field': 'age'}],
  45. 'rowData': [{'name': 'Alice', 'age': 18}],
  46. })
  47. grid.on('cellClicked', lambda e: ui.label(f'{e.args["data"]["name"]} has been clicked!'))
  48. screen.open('/')
  49. screen.click('Alice')
  50. screen.should_contain('Alice has been clicked!')
  51. def test_html_columns(screen: Screen):
  52. ui.aggrid({
  53. 'columnDefs': [{'field': 'name'}, {'field': 'age'}],
  54. 'rowData': [{'name': '<span class="text-bold">Alice</span>', 'age': 18}],
  55. }, html_columns=[0])
  56. screen.open('/')
  57. screen.should_contain('Alice')
  58. screen.should_not_contain('<span')
  59. assert 'text-bold' in screen.find('Alice').get_attribute('class')
  60. def test_dynamic_method(screen: Screen):
  61. ui.aggrid({
  62. 'columnDefs': [{'field': 'name'}, {'field': 'age'}],
  63. 'rowData': [{'name': 'Alice', 'age': '18'}, {'name': 'Bob', 'age': '21'}, {'name': 'Carol', 'age': '42'}],
  64. ':getRowHeight': 'params => params.data.age > 35 ? 50 : 25',
  65. })
  66. screen.open('/')
  67. trs = screen.find_all_by_class('ag-row')
  68. assert len(trs) == 3
  69. heights = [int(tr.get_attribute('clientHeight')) for tr in trs]
  70. assert 23 <= heights[0] <= 25
  71. assert 23 <= heights[1] <= 25
  72. assert 48 <= heights[2] <= 50
  73. def test_run_grid_method_with_argument(screen: Screen):
  74. grid = ui.aggrid({
  75. 'columnDefs': [{'field': 'name', 'filter': True}],
  76. 'rowData': [{'name': 'Alice'}, {'name': 'Bob'}, {'name': 'Carol'}],
  77. })
  78. filter_model = {'name': {'filterType': 'text', 'type': 'equals', 'filter': 'Alice'}}
  79. ui.button('Filter', on_click=lambda: grid.run_grid_method('setFilterModel', filter_model))
  80. screen.open('/')
  81. screen.should_contain('Alice')
  82. screen.should_contain('Bob')
  83. screen.should_contain('Carol')
  84. screen.click('Filter')
  85. screen.wait(0.5)
  86. screen.should_contain('Alice')
  87. screen.should_not_contain('Bob')
  88. screen.should_not_contain('Carol')
  89. def test_get_selected_rows(screen: Screen):
  90. @ui.page('/')
  91. def page():
  92. grid = ui.aggrid({
  93. 'columnDefs': [{'field': 'name'}],
  94. 'rowData': [{'name': 'Alice'}, {'name': 'Bob'}, {'name': 'Carol'}],
  95. 'rowSelection': 'multiple',
  96. })
  97. async def get_selected_rows():
  98. ui.label(str(await grid.get_selected_rows()))
  99. ui.button('Get selected rows', on_click=get_selected_rows)
  100. async def get_selected_row():
  101. ui.label(str(await grid.get_selected_row()))
  102. ui.button('Get selected row', on_click=get_selected_row)
  103. screen.open('/')
  104. screen.click('Alice')
  105. screen.find('Bob')
  106. ActionChains(screen.selenium).key_down(Keys.SHIFT).click(screen.find('Bob')).key_up(Keys.SHIFT).perform()
  107. screen.click('Get selected rows')
  108. screen.should_contain("[{'name': 'Alice'}, {'name': 'Bob'}]")
  109. screen.click('Get selected row')
  110. screen.should_contain("{'name': 'Alice'}")
  111. def test_replace_aggrid(screen: Screen):
  112. with ui.row().classes('w-full') as container:
  113. ui.aggrid({'columnDefs': [{'field': 'name'}], 'rowData': [{'name': 'Alice'}]})
  114. def replace():
  115. container.clear()
  116. with container:
  117. ui.aggrid({'columnDefs': [{'field': 'name'}], 'rowData': [{'name': 'Bob'}]})
  118. ui.button('Replace', on_click=replace)
  119. screen.open('/')
  120. screen.should_contain('Alice')
  121. screen.click('Replace')
  122. screen.should_contain('Bob')
  123. screen.should_not_contain('Alice')
  124. def test_create_from_pandas(screen: Screen):
  125. df = pd.DataFrame({'name': ['Alice', 'Bob'], 'age': [18, 21], 42: 'answer'})
  126. ui.aggrid.from_pandas(df)
  127. screen.open('/')
  128. screen.should_contain('Alice')
  129. screen.should_contain('Bob')
  130. screen.should_contain('18')
  131. screen.should_contain('21')
  132. screen.should_contain('42')
  133. screen.should_contain('answer')
  134. def test_create_dynamically(screen: Screen):
  135. ui.button('Create', on_click=lambda: ui.aggrid({'columnDefs': [{'field': 'name'}], 'rowData': [{'name': 'Alice'}]}))
  136. screen.open('/')
  137. screen.click('Create')
  138. screen.should_contain('Alice')
  139. def test_api_method_after_creation(screen: Screen):
  140. options = {'columnDefs': [{'field': 'name'}], 'rowData': [{'name': 'Alice'}]}
  141. ui.button('Create', on_click=lambda: ui.aggrid(options).run_grid_method('selectAll'))
  142. screen.open('/')
  143. screen.click('Create')
  144. assert screen.find_by_class('ag-row-selected')
  145. def test_problematic_datatypes(screen: Screen):
  146. df = pd.DataFrame({
  147. 'datetime_col': [datetime(2020, 1, 1)],
  148. 'datetime_col_tz': [datetime(2020, 1, 1, tzinfo=timezone.utc)],
  149. 'timedelta_col': [timedelta(days=5)],
  150. 'complex_col': [1 + 2j],
  151. 'period_col': pd.Series([pd.Period('2021-01')]),
  152. })
  153. ui.aggrid.from_pandas(df)
  154. screen.open('/')
  155. screen.should_contain('Datetime_col')
  156. screen.should_contain('Datetime_col_tz')
  157. screen.should_contain('Timedelta_col')
  158. screen.should_contain('Complex_col')
  159. screen.should_contain('Period_col')
  160. screen.should_contain('2020-01-01')
  161. screen.should_contain('5 days')
  162. screen.should_contain('(1+2j)')
  163. screen.should_contain('2021-01')
  164. def test_run_row_method(screen: Screen):
  165. grid = ui.aggrid({
  166. 'columnDefs': [{'field': 'name'}, {'field': 'age'}],
  167. 'rowData': [{'name': 'Alice', 'age': 18}],
  168. ':getRowId': '(params) => params.data.name',
  169. })
  170. ui.button('Update', on_click=lambda: grid.run_row_method('Alice', 'setDataValue', 'age', 42))
  171. screen.open('/')
  172. screen.should_contain('Alice')
  173. screen.should_contain('18')
  174. screen.click('Update')
  175. screen.should_contain('Alice')
  176. screen.should_contain('42')
  177. def test_run_method_with_function(screen: Screen):
  178. @ui.page('/')
  179. def page():
  180. grid = ui.aggrid({'columnDefs': [{'field': 'name'}], 'rowData': [{'name': 'Alice'}, {'name': 'Bob'}]})
  181. async def print_row(index: int) -> None:
  182. ui.label(f'Row {index}: {await grid.run_grid_method(f"(g) => g.getDisplayedRowAtIndex({index}).data")}')
  183. ui.button('Print Row 0', on_click=lambda: print_row(0))
  184. screen.open('/')
  185. screen.click('Print Row 0')
  186. screen.should_contain("Row 0: {'name': 'Alice'}")
  187. def test_get_client_data(screen: Screen):
  188. data: List = []
  189. @ui.page('/')
  190. def page():
  191. grid = ui.aggrid({
  192. 'columnDefs': [
  193. {'field': 'name'},
  194. {'field': 'age', 'sort': 'desc'},
  195. ],
  196. 'rowData': [
  197. {'name': 'Alice', 'age': 18},
  198. {'name': 'Bob', 'age': 21},
  199. {'name': 'Carol', 'age': 42},
  200. ],
  201. })
  202. async def get_data():
  203. data[:] = await grid.get_client_data()
  204. ui.button('Get Data', on_click=get_data)
  205. async def get_sorted_data():
  206. data[:] = await grid.get_client_data(method='filtered_sorted')
  207. ui.button('Get Sorted Data', on_click=get_sorted_data)
  208. screen.open('/')
  209. screen.click('Get Data')
  210. screen.wait(0.5)
  211. assert data == [{'name': 'Alice', 'age': 18}, {'name': 'Bob', 'age': 21}, {'name': 'Carol', 'age': 42}]
  212. screen.click('Get Sorted Data')
  213. screen.wait(0.5)
  214. assert data == [{'name': 'Carol', 'age': 42}, {'name': 'Bob', 'age': 21}, {'name': 'Alice', 'age': 18}]