test_dynamic_components.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """Integration tests for var operations."""
  2. import time
  3. from typing import Callable, Generator, TypeVar
  4. import pytest
  5. from selenium.webdriver.common.by import By
  6. from reflex.testing import AppHarness
  7. # pyright: reportOptionalMemberAccess=false, reportGeneralTypeIssues=false, reportUnknownMemberType=false
  8. def DynamicComponents():
  9. """App with var operations."""
  10. import reflex as rx
  11. class DynamicComponentsState(rx.State):
  12. value: int = 10
  13. button: rx.Component = rx.button(
  14. "Click me",
  15. custom_attrs={
  16. "id": "button",
  17. },
  18. )
  19. def got_clicked(self):
  20. self.button = rx.button(
  21. "Clicked",
  22. custom_attrs={
  23. "id": "button",
  24. },
  25. )
  26. @rx.var
  27. def client_token_component(self) -> rx.Component:
  28. return rx.vstack(
  29. rx.el.input(
  30. custom_attrs={
  31. "id": "token",
  32. },
  33. value=self.router.session.client_token,
  34. is_read_only=True,
  35. ),
  36. rx.button(
  37. "Update",
  38. custom_attrs={
  39. "id": "update",
  40. },
  41. on_click=DynamicComponentsState.got_clicked,
  42. ),
  43. )
  44. app = rx.App()
  45. def factorial(n: int) -> int:
  46. if n == 0:
  47. return 1
  48. return n * factorial(n - 1)
  49. @app.add_page
  50. def index():
  51. return rx.vstack(
  52. DynamicComponentsState.client_token_component,
  53. DynamicComponentsState.button,
  54. rx.text(
  55. DynamicComponentsState._evaluate(lambda state: factorial(state.value)),
  56. id="factorial",
  57. ),
  58. )
  59. @pytest.fixture(scope="module")
  60. def dynamic_components(tmp_path_factory) -> Generator[AppHarness, None, None]:
  61. """Start VarOperations app at tmp_path via AppHarness.
  62. Args:
  63. tmp_path_factory: pytest tmp_path_factory fixture
  64. Yields:
  65. running AppHarness instance
  66. """
  67. with AppHarness.create(
  68. root=tmp_path_factory.mktemp("dynamic_components"),
  69. app_source=DynamicComponents, # type: ignore
  70. ) as harness:
  71. assert harness.app_instance is not None, "app is not running"
  72. yield harness
  73. T = TypeVar("T")
  74. def poll_for_result(
  75. f: Callable[[], T], exception=Exception, max_attempts=5, seconds_between_attempts=1
  76. ) -> T:
  77. """Poll for a result from a function.
  78. Args:
  79. f: function to call
  80. exception: exception to catch
  81. max_attempts: maximum number of attempts
  82. seconds_between_attempts: seconds to wait between
  83. Returns:
  84. Result of the function
  85. Raises:
  86. AssertionError: if the function does not return a value
  87. """
  88. attempts = 0
  89. while attempts < max_attempts:
  90. try:
  91. return f()
  92. except exception:
  93. attempts += 1
  94. time.sleep(seconds_between_attempts)
  95. raise AssertionError("Function did not return a value")
  96. @pytest.fixture
  97. def driver(dynamic_components: AppHarness):
  98. """Get an instance of the browser open to the dynamic components app.
  99. Args:
  100. dynamic_components: AppHarness for the dynamic components
  101. Yields:
  102. WebDriver instance.
  103. """
  104. driver = dynamic_components.frontend()
  105. try:
  106. token_input = poll_for_result(lambda: driver.find_element(By.ID, "token"))
  107. assert token_input
  108. # wait for the backend connection to send the token
  109. token = dynamic_components.poll_for_value(token_input)
  110. assert token is not None
  111. yield driver
  112. finally:
  113. driver.quit()
  114. def test_dynamic_components(driver, dynamic_components: AppHarness):
  115. """Test that the var operations produce the right results.
  116. Args:
  117. driver: selenium WebDriver open to the app
  118. dynamic_components: AppHarness for the dynamic components
  119. """
  120. button = poll_for_result(lambda: driver.find_element(By.ID, "button"))
  121. assert button
  122. assert button.text == "Click me"
  123. update_button = driver.find_element(By.ID, "update")
  124. assert update_button
  125. update_button.click()
  126. assert (
  127. dynamic_components.poll_for_content(button, exp_not_equal="Click me")
  128. == "Clicked"
  129. )
  130. factorial = poll_for_result(lambda: driver.find_element(By.ID, "factorial"))
  131. assert factorial
  132. assert factorial.text == "3628800"