test_table.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. from datetime import datetime, timedelta, timezone
  2. from typing import List
  3. import pandas as pd
  4. from selenium.webdriver.common.by import By
  5. from nicegui import ui
  6. from nicegui.testing import Screen
  7. def columns() -> List:
  8. return [
  9. {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True},
  10. {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},
  11. ]
  12. def rows() -> List:
  13. return [
  14. {'id': 0, 'name': 'Alice', 'age': 18},
  15. {'id': 1, 'name': 'Bob', 'age': 21},
  16. {'id': 2, 'name': 'Lionel', 'age': 19},
  17. ]
  18. def test_table(screen: Screen):
  19. ui.table(title='My Team', columns=columns(), rows=rows())
  20. screen.open('/')
  21. screen.should_contain('My Team')
  22. screen.should_contain('Name')
  23. screen.should_contain('Alice')
  24. screen.should_contain('Bob')
  25. screen.should_contain('Lionel')
  26. def test_pagination_int(screen: Screen):
  27. ui.table(columns=columns(), rows=rows(), pagination=2)
  28. screen.open('/')
  29. screen.should_contain('Alice')
  30. screen.should_contain('Bob')
  31. screen.should_not_contain('Lionel')
  32. screen.should_contain('1-2 of 3')
  33. def test_pagination_dict(screen: Screen):
  34. ui.table(columns=columns(), rows=rows(), pagination={'rowsPerPage': 2})
  35. screen.open('/')
  36. screen.should_contain('Alice')
  37. screen.should_contain('Bob')
  38. screen.should_not_contain('Lionel')
  39. screen.should_contain('1-2 of 3')
  40. def test_filter(screen: Screen):
  41. table = ui.table(columns=columns(), rows=rows())
  42. ui.input('Search by name').bind_value(table, 'filter')
  43. screen.open('/')
  44. screen.should_contain('Alice')
  45. screen.should_contain('Bob')
  46. screen.should_contain('Lionel')
  47. element = screen.selenium.find_element(By.XPATH, '//*[@aria-label="Search by name"]')
  48. element.send_keys('e')
  49. screen.should_contain('Alice')
  50. screen.should_not_contain('Bob')
  51. screen.should_contain('Lionel')
  52. def test_add_remove(screen: Screen):
  53. table = ui.table(columns=columns(), rows=rows())
  54. ui.button('Add', on_click=lambda: table.add_rows({'id': 3, 'name': 'Carol', 'age': 32}))
  55. ui.button('Remove', on_click=lambda: table.remove_rows(table.rows[0]))
  56. screen.open('/')
  57. screen.click('Add')
  58. screen.should_contain('Carol')
  59. screen.click('Remove')
  60. screen.wait(0.5)
  61. screen.should_not_contain('Alice')
  62. def test_slots(screen: Screen):
  63. with ui.table(columns=columns(), rows=rows()) as table:
  64. with table.add_slot('top-row'):
  65. with table.row():
  66. with table.cell():
  67. ui.label('This is the top slot.')
  68. table.add_slot('body', '''
  69. <q-tr :props="props">
  70. <q-td key="name" :props="props">overridden</q-td>
  71. <q-td key="age" :props="props">
  72. <q-badge color="green">{{ props.row.age }}</q-badge>
  73. </q-td>
  74. </q-tr>
  75. ''')
  76. screen.open('/')
  77. screen.should_contain('This is the top slot.')
  78. screen.should_not_contain('Alice')
  79. screen.should_contain('overridden')
  80. screen.should_contain('21')
  81. def test_single_selection(screen: Screen):
  82. ui.table(columns=columns(), rows=rows(), selection='single')
  83. screen.open('/')
  84. screen.find('Alice').find_element(By.XPATH, 'preceding-sibling::td').click()
  85. screen.wait(0.5)
  86. screen.should_contain('1 record selected.')
  87. screen.find('Bob').find_element(By.XPATH, 'preceding-sibling::td').click()
  88. screen.wait(0.5)
  89. screen.should_contain('1 record selected.')
  90. def test_dynamic_column_attributes(screen: Screen):
  91. ui.table(columns=[{'name': 'age', 'label': 'Age', 'field': 'age', ':format': 'value => value + " years"'}],
  92. rows=[{'name': 'Alice', 'age': 18}])
  93. screen.open('/')
  94. screen.should_contain('18 years')
  95. def test_remove_selection(screen: Screen):
  96. t = ui.table(columns=columns(), rows=rows(), selection='single')
  97. ui.button('Remove first row', on_click=lambda: t.remove_rows(t.rows[0]))
  98. screen.open('/')
  99. screen.find('Alice').find_element(By.XPATH, 'preceding-sibling::td').click()
  100. screen.should_contain('1 record selected.')
  101. screen.click('Remove first row')
  102. screen.wait(0.5)
  103. screen.should_not_contain('Alice')
  104. screen.should_not_contain('1 record selected.')
  105. def test_replace_rows(screen: Screen):
  106. t = ui.table(columns=columns(), rows=rows())
  107. def replace_rows_with_carol():
  108. t.rows = [{'id': 3, 'name': 'Carol', 'age': 32}]
  109. def replace_rows_with_daniel():
  110. t.update_rows([{'id': 4, 'name': 'Daniel', 'age': 33}])
  111. ui.button('Replace rows with C.', on_click=replace_rows_with_carol)
  112. ui.button('Replace rows with D.', on_click=replace_rows_with_daniel)
  113. screen.open('/')
  114. screen.should_contain('Alice')
  115. screen.should_contain('Bob')
  116. screen.should_contain('Lionel')
  117. screen.click('Replace rows with C.')
  118. screen.wait(0.5)
  119. screen.should_not_contain('Alice')
  120. screen.should_not_contain('Bob')
  121. screen.should_not_contain('Lionel')
  122. screen.should_contain('Carol')
  123. screen.click('Replace rows with D.')
  124. screen.wait(0.5)
  125. screen.should_not_contain('Carol')
  126. screen.should_contain('Daniel')
  127. def test_create_from_pandas(screen: Screen):
  128. df = pd.DataFrame({'name': ['Alice', 'Bob'], 'age': [18, 21], 42: 'answer'})
  129. ui.table.from_pandas(df)
  130. screen.open('/')
  131. screen.should_contain('Alice')
  132. screen.should_contain('Bob')
  133. screen.should_contain('18')
  134. screen.should_contain('21')
  135. screen.should_contain('42')
  136. screen.should_contain('answer')
  137. def test_problematic_datatypes(screen: Screen):
  138. df = pd.DataFrame({
  139. 'Datetime_col': [datetime(2020, 1, 1)],
  140. 'Datetime_col_tz': [datetime(2020, 1, 1, tzinfo=timezone.utc)],
  141. 'Timedelta_col': [timedelta(days=5)],
  142. 'Complex_col': [1 + 2j],
  143. 'Period_col': pd.Series([pd.Period('2021-01')]),
  144. })
  145. ui.table.from_pandas(df)
  146. screen.open('/')
  147. screen.should_contain('Datetime_col')
  148. screen.should_contain('Datetime_col_tz')
  149. screen.should_contain('Timedelta_col')
  150. screen.should_contain('Complex_col')
  151. screen.should_contain('Period_col')
  152. screen.should_contain('2020-01-01')
  153. screen.should_contain('5 days')
  154. screen.should_contain('(1+2j)')
  155. screen.should_contain('2021-01')