"""Integration tests for var operations.""" import time from collections.abc import Callable, Generator from typing import TypeVar import pytest from selenium.webdriver.common.by import By from reflex.testing import AppHarness # pyright: reportOptionalMemberAccess=false, reportGeneralTypeIssues=false, reportUnknownMemberType=false def DynamicComponents(): """App with var operations.""" import reflex as rx class DynamicComponentsState(rx.State): value: int = 10 button: rx.Component = rx.button( "Click me", custom_attrs={ "id": "button", }, ) @rx.event def got_clicked(self): self.button = rx.button( "Clicked", custom_attrs={ "id": "button", }, ) @rx.var def client_token_component(self) -> rx.Component: return rx.vstack( rx.el.input( custom_attrs={ "id": "token", }, value=self.router.session.client_token, is_read_only=True, ), rx.button( "Update", custom_attrs={ "id": "update", }, on_click=DynamicComponentsState.got_clicked, ), ) app = rx.App() def factorial(n: int) -> int: if n == 0: return 1 return n * factorial(n - 1) @app.add_page def index(): return rx.vstack( DynamicComponentsState.client_token_component, DynamicComponentsState.button, rx.text( DynamicComponentsState._evaluate( lambda state: factorial(state.value), of_type=int ), id="factorial", ), ) @pytest.fixture(scope="module") def dynamic_components(tmp_path_factory) -> Generator[AppHarness, None, None]: """Start VarOperations app at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture Yields: running AppHarness instance """ with AppHarness.create( root=tmp_path_factory.mktemp("dynamic_components"), app_source=DynamicComponents, ) as harness: assert harness.app_instance is not None, "app is not running" yield harness T = TypeVar("T") def poll_for_result( f: Callable[[], T], exception=Exception, max_attempts=5, seconds_between_attempts=1 ) -> T: """Poll for a result from a function. Args: f: function to call exception: exception to catch max_attempts: maximum number of attempts seconds_between_attempts: seconds to wait between Returns: Result of the function Raises: AssertionError: if the function does not return a value """ attempts = 0 while attempts < max_attempts: try: return f() except exception: attempts += 1 time.sleep(seconds_between_attempts) raise AssertionError("Function did not return a value") @pytest.fixture def driver(dynamic_components: AppHarness): """Get an instance of the browser open to the dynamic components app. Args: dynamic_components: AppHarness for the dynamic components Yields: WebDriver instance. """ driver = dynamic_components.frontend() try: token_input = poll_for_result(lambda: driver.find_element(By.ID, "token")) assert token_input # wait for the backend connection to send the token token = dynamic_components.poll_for_value(token_input) assert token is not None yield driver finally: driver.quit() def test_dynamic_components(driver, dynamic_components: AppHarness): """Test that the var operations produce the right results. Args: driver: selenium WebDriver open to the app dynamic_components: AppHarness for the dynamic components """ button = poll_for_result(lambda: driver.find_element(By.ID, "button")) assert button assert button.text == "Click me" update_button = driver.find_element(By.ID, "update") assert update_button update_button.click() assert ( dynamic_components.poll_for_content(button, exp_not_equal="Click me") == "Clicked" ) factorial = poll_for_result(lambda: driver.find_element(By.ID, "factorial")) assert factorial assert factorial.text == "3628800"