reflex/tests/integration/test_exception_handlers.py
Thomas Brandého 42e6dfa40d
enable PGH, bump pyright and fix all #type: ignore (#4699)
* enable PGH, bump pyright and fix  all #type: ignore

* relock poetry file

* ignore incompatible override

* fix varop tests

* ignore missing imports

* fix

* fix stuff

* fix tests

* rechange tests

* relock with poetry 2.0
2025-01-28 13:11:05 -08:00

205 lines
5.6 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, AppHarnessProd
pytestmark = [pytest.mark.ignore_console_error]
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."""
react_error: bool = False
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), # pyright: ignore [reportCallIssue]
id="induce-backend-error-btn",
),
rx.button(
"induce_react_error",
on_click=TestAppState.set_react_error(True), # pyright: ignore [reportAttributeAccessIssue]
id="induce-react-error-btn",
),
rx.box(
rx.cond(
TestAppState.react_error,
rx.Var.create({"invalid": "cannot have object as child"}),
"",
),
),
)
@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,
) 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
)
def test_frontend_exception_handler_with_react(
test_app: AppHarness,
driver: WebDriver,
capsys,
):
"""Test calling frontend exception handler during runtime.
Render an object as a react child, which is invalid.
Args:
test_app: harness for TestApp app
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-react-error-btn"))
)
reset_button.click()
# Wait for the error to be logged
time.sleep(2)
captured_default_handler_output = capsys.readouterr()
if isinstance(test_app, AppHarnessProd):
assert "Error: Minified React error #31" in captured_default_handler_output.out
else:
assert (
"Error: Objects are not valid as a React child (found: object with keys \n{invalid})"
in captured_default_handler_output.out
)