reflex/tests/integration/test_dynamic_components.py
2024-11-07 14:08:37 -08:00

170 lines
4.5 KiB
Python

"""Integration tests for var operations."""
import time
from typing import Callable, Generator, 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",
},
)
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"