
* add module prefix to state names * fix state names in test_app * update state names in test_state * fix state names in test_var * fix state name in test_component * fix state names in test_format * fix state names in test_foreach * fix state names in test_cond * fix state names in test_datatable * fix state names in test_colors * fix state names in test_script * fix state names in test_match * fix state name in event1 fixture * fix pyright and darglint * fix state names in state_tree * fix state names in redis only test * fix state names in test_client_storage * fix state name in js template * add `get_state_name` and `get_full_state_name` helpers for `AppHarness` * fix state names in test_dynamic_routes * use new state name helpers in test_client_storage * fix state names in test_event_actions * fix state names in test_event_chain * fix state names in test_upload * fix state name in test_login_flow * fix state names in test_input * fix state names in test_form_submit * ruff * validate state module names * wtf is going on here? * remove comments leftover from refactoring * adjust new test_add_style_embedded_vars * fix state name in state.js * fix integration/test_client_state.py new SessionStorage feature was added with more full state names that need to be formatted in * fix pre-commit issues in test_client_storage.py * adjust test_computed_vars * adjust safe-guards * fix redis tests with new exception state --------- Co-authored-by: Masen Furer <m_github@0x26.net>
155 lines
4.0 KiB
Python
155 lines
4.0 KiB
Python
"""Integration tests for event exception handlers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import Generator, Type
|
|
|
|
import pytest
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.remote.webdriver import WebDriver
|
|
from selenium.webdriver.support import expected_conditions as EC
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
|
|
from reflex.testing import AppHarness
|
|
|
|
|
|
def TestApp():
|
|
"""A test app for event exception handler integration."""
|
|
import reflex as rx
|
|
|
|
class TestAppConfig(rx.Config):
|
|
"""Config for the TestApp app."""
|
|
|
|
pass
|
|
|
|
class TestAppState(rx.State):
|
|
"""State for the TestApp app."""
|
|
|
|
def divide_by_number(self, number: int):
|
|
"""Divide by number and print the result.
|
|
|
|
Args:
|
|
number: number to divide by
|
|
|
|
"""
|
|
print(1 / number)
|
|
|
|
app = rx.App(state=rx.State)
|
|
|
|
@app.add_page
|
|
def index():
|
|
return rx.vstack(
|
|
rx.button(
|
|
"induce_frontend_error",
|
|
on_click=rx.call_script("induce_frontend_error()"),
|
|
id="induce-frontend-error-btn",
|
|
),
|
|
rx.button(
|
|
"induce_backend_error",
|
|
on_click=lambda: TestAppState.divide_by_number(0), # type: ignore
|
|
id="induce-backend-error-btn",
|
|
),
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def test_app(
|
|
app_harness_env: Type[AppHarness], tmp_path_factory
|
|
) -> Generator[AppHarness, None, None]:
|
|
"""Start TestApp app at tmp_path via AppHarness.
|
|
|
|
Args:
|
|
app_harness_env: either AppHarness (dev) or AppHarnessProd (prod)
|
|
tmp_path_factory: pytest tmp_path_factory fixture
|
|
|
|
Yields:
|
|
running AppHarness instance
|
|
|
|
"""
|
|
with app_harness_env.create(
|
|
root=tmp_path_factory.mktemp("test_app"),
|
|
app_name=f"testapp_{app_harness_env.__name__.lower()}",
|
|
app_source=TestApp, # type: ignore
|
|
) as harness:
|
|
yield harness
|
|
|
|
|
|
@pytest.fixture
|
|
def driver(test_app: AppHarness) -> Generator[WebDriver, None, None]:
|
|
"""Get an instance of the browser open to the test_app app.
|
|
|
|
Args:
|
|
test_app: harness for TestApp app
|
|
|
|
Yields:
|
|
WebDriver instance.
|
|
|
|
"""
|
|
assert test_app.app_instance is not None, "app is not running"
|
|
driver = test_app.frontend()
|
|
try:
|
|
yield driver
|
|
finally:
|
|
driver.quit()
|
|
|
|
|
|
def test_frontend_exception_handler_during_runtime(
|
|
driver: WebDriver,
|
|
capsys,
|
|
):
|
|
"""Test calling frontend exception handler during runtime.
|
|
|
|
We send an event containing a call to a non-existent function in the frontend.
|
|
This should trigger the default frontend exception handler.
|
|
|
|
Args:
|
|
driver: WebDriver instance.
|
|
capsys: pytest fixture for capturing stdout and stderr.
|
|
|
|
"""
|
|
reset_button = WebDriverWait(driver, 20).until(
|
|
EC.element_to_be_clickable((By.ID, "induce-frontend-error-btn"))
|
|
)
|
|
|
|
reset_button.click()
|
|
|
|
# Wait for the error to be logged
|
|
time.sleep(2)
|
|
|
|
captured_default_handler_output = capsys.readouterr()
|
|
assert (
|
|
"induce_frontend_error" in captured_default_handler_output.out
|
|
and "ReferenceError" in captured_default_handler_output.out
|
|
)
|
|
|
|
|
|
def test_backend_exception_handler_during_runtime(
|
|
driver: WebDriver,
|
|
capsys,
|
|
):
|
|
"""Test calling backend exception handler during runtime.
|
|
|
|
We invoke TestAppState.divide_by_zero to induce backend error.
|
|
This should trigger the default backend exception handler.
|
|
|
|
Args:
|
|
driver: WebDriver instance.
|
|
capsys: pytest fixture for capturing stdout and stderr.
|
|
|
|
"""
|
|
reset_button = WebDriverWait(driver, 20).until(
|
|
EC.element_to_be_clickable((By.ID, "induce-backend-error-btn"))
|
|
)
|
|
|
|
reset_button.click()
|
|
|
|
# Wait for the error to be logged
|
|
time.sleep(2)
|
|
|
|
captured_default_handler_output = capsys.readouterr()
|
|
assert (
|
|
"divide_by_number" in captured_default_handler_output.out
|
|
and "ZeroDivisionError" in captured_default_handler_output.out
|
|
)
|