test_dynamic_components.py 4.5 KB

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