test_computed_vars.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. """Test computed vars."""
  2. from __future__ import annotations
  3. import time
  4. from typing import Generator
  5. import pytest
  6. from selenium.webdriver.common.by import By
  7. from reflex.testing import DEFAULT_TIMEOUT, AppHarness, WebDriver
  8. def ComputedVars():
  9. """Test app for computed vars."""
  10. import reflex as rx
  11. class State(rx.State):
  12. count: int = 0
  13. # cached var with dep on count
  14. @rx.cached_var(interval=15)
  15. def count1(self) -> int:
  16. return self.count
  17. # same as above, different notation
  18. @rx.var(interval=15, cache=True)
  19. def count2(self) -> int:
  20. return self.count
  21. # explicit disabled auto_deps
  22. @rx.var(interval=15, cache=True, auto_deps=False)
  23. def count3(self) -> int:
  24. # this will not add deps, because auto_deps is False
  25. print(self.count1)
  26. print(self.count2)
  27. return self.count
  28. # explicit dependency on count1 var
  29. @rx.cached_var(deps=[count1], auto_deps=False)
  30. def depends_on_count1(self) -> int:
  31. return self.count
  32. @rx.var(deps=[count3], auto_deps=False, cache=True)
  33. def depends_on_count3(self) -> int:
  34. return self.count
  35. def increment(self):
  36. self.count += 1
  37. def mark_dirty(self):
  38. self._mark_dirty()
  39. def index() -> rx.Component:
  40. return rx.center(
  41. rx.vstack(
  42. rx.input(
  43. id="token",
  44. value=State.router.session.client_token,
  45. is_read_only=True,
  46. ),
  47. rx.button("Increment", on_click=State.increment, id="increment"),
  48. rx.button("Do nothing", on_click=State.mark_dirty, id="mark_dirty"),
  49. rx.text("count:"),
  50. rx.text(State.count, id="count"),
  51. rx.text("count1:"),
  52. rx.text(State.count1, id="count1"),
  53. rx.text("count2:"),
  54. rx.text(State.count2, id="count2"),
  55. rx.text("count3:"),
  56. rx.text(State.count3, id="count3"),
  57. rx.text("depends_on_count1:"),
  58. rx.text(
  59. State.depends_on_count1,
  60. id="depends_on_count1",
  61. ),
  62. rx.text("depends_on_count3:"),
  63. rx.text(
  64. State.depends_on_count3,
  65. id="depends_on_count3",
  66. ),
  67. ),
  68. )
  69. # raise Exception(State.count3._deps(objclass=State))
  70. app = rx.App()
  71. app.add_page(index)
  72. @pytest.fixture(scope="module")
  73. def computed_vars(
  74. tmp_path_factory,
  75. ) -> Generator[AppHarness, None, None]:
  76. """Start ComputedVars app at tmp_path via AppHarness.
  77. Args:
  78. tmp_path_factory: pytest tmp_path_factory fixture
  79. Yields:
  80. running AppHarness instance
  81. """
  82. with AppHarness.create(
  83. root=tmp_path_factory.mktemp(f"computed_vars"),
  84. app_source=ComputedVars, # type: ignore
  85. ) as harness:
  86. yield harness
  87. @pytest.fixture
  88. def driver(computed_vars: AppHarness) -> Generator[WebDriver, None, None]:
  89. """Get an instance of the browser open to the computed_vars app.
  90. Args:
  91. computed_vars: harness for ComputedVars app
  92. Yields:
  93. WebDriver instance.
  94. """
  95. assert computed_vars.app_instance is not None, "app is not running"
  96. driver = computed_vars.frontend()
  97. try:
  98. yield driver
  99. finally:
  100. driver.quit()
  101. @pytest.fixture()
  102. def token(computed_vars: AppHarness, driver: WebDriver) -> str:
  103. """Get a function that returns the active token.
  104. Args:
  105. computed_vars: harness for ComputedVars app.
  106. driver: WebDriver instance.
  107. Returns:
  108. The token for the connected client
  109. """
  110. assert computed_vars.app_instance is not None
  111. token_input = driver.find_element(By.ID, "token")
  112. assert token_input
  113. # wait for the backend connection to send the token
  114. token = computed_vars.poll_for_value(token_input, timeout=DEFAULT_TIMEOUT * 2)
  115. assert token is not None
  116. return token
  117. def test_computed_vars(
  118. computed_vars: AppHarness,
  119. driver: WebDriver,
  120. token: str,
  121. ):
  122. """Test that computed vars are working as expected.
  123. Args:
  124. computed_vars: harness for ComputedVars app.
  125. driver: WebDriver instance.
  126. token: The token for the connected client.
  127. """
  128. assert computed_vars.app_instance is not None
  129. count = driver.find_element(By.ID, "count")
  130. assert count
  131. assert count.text == "0"
  132. count1 = driver.find_element(By.ID, "count1")
  133. assert count1
  134. assert count1.text == "0"
  135. count2 = driver.find_element(By.ID, "count2")
  136. assert count2
  137. assert count2.text == "0"
  138. count3 = driver.find_element(By.ID, "count3")
  139. assert count3
  140. assert count3.text == "0"
  141. depends_on_count1 = driver.find_element(By.ID, "depends_on_count1")
  142. assert depends_on_count1
  143. assert depends_on_count1.text == "0"
  144. depends_on_count3 = driver.find_element(By.ID, "depends_on_count3")
  145. assert depends_on_count3
  146. assert depends_on_count3.text == "0"
  147. increment = driver.find_element(By.ID, "increment")
  148. assert increment.is_enabled()
  149. mark_dirty = driver.find_element(By.ID, "mark_dirty")
  150. assert mark_dirty.is_enabled()
  151. mark_dirty.click()
  152. increment.click()
  153. assert computed_vars.poll_for_content(count, timeout=2, exp_not_equal="0") == "1"
  154. assert computed_vars.poll_for_content(count1, timeout=2, exp_not_equal="0") == "1"
  155. assert computed_vars.poll_for_content(count2, timeout=2, exp_not_equal="0") == "1"
  156. mark_dirty.click()
  157. with pytest.raises(TimeoutError):
  158. computed_vars.poll_for_content(count3, timeout=5, exp_not_equal="0")
  159. time.sleep(10)
  160. assert count3.text == "0"
  161. assert depends_on_count3.text == "0"
  162. mark_dirty.click()
  163. assert computed_vars.poll_for_content(count3, timeout=2, exp_not_equal="0") == "1"
  164. assert depends_on_count3.text == "1"