1
0

test_aggrid.py 9.4 KB

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