test_form_submit.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. """Integration tests for forms."""
  2. import time
  3. from typing import Generator
  4. import pytest
  5. from selenium.webdriver.common.by import By
  6. from selenium.webdriver.common.keys import Keys
  7. from reflex.testing import AppHarness
  8. def FormSubmit():
  9. """App with a form using on_submit."""
  10. import reflex as rx
  11. class FormState(rx.State):
  12. form_data: dict = {}
  13. var_options: list[str] = ["option3", "option4"]
  14. def form_submit(self, form_data: dict):
  15. self.form_data = form_data
  16. app = rx.App(state=FormState)
  17. @app.add_page
  18. def index():
  19. return rx.vstack(
  20. rx.input(
  21. value=FormState.router.session.client_token,
  22. is_read_only=True,
  23. id="token",
  24. ),
  25. rx.form(
  26. rx.vstack(
  27. rx.input(id="name_input"),
  28. rx.hstack(rx.pin_input(length=4, id="pin_input")),
  29. rx.number_input(id="number_input"),
  30. rx.checkbox(id="bool_input"),
  31. rx.switch(id="bool_input2"),
  32. rx.slider(id="slider_input"),
  33. rx.range_slider(id="range_input"),
  34. rx.radio_group(["option1", "option2"], id="radio_input"),
  35. rx.radio_group(FormState.var_options, id="radio_input_var"),
  36. rx.select(["option1", "option2"], id="select_input"),
  37. rx.select(FormState.var_options, id="select_input_var"),
  38. rx.text_area(id="text_area_input"),
  39. rx.input(
  40. id="debounce_input",
  41. debounce_timeout=0,
  42. on_change=rx.console_log,
  43. ),
  44. rx.button("Submit", type_="submit"),
  45. ),
  46. on_submit=FormState.form_submit,
  47. ),
  48. rx.spacer(),
  49. height="100vh",
  50. )
  51. app.compile()
  52. @pytest.fixture(scope="session")
  53. def form_submit(tmp_path_factory) -> Generator[AppHarness, None, None]:
  54. """Start FormSubmit app at tmp_path via AppHarness.
  55. Args:
  56. tmp_path_factory: pytest tmp_path_factory fixture
  57. Yields:
  58. running AppHarness instance
  59. """
  60. with AppHarness.create(
  61. root=tmp_path_factory.mktemp("form_submit"),
  62. app_source=FormSubmit, # type: ignore
  63. ) as harness:
  64. assert harness.app_instance is not None, "app is not running"
  65. yield harness
  66. @pytest.fixture
  67. def driver(form_submit: AppHarness):
  68. """GEt an instance of the browser open to the form_submit app.
  69. Args:
  70. form_submit: harness for ServerSideEvent app
  71. Yields:
  72. WebDriver instance.
  73. """
  74. driver = form_submit.frontend()
  75. try:
  76. yield driver
  77. finally:
  78. driver.quit()
  79. @pytest.mark.asyncio
  80. async def test_submit(driver, form_submit: AppHarness):
  81. """Fill a form with various different output, submit it to backend and verify
  82. the output.
  83. Args:
  84. driver: selenium WebDriver open to the app
  85. form_submit: harness for FormSubmit app
  86. """
  87. assert form_submit.app_instance is not None, "app is not running"
  88. # get a reference to the connected client
  89. token_input = driver.find_element(By.ID, "token")
  90. assert token_input
  91. # wait for the backend connection to send the token
  92. token = form_submit.poll_for_value(token_input)
  93. assert token
  94. name_input = driver.find_element(By.ID, "name_input")
  95. name_input.send_keys("foo")
  96. pin_inputs = driver.find_elements(By.CLASS_NAME, "chakra-pin-input")
  97. pin_values = ["8", "1", "6", "4"]
  98. for i, pin_input in enumerate(pin_inputs):
  99. pin_input.send_keys(pin_values[i])
  100. number_input = driver.find_element(By.CLASS_NAME, "chakra-numberinput")
  101. buttons = number_input.find_elements(By.XPATH, "//div[@role='button']")
  102. for _ in range(3):
  103. buttons[1].click()
  104. checkbox_input = driver.find_element(By.CLASS_NAME, "chakra-checkbox__control")
  105. checkbox_input.click()
  106. switch_input = driver.find_element(By.CLASS_NAME, "chakra-switch__track")
  107. switch_input.click()
  108. radio_buttons = driver.find_elements(By.CLASS_NAME, "chakra-radio__control")
  109. radio_buttons[1].click()
  110. textarea_input = driver.find_element(By.CLASS_NAME, "chakra-textarea")
  111. textarea_input.send_keys("Some", Keys.ENTER, "Text")
  112. debounce_input = driver.find_element(By.ID, "debounce_input")
  113. debounce_input.send_keys("bar baz")
  114. time.sleep(1)
  115. submit_input = driver.find_element(By.CLASS_NAME, "chakra-button")
  116. submit_input.click()
  117. async def get_form_data():
  118. return (await form_submit.get_state(token)).form_data
  119. # wait for the form data to arrive at the backend
  120. form_data = await AppHarness._poll_for_async(get_form_data)
  121. assert isinstance(form_data, dict)
  122. assert form_data["name_input"] == "foo"
  123. assert form_data["pin_input"] == pin_values
  124. assert form_data["number_input"] == "-3"
  125. assert form_data["bool_input"] is True
  126. assert form_data["bool_input2"] is True
  127. assert form_data["slider_input"] == "50"
  128. assert form_data["range_input"] == ["25", "75"]
  129. assert form_data["radio_input"] == "option2"
  130. assert form_data["select_input"] == "option1"
  131. assert form_data["text_area_input"] == "Some\nText"
  132. assert form_data["debounce_input"] == "bar baz"