element."""
19 |
20 | tag = "Heading"
21 |
22 | # Change the default rendered element for the one passed as a child, merging their props and behavior.
23 | as_child: Var[bool]
24 |
25 | # Change the default rendered element into a semantically appropriate alternative (cannot be used with asChild)
26 | as_: Var[str]
27 |
28 | # Text size: "1" - "9"
29 | size: Var[Responsive[LiteralTextSize]]
30 |
31 | # Thickness of text: "light" | "regular" | "medium" | "bold"
32 | weight: Var[Responsive[LiteralTextWeight]]
33 |
34 | # Alignment of text in element: "left" | "center" | "right"
35 | align: Var[Responsive[LiteralTextAlign]]
36 |
37 | # Removes the leading trim space: "normal" | "start" | "end" | "both"
38 | trim: Var[Responsive[LiteralTextTrim]]
39 |
40 | # Overrides the accent color inherited from the Theme.
41 | color_scheme: Var[LiteralAccentColor]
42 |
43 | # Whether to render the text with higher contrast color
44 | high_contrast: Var[bool]
45 |
46 |
47 | heading = Heading.create
48 |
--------------------------------------------------------------------------------
/reflex/components/react_player/__init__.py:
--------------------------------------------------------------------------------
1 | """React Player component for audio and video."""
2 |
3 | from . import react_player
4 | from .audio import Audio
5 | from .video import Video
6 |
7 | audio = Audio.create
8 | video = Video.create
9 |
--------------------------------------------------------------------------------
/reflex/components/react_player/audio.py:
--------------------------------------------------------------------------------
1 | """A audio component."""
2 |
3 | from reflex.components.react_player.react_player import ReactPlayer
4 |
5 |
6 | class Audio(ReactPlayer):
7 | """Audio component share with Video component."""
8 |
--------------------------------------------------------------------------------
/reflex/components/react_player/video.py:
--------------------------------------------------------------------------------
1 | """A video component."""
2 |
3 | from reflex.components.react_player.react_player import ReactPlayer
4 |
5 |
6 | class Video(ReactPlayer):
7 | """Video component share with audio component."""
8 |
--------------------------------------------------------------------------------
/reflex/components/sonner/__init__.py:
--------------------------------------------------------------------------------
1 | """Init file for the sonner component."""
2 |
3 | from .toast import toast
4 |
--------------------------------------------------------------------------------
/reflex/components/suneditor/__init__.py:
--------------------------------------------------------------------------------
1 | """Editor component."""
2 |
3 | from .editor import Editor, EditorButtonList, EditorOptions
4 |
5 | editor = Editor.create
6 |
--------------------------------------------------------------------------------
/reflex/components/tags/__init__.py:
--------------------------------------------------------------------------------
1 | """Representations for React tags."""
2 |
3 | from .cond_tag import CondTag
4 | from .iter_tag import IterTag
5 | from .match_tag import MatchTag
6 | from .tag import Tag
7 |
--------------------------------------------------------------------------------
/reflex/components/tags/cond_tag.py:
--------------------------------------------------------------------------------
1 | """Tag to conditionally render components."""
2 |
3 | import dataclasses
4 | from typing import Any
5 |
6 | from reflex.components.tags.tag import Tag
7 | from reflex.vars.base import Var
8 |
9 |
10 | @dataclasses.dataclass()
11 | class CondTag(Tag):
12 | """A conditional tag."""
13 |
14 | # The condition to determine which component to render.
15 | cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True))
16 |
17 | # The code to render if the condition is true.
18 | true_value: dict = dataclasses.field(default_factory=dict)
19 |
20 | # The code to render if the condition is false.
21 | false_value: dict | None = None
22 |
--------------------------------------------------------------------------------
/reflex/components/tags/match_tag.py:
--------------------------------------------------------------------------------
1 | """Tag to conditionally match cases."""
2 |
3 | import dataclasses
4 | from typing import Any
5 |
6 | from reflex.components.tags.tag import Tag
7 | from reflex.vars.base import Var
8 |
9 |
10 | @dataclasses.dataclass()
11 | class MatchTag(Tag):
12 | """A match tag."""
13 |
14 | # The condition to determine which case to match.
15 | cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True))
16 |
17 | # The list of match cases to be matched.
18 | match_cases: list[Any] = dataclasses.field(default_factory=list)
19 |
20 | # The catchall case to match.
21 | default: Any = dataclasses.field(default=Var.create(None))
22 |
--------------------------------------------------------------------------------
/reflex/components/tags/tagless.py:
--------------------------------------------------------------------------------
1 | """A tag with no tag."""
2 |
3 | from reflex.components.tags import Tag
4 | from reflex.utils import format
5 |
6 |
7 | class Tagless(Tag):
8 | """A tag with no tag."""
9 |
10 | def __str__(self) -> str:
11 | """Return the string representation of the tag.
12 |
13 | Returns:
14 | The string representation of the tag.
15 | """
16 | out = self.contents
17 | space = format.wrap(" ", "{")
18 | if len(self.contents) > 0 and self.contents[0] == " ":
19 | out = space + out
20 | if len(self.contents) > 0 and self.contents[-1] == " ":
21 | out = out + space
22 | return out
23 |
--------------------------------------------------------------------------------
/reflex/constants/config.py:
--------------------------------------------------------------------------------
1 | """Config constants."""
2 |
3 | from pathlib import Path
4 | from types import SimpleNamespace
5 |
6 | from reflex.constants.base import Dirs, Reflex
7 |
8 | from .compiler import Ext
9 |
10 | # Alembic migrations
11 | ALEMBIC_CONFIG = "alembic.ini"
12 |
13 |
14 | class Config(SimpleNamespace):
15 | """Config constants."""
16 |
17 | # The name of the reflex config module.
18 | MODULE = "rxconfig"
19 | # The python config file.
20 | FILE = Path(f"{MODULE}{Ext.PY}")
21 |
22 |
23 | class Expiration(SimpleNamespace):
24 | """Expiration constants."""
25 |
26 | # Token expiration time in seconds
27 | TOKEN = 60 * 60
28 | # Maximum time in milliseconds that a state can be locked for exclusive access.
29 | LOCK = 10000
30 | # The PING timeout
31 | PING = 120
32 | # The maximum time in milliseconds to hold a lock before throwing a warning.
33 | LOCK_WARNING_THRESHOLD = 1000
34 |
35 |
36 | class GitIgnore(SimpleNamespace):
37 | """Gitignore constants."""
38 |
39 | # The gitignore file.
40 | FILE = Path(".gitignore")
41 | # Files to gitignore.
42 | DEFAULTS = {
43 | Dirs.WEB,
44 | Dirs.STATES,
45 | "*.db",
46 | "__pycache__/",
47 | "*.py[cod]",
48 | "assets/external/",
49 | }
50 |
51 |
52 | class PyprojectToml(SimpleNamespace):
53 | """Pyproject.toml constants."""
54 |
55 | # The pyproject.toml file.
56 | FILE = "pyproject.toml"
57 |
58 |
59 | class RequirementsTxt(SimpleNamespace):
60 | """Requirements.txt constants."""
61 |
62 | # The requirements.txt file.
63 | FILE = "requirements.txt"
64 | # The partial text used to form requirement that pins a reflex version
65 | DEFAULTS_STUB = f"{Reflex.MODULE_NAME}=="
66 |
67 |
68 | class DefaultPorts(SimpleNamespace):
69 | """Default port constants."""
70 |
71 | FRONTEND_PORT = 3000
72 | BACKEND_PORT = 8000
73 |
74 |
75 | # The deployment URL.
76 | PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
77 |
--------------------------------------------------------------------------------
/reflex/constants/custom_components.py:
--------------------------------------------------------------------------------
1 | """Constants for the custom components."""
2 |
3 | from __future__ import annotations
4 |
5 | from pathlib import Path
6 | from types import SimpleNamespace
7 |
8 |
9 | class CustomComponents(SimpleNamespace):
10 | """Constants for the custom components."""
11 |
12 | # The name of the custom components source directory.
13 | SRC_DIR = Path("custom_components")
14 | # The name of the custom components pyproject.toml file.
15 | PYPROJECT_TOML = Path("pyproject.toml")
16 | # The name of the custom components package README file.
17 | PACKAGE_README = Path("README.md")
18 | # The name of the custom components package .gitignore file.
19 | PACKAGE_GITIGNORE = ".gitignore"
20 | # The name of the distribution directory as result of a build.
21 | DIST_DIR = "dist"
22 | # The name of the init file.
23 | INIT_FILE = "__init__.py"
24 | # Suffixes for the distribution files.
25 | DISTRIBUTION_FILE_SUFFIXES = [".tar.gz", ".whl"]
26 | # The name to the URL of python package repositories.
27 | REPO_URLS = {
28 | # Note: the trailing slash is required for below URLs.
29 | "pypi": "https://upload.pypi.org/legacy/",
30 | "testpypi": "https://test.pypi.org/legacy/",
31 | }
32 | # The .gitignore file for the custom component project.
33 | FILE = Path(".gitignore")
34 | # Files to gitignore.
35 | DEFAULTS = {"__pycache__/", "*.py[cod]", "*.egg-info/", "dist/"}
36 |
--------------------------------------------------------------------------------
/reflex/constants/state.py:
--------------------------------------------------------------------------------
1 | """State-related constants."""
2 |
3 | from enum import Enum
4 |
5 |
6 | class StateManagerMode(str, Enum):
7 | """State manager constants."""
8 |
9 | DISK = "disk"
10 | MEMORY = "memory"
11 | REDIS = "redis"
12 |
13 |
14 | # Used for things like console_log, etc.
15 | FRONTEND_EVENT_STATE = "__reflex_internal_frontend_event_state"
16 |
--------------------------------------------------------------------------------
/reflex/constants/utils.py:
--------------------------------------------------------------------------------
1 | """Utility functions for constants."""
2 |
3 | from collections.abc import Callable
4 | from typing import Any, Generic, TypeVar
5 |
6 | T = TypeVar("T")
7 | V = TypeVar("V")
8 |
9 |
10 | class classproperty(Generic[T, V]):
11 | """A class property decorator."""
12 |
13 | def __init__(self, getter: Callable[[type[T]], V]) -> None:
14 | """Initialize the class property.
15 |
16 | Args:
17 | getter: The getter function.
18 | """
19 | self.getter = getattr(getter, "__func__", getter)
20 |
21 | def __get__(self, instance: Any, owner: type[T]) -> V:
22 | """Get the value of the class property.
23 |
24 | Args:
25 | instance: The instance of the class.
26 | owner: The class itself.
27 |
28 | Returns:
29 | The value of the class property.
30 | """
31 | return self.getter(owner)
32 |
--------------------------------------------------------------------------------
/reflex/custom_components/__init__.py:
--------------------------------------------------------------------------------
1 | """The Reflex custom components."""
2 |
--------------------------------------------------------------------------------
/reflex/experimental/__init__.py:
--------------------------------------------------------------------------------
1 | """Namespace for experimental features."""
2 |
3 | from types import SimpleNamespace
4 |
5 | from reflex.components.datadisplay.shiki_code_block import code_block as code_block
6 | from reflex.utils.console import warn
7 | from reflex.utils.misc import run_in_thread
8 |
9 | from . import hooks as hooks
10 | from .client_state import ClientStateVar as ClientStateVar
11 |
12 |
13 | class ExperimentalNamespace(SimpleNamespace):
14 | """Namespace for experimental features."""
15 |
16 | def __getattribute__(self, item: str):
17 | """Get attribute from the namespace.
18 |
19 | Args:
20 | item: attribute name.
21 |
22 | Returns:
23 | The attribute.
24 | """
25 | warn(
26 | "`rx._x` contains experimental features and might be removed at any time in the future.",
27 | dedupe=True,
28 | )
29 | return super().__getattribute__(item)
30 |
31 | @property
32 | def run_in_thread(self):
33 | """Temporary property returning the run_in_thread helper function.
34 |
35 | Remove this property when run_in_thread is fully promoted.
36 |
37 | Returns:
38 | The run_in_thread helper function.
39 | """
40 | self.register_component_warning("run_in_thread")
41 | return run_in_thread
42 |
43 | @staticmethod
44 | def register_component_warning(component_name: str):
45 | """Add component to emitted warnings and throw a warning if it
46 | doesn't exist.
47 |
48 | Args:
49 | component_name: name of the component.
50 | """
51 | warn(
52 | f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.",
53 | dedupe=True,
54 | )
55 |
56 |
57 | _x = ExperimentalNamespace(
58 | client_state=ClientStateVar.create,
59 | hooks=hooks,
60 | code_block=code_block,
61 | )
62 |
--------------------------------------------------------------------------------
/reflex/istate/__init__.py:
--------------------------------------------------------------------------------
1 | """This module will provide interfaces for the state."""
2 |
--------------------------------------------------------------------------------
/reflex/istate/dynamic.py:
--------------------------------------------------------------------------------
1 | """A container for dynamically generated states."""
2 |
3 | # This page intentionally left blank.
4 |
--------------------------------------------------------------------------------
/reflex/istate/wrappers.py:
--------------------------------------------------------------------------------
1 | """Wrappers for the state manager."""
2 |
3 | from typing import Any
4 |
5 | from reflex.istate.proxy import ReadOnlyStateProxy
6 | from reflex.state import _split_substate_key, _substate_key, get_state_manager
7 |
8 |
9 | async def get_state(token: str, state_cls: Any | None = None) -> ReadOnlyStateProxy:
10 | """Get the instance of a state for a token.
11 |
12 | Args:
13 | token: The token for the state.
14 | state_cls: The class of the state.
15 |
16 | Returns:
17 | A read-only proxy of the state instance.
18 | """
19 | mng = get_state_manager()
20 | if state_cls is not None:
21 | root_state = await mng.get_state(_substate_key(token, state_cls))
22 | else:
23 | root_state = await mng.get_state(token)
24 | _, state_path = _split_substate_key(token)
25 | state_cls = root_state.get_class_substate(tuple(state_path.split(".")))
26 | instance = await root_state.get_state(state_cls)
27 | return ReadOnlyStateProxy(instance)
28 |
--------------------------------------------------------------------------------
/reflex/middleware/__init__.py:
--------------------------------------------------------------------------------
1 | """Reflex middleware."""
2 |
3 | from .hydrate_middleware import HydrateMiddleware
4 | from .middleware import Middleware
5 |
--------------------------------------------------------------------------------
/reflex/middleware/hydrate_middleware.py:
--------------------------------------------------------------------------------
1 | """Middleware to hydrate the state."""
2 |
3 | from __future__ import annotations
4 |
5 | import dataclasses
6 | from typing import TYPE_CHECKING
7 |
8 | from reflex import constants
9 | from reflex.event import Event, get_hydrate_event
10 | from reflex.middleware.middleware import Middleware
11 | from reflex.state import BaseState, StateUpdate, _resolve_delta
12 |
13 | if TYPE_CHECKING:
14 | from reflex.app import App
15 |
16 |
17 | @dataclasses.dataclass(init=True)
18 | class HydrateMiddleware(Middleware):
19 | """Middleware to handle initial app hydration."""
20 |
21 | async def preprocess(
22 | self, app: App, state: BaseState, event: Event
23 | ) -> StateUpdate | None:
24 | """Preprocess the event.
25 |
26 | Args:
27 | app: The app to apply the middleware to.
28 | state: The client state.
29 | event: The event to preprocess.
30 |
31 | Returns:
32 | An optional delta or list of state updates to return.
33 | """
34 | # If this is not the hydrate event, return None
35 | if event.name != get_hydrate_event(state):
36 | return None
37 |
38 | # Clear client storage, to respect clearing cookies
39 | state._reset_client_storage()
40 |
41 | # Mark state as not hydrated (until on_loads are complete)
42 | setattr(state, constants.CompileVars.IS_HYDRATED, False)
43 |
44 | # Get the initial state.
45 | delta = await _resolve_delta(state.dict())
46 | # since a full dict was captured, clean any dirtiness
47 | state._clean()
48 |
49 | # Return the state update.
50 | return StateUpdate(delta=delta, events=[])
51 |
--------------------------------------------------------------------------------
/reflex/middleware/middleware.py:
--------------------------------------------------------------------------------
1 | """Base Reflex middleware."""
2 |
3 | from __future__ import annotations
4 |
5 | from abc import ABC, abstractmethod
6 | from typing import TYPE_CHECKING
7 |
8 | from reflex.event import Event
9 | from reflex.state import BaseState, StateUpdate
10 |
11 | if TYPE_CHECKING:
12 | from reflex.app import App
13 |
14 |
15 | class Middleware(ABC):
16 | """Middleware to preprocess and postprocess requests."""
17 |
18 | @abstractmethod
19 | async def preprocess(
20 | self, app: App, state: BaseState, event: Event
21 | ) -> StateUpdate | None:
22 | """Preprocess the event.
23 |
24 | Args:
25 | app: The app.
26 | state: The client state.
27 | event: The event to preprocess.
28 |
29 | Returns:
30 | An optional state update to return.
31 | """
32 | return None
33 |
34 | async def postprocess(
35 | self, app: App, state: BaseState, event: Event, update: StateUpdate
36 | ) -> StateUpdate:
37 | """Postprocess the event.
38 |
39 | Args:
40 | app: The app.
41 | state: The client state.
42 | event: The event to postprocess.
43 | update: The current state update.
44 |
45 | Returns:
46 | An optional state to return.
47 | """
48 | return update
49 |
--------------------------------------------------------------------------------
/reflex/plugins/__init__.py:
--------------------------------------------------------------------------------
1 | """Reflex Plugin System."""
2 |
3 | from .base import CommonContext as CommonContext
4 | from .base import Plugin as Plugin
5 | from .base import PreCompileContext as PreCompileContext
6 | from .tailwind_v3 import Plugin as TailwindV3Plugin
7 | from .tailwind_v4 import Plugin as TailwindV4Plugin
8 |
--------------------------------------------------------------------------------
/reflex/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/reflex/py.typed
--------------------------------------------------------------------------------
/reflex/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """Reflex utilities."""
2 |
--------------------------------------------------------------------------------
/reflex/utils/decorator.py:
--------------------------------------------------------------------------------
1 | """Decorator utilities."""
2 |
3 | import functools
4 | from collections.abc import Callable
5 | from typing import ParamSpec, TypeVar
6 |
7 | T = TypeVar("T")
8 |
9 |
10 | def once(f: Callable[[], T]) -> Callable[[], T]:
11 | """A decorator that calls the function once and caches the result.
12 |
13 | Args:
14 | f: The function to call.
15 |
16 | Returns:
17 | A function that calls the function once and caches the result.
18 | """
19 | unset = object()
20 | value: object | T = unset
21 |
22 | @functools.wraps(f)
23 | def wrapper() -> T:
24 | nonlocal value
25 | value = f() if value is unset else value
26 | return value # pyright: ignore[reportReturnType]
27 |
28 | return wrapper
29 |
30 |
31 | def once_unless_none(f: Callable[[], T | None]) -> Callable[[], T | None]:
32 | """A decorator that calls the function once and caches the result unless it is None.
33 |
34 | Args:
35 | f: The function to call.
36 |
37 | Returns:
38 | A function that calls the function once and caches the result unless it is None.
39 | """
40 | value: T | None = None
41 |
42 | @functools.wraps(f)
43 | def wrapper() -> T | None:
44 | nonlocal value
45 | value = f() if value is None else value
46 | return value
47 |
48 | return wrapper
49 |
50 |
51 | P = ParamSpec("P")
52 |
53 |
54 | def debug(f: Callable[P, T]) -> Callable[P, T]:
55 | """A decorator that prints the function name, arguments, and result.
56 |
57 | Args:
58 | f: The function to call.
59 |
60 | Returns:
61 | A function that prints the function name, arguments, and result.
62 | """
63 |
64 | @functools.wraps(f)
65 | def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
66 | result = f(*args, **kwargs)
67 | print( # noqa: T201
68 | f"Calling {f.__name__} with args: {args} and kwargs: {kwargs}, result: {result}"
69 | )
70 | return result
71 |
72 | return wrapper
73 |
--------------------------------------------------------------------------------
/reflex/utils/misc.py:
--------------------------------------------------------------------------------
1 | """Miscellaneous functions for the experimental package."""
2 |
3 | import asyncio
4 | from collections.abc import Callable
5 | from typing import Any
6 |
7 |
8 | async def run_in_thread(func: Callable) -> Any:
9 | """Run a function in a separate thread.
10 |
11 | To not block the UI event queue, run_in_thread must be inside inside a rx.event(background=True) decorated method.
12 |
13 | Args:
14 | func: The non-async function to run.
15 |
16 | Raises:
17 | ValueError: If the function is an async function.
18 |
19 | Returns:
20 | Any: The return value of the function.
21 | """
22 | if asyncio.coroutines.iscoroutinefunction(func):
23 | msg = "func must be a non-async function"
24 | raise ValueError(msg)
25 | return await asyncio.get_event_loop().run_in_executor(None, func)
26 |
--------------------------------------------------------------------------------
/reflex/utils/redir.py:
--------------------------------------------------------------------------------
1 | """Utilities to handle redirection to browser UI."""
2 |
3 | import time
4 | import webbrowser
5 |
6 | import httpx
7 |
8 | from reflex import constants
9 | from reflex.utils import net
10 |
11 | from . import console
12 |
13 |
14 | def open_browser(target_url: str) -> None:
15 | """Open a browser window to target_url.
16 |
17 | Args:
18 | target_url: The URL to open in the browser.
19 | """
20 | if not webbrowser.open(target_url):
21 | console.warn(
22 | f"Unable to automatically open the browser. Please navigate to {target_url} in your browser."
23 | )
24 | else:
25 | console.info(f"Opening browser to {target_url}.")
26 |
27 |
28 | def open_browser_and_wait(
29 | target_url: str, poll_url: str, interval: int = 2
30 | ) -> httpx.Response:
31 | """Open a browser window to target_url and request poll_url until it returns successfully.
32 |
33 | Args:
34 | target_url: The URL to open in the browser.
35 | poll_url: The URL to poll for success.
36 | interval: The interval in seconds to wait between polling.
37 |
38 | Returns:
39 | The response from the poll_url.
40 | """
41 | open_browser(target_url)
42 | console.info("[b]Complete the workflow in the browser to continue.[/b]")
43 | while True:
44 | try:
45 | response = net.get(poll_url, follow_redirects=True)
46 | if response.is_success:
47 | break
48 | except httpx.RequestError as err:
49 | console.info(f"Will retry after error occurred while polling: {err}.")
50 | time.sleep(interval)
51 | return response
52 |
53 |
54 | def reflex_build_redirect() -> None:
55 | """Open the browser window to reflex.build."""
56 | open_browser(constants.Templates.REFLEX_BUILD_FRONTEND)
57 |
58 |
59 | def reflex_templates():
60 | """Open the browser window to reflex.build/templates."""
61 | open_browser(constants.Templates.REFLEX_TEMPLATES_URL)
62 |
--------------------------------------------------------------------------------
/reflex/utils/registry.py:
--------------------------------------------------------------------------------
1 | """Utilities for working with registries."""
2 |
3 | import httpx
4 |
5 | from reflex.environment import environment
6 | from reflex.utils import console, net
7 | from reflex.utils.decorator import once
8 |
9 |
10 | def latency(registry: str) -> int:
11 | """Get the latency of a registry.
12 |
13 | Args:
14 | registry (str): The URL of the registry.
15 |
16 | Returns:
17 | int: The latency of the registry in microseconds.
18 | """
19 | try:
20 | time_to_respond = net.get(registry, timeout=2).elapsed.microseconds
21 | except httpx.HTTPError:
22 | console.info(f"Failed to connect to {registry}.")
23 | return 10_000_000
24 | else:
25 | console.debug(f"Latency of {registry}: {time_to_respond}")
26 | return time_to_respond
27 |
28 |
29 | def average_latency(registry: str, attempts: int = 3) -> int:
30 | """Get the average latency of a registry.
31 |
32 | Args:
33 | registry: The URL of the registry.
34 | attempts: The number of attempts to make. Defaults to 10.
35 |
36 | Returns:
37 | The average latency of the registry in microseconds.
38 | """
39 | registry_latency = sum(latency(registry) for _ in range(attempts)) // attempts
40 | console.debug(f"Average latency of {registry}: {registry_latency}")
41 | return registry_latency
42 |
43 |
44 | def _get_best_registry() -> str:
45 | """Get the best registry based on latency.
46 |
47 | Returns:
48 | The best registry.
49 | """
50 | console.debug("Getting best registry...")
51 | registries = [
52 | ("https://registry.npmjs.org", 1),
53 | ("https://registry.npmmirror.com", 2),
54 | ]
55 |
56 | best_registry = min(registries, key=lambda x: average_latency(x[0]) * x[1])[0]
57 | console.debug(f"Best registry: {best_registry}")
58 | return best_registry
59 |
60 |
61 | @once
62 | def get_npm_registry() -> str:
63 | """Get npm registry. If environment variable is set, use it first.
64 |
65 | Returns:
66 | The npm registry.
67 | """
68 | return environment.NPM_CONFIG_REGISTRY.get() or _get_best_registry()
69 |
--------------------------------------------------------------------------------
/reflex/vars/__init__.py:
--------------------------------------------------------------------------------
1 | """Immutable-Based Var System."""
2 |
3 | from .base import Field as Field
4 | from .base import LiteralVar as LiteralVar
5 | from .base import Var as Var
6 | from .base import VarData as VarData
7 | from .base import field as field
8 | from .base import get_unique_variable_name as get_unique_variable_name
9 | from .base import get_uuid_string_var as get_uuid_string_var
10 | from .base import var_operation as var_operation
11 | from .base import var_operation_return as var_operation_return
12 | from .datetime import DateTimeVar as DateTimeVar
13 | from .function import FunctionStringVar as FunctionStringVar
14 | from .function import FunctionVar as FunctionVar
15 | from .function import VarOperationCall as VarOperationCall
16 | from .number import BooleanVar as BooleanVar
17 | from .number import LiteralBooleanVar as LiteralBooleanVar
18 | from .number import LiteralNumberVar as LiteralNumberVar
19 | from .number import NumberVar as NumberVar
20 | from .object import LiteralObjectVar as LiteralObjectVar
21 | from .object import ObjectVar as ObjectVar
22 | from .sequence import ArrayVar as ArrayVar
23 | from .sequence import ConcatVarOperation as ConcatVarOperation
24 | from .sequence import LiteralArrayVar as LiteralArrayVar
25 | from .sequence import LiteralStringVar as LiteralStringVar
26 | from .sequence import StringVar as StringVar
27 |
--------------------------------------------------------------------------------
/scripts/__init__.py:
--------------------------------------------------------------------------------
1 | """Utility scripts for the project."""
2 |
--------------------------------------------------------------------------------
/scripts/darglint_test.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cd ..
3 |
4 | echo "start darglint"
5 |
6 | echo "reflex folder"
7 | for /R reflex %%f in (*.py) do (
8 | echo %%f
9 | echo %%f|findstr /r "^.*reflex\\rx\.py$"
10 | if errorlevel 1 (
11 | uv run darglint %%f
12 | )
13 | )
14 |
15 | echo "tests folder"
16 | for /R tests %%f in (*.py) do (
17 | echo %%f
18 | uv run darglint %%f
19 | )
20 |
21 | echo "darglint finished"
22 | pause
23 |
--------------------------------------------------------------------------------
/scripts/hatch_build.py:
--------------------------------------------------------------------------------
1 | """Custom build hook for Hatch."""
2 |
3 | import importlib.util
4 | import pathlib
5 | import subprocess
6 | import sys
7 | from typing import Any
8 |
9 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface
10 |
11 |
12 | class CustomBuilder(BuildHookInterface):
13 | """Custom build hook for Hatch."""
14 |
15 | PLUGIN_NAME = "custom"
16 |
17 | def marker(self) -> pathlib.Path:
18 | """Get the marker file path.
19 |
20 | Returns:
21 | The marker file path.
22 | """
23 | return (
24 | pathlib.Path(self.directory)
25 | / f".reflex-{self.metadata.version}.pyi_generated"
26 | )
27 |
28 | def finalize(
29 | self, version: str, build_data: dict[str, Any], artifact_path: str
30 | ) -> None:
31 | """Finalize the build process.
32 |
33 | Args:
34 | version: The version of the package.
35 | build_data: The build data.
36 | artifact_path: The path to the artifact.
37 | """
38 | if self.marker().exists():
39 | return
40 |
41 | if importlib.util.find_spec("pre_commit") and importlib.util.find_spec("toml"):
42 | import json
43 |
44 | import toml
45 | import yaml
46 |
47 | reflex_dir = pathlib.Path(__file__).parent.parent
48 | pre_commit_config = json.loads(
49 | json.dumps(
50 | toml.load(reflex_dir / "pyproject.toml")["tool"]["pre-commit"]
51 | )
52 | )
53 | (reflex_dir / ".pre-commit-config.yaml").write_text(
54 | yaml.dump(pre_commit_config), encoding="utf-8"
55 | )
56 |
57 | if not (pathlib.Path(self.root) / "scripts").exists():
58 | return
59 |
60 | for file in (pathlib.Path(self.root) / "reflex").rglob("**/*.pyi"):
61 | file.unlink(missing_ok=True)
62 |
63 | subprocess.run(
64 | [sys.executable, "-m", "reflex.utils.pyi_generator"],
65 | check=True,
66 | )
67 | self.marker().touch()
68 |
--------------------------------------------------------------------------------
/scripts/integration.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Change directory to the first argument passed to the script
4 | project_dir=$1
5 | shift
6 | pushd "$project_dir" || exit 1
7 | echo "Changed directory to $project_dir"
8 |
9 | # So we get stdout / stderr from Python ASAP. Without this, delays can be very long (e.g. on Windows, Github Actions)
10 | export PYTHONUNBUFFERED=1
11 |
12 | env_mode=$1
13 | shift
14 | check_ports=${1:-3000 8000}
15 | shift
16 |
17 | # Start the server in the background
18 | export TELEMETRY_ENABLED=false
19 | reflex run --loglevel debug --env "$env_mode" "$@" & pid=$!
20 |
21 | # Within the context of this bash, $pid_in_bash is what we need to pass to "kill" on exit
22 | # This is true on all platforms.
23 | pid_in_bash=$pid
24 | trap "kill -INT $pid_in_bash ||:" EXIT
25 |
26 | echo "Started server with PID $pid"
27 |
28 | # Assume we run from the root of the repo
29 | popd
30 |
31 | # In Windows, our Python script below needs to work with the WINPID
32 | if [ -f /proc/$pid/winpid ]; then
33 | pid=$(cat /proc/$pid/winpid)
34 | echo "Windows detected, passing winpid $pid to port waiter"
35 | fi
36 |
37 | python scripts/wait_for_listening_port.py $check_ports --timeout=900 --server-pid "$pid"
38 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | """Root directory for tests."""
2 |
3 | import os
4 |
5 | from reflex import constants
6 |
--------------------------------------------------------------------------------
/tests/benchmarks/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/benchmarks/__init__.py
--------------------------------------------------------------------------------
/tests/benchmarks/conftest.py:
--------------------------------------------------------------------------------
1 | from .fixtures import evaluated_page, unevaluated_page
2 |
3 | __all__ = ["evaluated_page", "unevaluated_page"]
4 |
--------------------------------------------------------------------------------
/tests/benchmarks/test_compilation.py:
--------------------------------------------------------------------------------
1 | from pytest_codspeed import BenchmarkFixture
2 |
3 | from reflex.compiler.compiler import _compile_page, _compile_stateful_components
4 | from reflex.components.component import Component
5 |
6 |
7 | def import_templates():
8 | # Importing the templates module to avoid the import time in the benchmark
9 | import reflex.compiler.templates # noqa: F401
10 |
11 |
12 | def test_compile_page(evaluated_page: Component, benchmark: BenchmarkFixture):
13 | import_templates()
14 |
15 | benchmark(lambda: _compile_page(evaluated_page, None))
16 |
17 |
18 | def test_compile_stateful(evaluated_page: Component, benchmark: BenchmarkFixture):
19 | import_templates()
20 |
21 | benchmark(lambda: _compile_stateful_components([evaluated_page]))
22 |
23 |
24 | def test_get_all_imports(evaluated_page: Component, benchmark: BenchmarkFixture):
25 | benchmark(lambda: evaluated_page._get_all_imports())
26 |
--------------------------------------------------------------------------------
/tests/benchmarks/test_evaluate.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Callable
2 |
3 | from pytest_codspeed import BenchmarkFixture
4 |
5 | from reflex.components.component import Component
6 |
7 |
8 | def test_evaluate_page(
9 | unevaluated_page: Callable[[], Component], benchmark: BenchmarkFixture
10 | ):
11 | benchmark(unevaluated_page)
12 |
--------------------------------------------------------------------------------
/tests/integration/__init__.py:
--------------------------------------------------------------------------------
1 | """Package for integration tests."""
2 |
--------------------------------------------------------------------------------
/tests/integration/conftest.py:
--------------------------------------------------------------------------------
1 | """Shared conftest for all integration tests."""
2 |
3 | import pytest
4 | from pytest_mock import MockerFixture
5 |
6 | import reflex.app
7 | from reflex.testing import AppHarness, AppHarnessProd
8 |
9 |
10 | @pytest.fixture(
11 | scope="session", params=[AppHarness, AppHarnessProd], ids=["dev", "prod"]
12 | )
13 | def app_harness_env(request):
14 | """Parametrize the AppHarness class to use for the test, either dev or prod.
15 |
16 | Args:
17 | request: The pytest fixture request object.
18 |
19 | Returns:
20 | The AppHarness class to use for the test.
21 | """
22 | return request.param
23 |
24 |
25 | @pytest.fixture(autouse=True)
26 | def raise_console_error(request, mocker: MockerFixture):
27 | """Spy on calls to `console.error` used by the framework.
28 |
29 | Help catch spurious error conditions that might otherwise go unnoticed.
30 |
31 | If a test is marked with `ignore_console_error`, the spy will be ignored
32 | after the test.
33 |
34 | Args:
35 | request: The pytest request object.
36 | mocker: The pytest mocker object.
37 |
38 | Yields:
39 | control to the test function.
40 | """
41 | spy = mocker.spy(reflex.app.console, "error")
42 | yield
43 | if "ignore_console_error" not in request.keywords:
44 | spy.assert_not_called()
45 |
--------------------------------------------------------------------------------
/tests/integration/init-test/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10
2 |
3 | ARG USERNAME=kerrigan
4 | RUN useradd -m $USERNAME
5 | RUN apt-get update && apt-get install -y redis && rm -rf /var/lib/apt/lists/*
6 | USER $USERNAME
7 |
8 | WORKDIR /home/$USERNAME
9 |
--------------------------------------------------------------------------------
/tests/integration/init-test/in_docker_test_script.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euxo pipefail
4 |
5 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
6 | export TELEMETRY_ENABLED=false
7 |
8 | function do_export () {
9 | template=$1
10 | mkdir ~/"$template"
11 | cd ~/"$template"
12 | rm -rf ~/.local/share/reflex ~/"$template"/.web
13 | reflex init --template "$template"
14 | reflex export
15 | (
16 | cd "$SCRIPTPATH/../../.."
17 | scripts/integration.sh ~/"$template" dev
18 | pkill -9 -f 'next-server|python3' || true
19 | sleep 10
20 | REDIS_URL=redis://localhost scripts/integration.sh ~/"$template" prod
21 | pkill -9 -f 'next-server|python3' || true
22 | sleep 10
23 | )
24 | }
25 |
26 | echo "Preparing test project dir"
27 | python3 -m venv ~/venv
28 | source ~/venv/bin/activate
29 | pip install -U pip
30 |
31 | echo "Installing reflex from local repo code"
32 | cp -r /reflex-repo ~/reflex-repo
33 | pip install ~/reflex-repo
34 |
35 | redis-server &
36 |
37 | echo "Running reflex init in test project dir"
38 | do_export blank
39 |
--------------------------------------------------------------------------------
/tests/integration/shared/state.py:
--------------------------------------------------------------------------------
1 | """Simple module which contains one reusable reflex state class."""
2 |
3 | import reflex as rx
4 |
5 |
6 | class SharedState(rx.State):
7 | """Shared state class for reflexers using librarys."""
8 |
--------------------------------------------------------------------------------
/tests/integration/test_shared_state.py:
--------------------------------------------------------------------------------
1 | """Test shared state."""
2 |
3 | from __future__ import annotations
4 |
5 | from collections.abc import Generator
6 |
7 | import pytest
8 |
9 | from reflex.testing import AppHarness, WebDriver
10 |
11 |
12 | def SharedStateApp():
13 | """Test that shared state works as expected."""
14 | import reflex as rx
15 | from tests.integration.shared.state import SharedState
16 |
17 | class State(SharedState):
18 | pass
19 |
20 | def index() -> rx.Component:
21 | return rx.vstack()
22 |
23 | app = rx.App()
24 | app.add_page(index)
25 |
26 |
27 | @pytest.fixture
28 | def shared_state(
29 | tmp_path_factory,
30 | ) -> Generator[AppHarness, None, None]:
31 | """Start SharedStateApp at tmp_path via AppHarness.
32 |
33 | Args:
34 | tmp_path_factory: pytest tmp_path_factory fixture
35 |
36 | Yields:
37 | running AppHarness instance
38 |
39 | """
40 | with AppHarness.create(
41 | root=tmp_path_factory.mktemp("shared_state"),
42 | app_source=SharedStateApp,
43 | ) as harness:
44 | yield harness
45 |
46 |
47 | @pytest.fixture
48 | def driver(shared_state: AppHarness) -> Generator[WebDriver, None, None]:
49 | """Get an instance of the browser open to the shared_state app.
50 |
51 | Args:
52 | shared_state: harness for SharedStateApp
53 |
54 | Yields:
55 | WebDriver instance.
56 |
57 | """
58 | assert shared_state.app_instance is not None, "app is not running"
59 | driver = shared_state.frontend()
60 | try:
61 | yield driver
62 | finally:
63 | driver.quit()
64 |
65 |
66 | def test_shared_state(
67 | shared_state: AppHarness,
68 | driver: WebDriver,
69 | ):
70 | """Test that 2 AppHarness instances can share a state (f.e. from a library).
71 |
72 | Args:
73 | shared_state: harness for SharedStateApp.
74 | driver: WebDriver instance.
75 |
76 | """
77 | assert shared_state.app_instance is not None
78 |
--------------------------------------------------------------------------------
/tests/integration/tests_playwright/test_link_hover.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Generator
2 |
3 | import pytest
4 | from playwright.sync_api import Page, expect
5 |
6 | from reflex.testing import AppHarness
7 |
8 |
9 | def LinkApp():
10 | import reflex as rx
11 |
12 | app = rx.App()
13 |
14 | def index():
15 | return rx.vstack(
16 | rx.box(height="10em"), # spacer, so the link isn't hovered initially
17 | rx.link(
18 | "Click me",
19 | href="#",
20 | color="blue",
21 | _hover=rx.Style({"color": "red"}),
22 | ),
23 | )
24 |
25 | app.add_page(index, "/")
26 |
27 |
28 | @pytest.fixture
29 | def link_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
30 | with AppHarness.create(
31 | root=tmp_path_factory.mktemp("link_app"),
32 | app_source=LinkApp,
33 | ) as harness:
34 | assert harness.app_instance is not None, "app is not running"
35 | yield harness
36 |
37 |
38 | def test_link_hover(link_app: AppHarness, page: Page):
39 | assert link_app.frontend_url is not None
40 | page.goto(link_app.frontend_url)
41 |
42 | link = page.get_by_role("link")
43 | expect(link).to_have_text("Click me")
44 | expect(link).to_have_css("color", "rgb(0, 0, 255)")
45 | link.hover()
46 | expect(link).to_have_css("color", "rgb(255, 0, 0)")
47 |
--------------------------------------------------------------------------------
/tests/integration/tests_playwright/test_stateless_app.py:
--------------------------------------------------------------------------------
1 | """Integration tests for a stateless app."""
2 |
3 | from collections.abc import Generator
4 |
5 | import httpx
6 | import pytest
7 | from playwright.sync_api import Page, expect
8 |
9 | import reflex as rx
10 | from reflex.testing import AppHarness
11 |
12 |
13 | def StatelessApp():
14 | """A stateless app that renders a heading."""
15 | import reflex as rx
16 |
17 | def index():
18 | return rx.heading("This is a stateless app")
19 |
20 | app = rx.App()
21 | app.add_page(index)
22 |
23 |
24 | @pytest.fixture(scope="module")
25 | def stateless_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
26 | """Create a stateless app AppHarness.
27 |
28 | Args:
29 | tmp_path_factory: pytest fixture for creating temporary directories.
30 |
31 | Yields:
32 | AppHarness: A harness for testing the stateless app.
33 | """
34 | with AppHarness.create(
35 | root=tmp_path_factory.mktemp("stateless_app"),
36 | app_source=StatelessApp,
37 | ) as harness:
38 | yield harness
39 |
40 |
41 | def test_statelessness(stateless_app: AppHarness, page: Page):
42 | """Test that the stateless app renders a heading but backend/_event is not mounted.
43 |
44 | Args:
45 | stateless_app: A harness for testing the stateless app.
46 | page: A Playwright page.
47 | """
48 | assert stateless_app.frontend_url is not None
49 | assert stateless_app.backend is not None
50 | assert stateless_app.backend.started
51 |
52 | res = httpx.get(rx.config.get_config().api_url + "/_event")
53 | assert res.status_code == 404
54 |
55 | res2 = httpx.get(rx.config.get_config().api_url + "/ping")
56 | assert res2.status_code == 200
57 |
58 | page.goto(stateless_app.frontend_url)
59 | expect(page.get_by_role("heading")).to_have_text("This is a stateless app")
60 |
--------------------------------------------------------------------------------
/tests/units/__init__.py:
--------------------------------------------------------------------------------
1 | """Root directory for tests."""
2 |
3 | import os
4 |
5 | from reflex import constants
6 |
--------------------------------------------------------------------------------
/tests/units/assets/custom_script.js:
--------------------------------------------------------------------------------
1 | const test = "inside custom_script.js";
2 |
--------------------------------------------------------------------------------
/tests/units/compiler/__init__.py:
--------------------------------------------------------------------------------
1 | """Compiler tests."""
2 |
--------------------------------------------------------------------------------
/tests/units/compiler/test_compiler_utils.py:
--------------------------------------------------------------------------------
1 | def TestState(State):
2 | pass
3 |
4 |
5 | def test_compile_state():
6 | # TODO: Implement test for compile_state function.
7 | pass
8 |
--------------------------------------------------------------------------------
/tests/units/components/__init__.py:
--------------------------------------------------------------------------------
1 | """Component tests."""
2 |
--------------------------------------------------------------------------------
/tests/units/components/base/test_bare.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from reflex.components.base.bare import Bare
4 | from reflex.vars.base import Var
5 |
6 | STATE_VAR = Var(_js_expr="default_state.name")
7 |
8 |
9 | @pytest.mark.parametrize(
10 | ("contents", "expected"),
11 | [
12 | ("hello", '"hello"'),
13 | ("{}", '"{}"'),
14 | (None, '""'),
15 | (STATE_VAR, "default_state.name"),
16 | ],
17 | )
18 | def test_fstrings(contents, expected):
19 | """Test that fstrings are rendered correctly.
20 |
21 | Args:
22 | contents: The contents of the component.
23 | expected: The expected output.
24 | """
25 | comp = Bare.create(contents).render()
26 | assert comp["contents"] == expected
27 |
--------------------------------------------------------------------------------
/tests/units/components/base/test_link.py:
--------------------------------------------------------------------------------
1 | from reflex.components.base.link import RawLink, ScriptTag
2 |
3 |
4 | def test_raw_link():
5 | raw_link = RawLink.create("https://example.com").render()
6 | assert raw_link["name"] == '"link"'
7 | assert raw_link["children"][0]["contents"] == '"https://example.com"'
8 |
9 |
10 | def test_script_tag():
11 | script_tag = ScriptTag.create("console.log('Hello, world!');").render()
12 | assert script_tag["name"] == '"script"'
13 | assert script_tag["children"][0]["contents"] == "\"console.log('Hello, world!');\""
14 |
--------------------------------------------------------------------------------
/tests/units/components/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/core/__init__.py
--------------------------------------------------------------------------------
/tests/units/components/core/test_banner.py:
--------------------------------------------------------------------------------
1 | from reflex.components.core.banner import (
2 | ConnectionBanner,
3 | ConnectionModal,
4 | ConnectionPulser,
5 | WebsocketTargetURL,
6 | )
7 | from reflex.components.radix.themes.base import RadixThemesComponent
8 | from reflex.components.radix.themes.typography.text import Text
9 |
10 |
11 | def test_websocket_target_url():
12 | url = WebsocketTargetURL.create()
13 | var_data = url._get_all_var_data()
14 | assert var_data is not None
15 | assert sorted(key for key, _ in var_data.imports) == sorted(
16 | ("$/utils/state", "$/env.json")
17 | )
18 |
19 |
20 | def test_connection_banner():
21 | banner = ConnectionBanner.create()
22 | _imports = banner._get_all_imports(collapse=True)
23 | assert sorted(_imports) == sorted(
24 | (
25 | "react",
26 | "$/utils/context",
27 | "$/utils/state",
28 | RadixThemesComponent.create().library or "",
29 | "$/env.json",
30 | )
31 | )
32 |
33 | msg = "Connection error"
34 | custom_banner = ConnectionBanner.create(Text.create(msg))
35 | assert msg in str(custom_banner.render())
36 |
37 |
38 | def test_connection_modal():
39 | modal = ConnectionModal.create()
40 | _imports = modal._get_all_imports(collapse=True)
41 | assert sorted(_imports) == sorted(
42 | (
43 | "react",
44 | "$/utils/context",
45 | "$/utils/state",
46 | RadixThemesComponent.create().library or "",
47 | "$/env.json",
48 | )
49 | )
50 |
51 | msg = "Connection error"
52 | custom_modal = ConnectionModal.create(Text.create(msg))
53 | assert msg in str(custom_modal.render())
54 |
55 |
56 | def test_connection_pulser():
57 | pulser = ConnectionPulser.create()
58 | _custom_code = pulser._get_all_custom_code()
59 | _imports = pulser._get_all_imports(collapse=True)
60 |
--------------------------------------------------------------------------------
/tests/units/components/core/test_html.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from reflex.components.core.html import Html
4 | from reflex.state import State
5 |
6 |
7 | def test_html_no_children():
8 | with pytest.raises(ValueError):
9 | _ = Html.create()
10 |
11 |
12 | def test_html_many_children():
13 | with pytest.raises(ValueError):
14 | _ = Html.create("foo", "bar")
15 |
16 |
17 | def test_html_create():
18 | html = Html.create("Hello !
")
19 | assert str(html.dangerouslySetInnerHTML) == '({ ["__html"] : "Hello !
" })' # pyright: ignore [reportAttributeAccessIssue]
20 | assert (
21 | str(html)
22 | == 'jsx("div",{className:"rx-Html",dangerouslySetInnerHTML:({ ["__html"] : "Hello !
" })},)\n'
23 | )
24 |
25 |
26 | def test_html_fstring_create():
27 | class TestState(State):
28 | """The app state."""
29 |
30 | myvar: str = "Blue"
31 |
32 | html = Html.create(f"Hello {TestState.myvar}!
")
33 |
34 | html_dangerouslySetInnerHTML = html.dangerouslySetInnerHTML # pyright: ignore [reportAttributeAccessIssue]
35 |
36 | assert (
37 | str(html_dangerouslySetInnerHTML)
38 | == f'({{ ["__html"] : ("Hello "+{TestState.myvar!s}+"!
") }})'
39 | )
40 | assert (
41 | str(html)
42 | == f'jsx("div",{{className:"rx-Html",dangerouslySetInnerHTML:{html_dangerouslySetInnerHTML!s}}},)\n'
43 | )
44 |
--------------------------------------------------------------------------------
/tests/units/components/core/test_responsive.py:
--------------------------------------------------------------------------------
1 | from reflex.components.core.responsive import (
2 | desktop_only,
3 | mobile_and_tablet,
4 | mobile_only,
5 | tablet_and_desktop,
6 | tablet_only,
7 | )
8 | from reflex.components.radix.themes.layout.box import Box
9 |
10 |
11 | def test_mobile_only():
12 | """Test the mobile_only responsive component."""
13 | component = mobile_only("Content")
14 | assert isinstance(component, Box)
15 |
16 |
17 | def test_tablet_only():
18 | """Test the tablet_only responsive component."""
19 | component = tablet_only("Content")
20 | assert isinstance(component, Box)
21 |
22 |
23 | def test_desktop_only():
24 | """Test the desktop_only responsive component."""
25 | component = desktop_only("Content")
26 | assert isinstance(component, Box)
27 |
28 |
29 | def test_tablet_and_desktop():
30 | """Test the tablet_and_desktop responsive component."""
31 | component = tablet_and_desktop("Content")
32 | assert isinstance(component, Box)
33 |
34 |
35 | def test_mobile_and_tablet():
36 | """Test the mobile_and_tablet responsive component."""
37 | component = mobile_and_tablet("Content")
38 | assert isinstance(component, Box)
39 |
--------------------------------------------------------------------------------
/tests/units/components/datadisplay/__init__.py:
--------------------------------------------------------------------------------
1 | """Datadisplay component tests."""
2 |
--------------------------------------------------------------------------------
/tests/units/components/datadisplay/conftest.py:
--------------------------------------------------------------------------------
1 | """Data display component tests fixtures."""
2 |
3 | import pandas as pd
4 | import pytest
5 |
6 | import reflex as rx
7 | from reflex.state import BaseState
8 |
9 |
10 | @pytest.fixture
11 | def data_table_state(request):
12 | """Get a data table state.
13 |
14 | Args:
15 | request: The request.
16 |
17 | Returns:
18 | The data table state class.
19 | """
20 |
21 | class DataTableState(BaseState):
22 | data = request.param["data"]
23 | columns = ["column1", "column2"]
24 |
25 | return DataTableState
26 |
27 |
28 | @pytest.fixture
29 | def data_table_state2():
30 | """Get a data table state.
31 |
32 | Returns:
33 | The data table state class.
34 | """
35 |
36 | class DataTableState(BaseState):
37 | _data = pd.DataFrame()
38 |
39 | @rx.var
40 | def data(self):
41 | return self._data
42 |
43 | return DataTableState
44 |
45 |
46 | @pytest.fixture
47 | def data_table_state3():
48 | """Get a data table state.
49 |
50 | Returns:
51 | The data table state class.
52 | """
53 |
54 | class DataTableState(BaseState):
55 | _data: list = []
56 | _columns: list = ["col1", "col2"]
57 |
58 | @rx.var
59 | def data(self) -> list:
60 | return self._data
61 |
62 | @rx.var
63 | def columns(self):
64 | return self._columns
65 |
66 | return DataTableState
67 |
68 |
69 | @pytest.fixture
70 | def data_table_state4():
71 | """Get a data table state.
72 |
73 | Returns:
74 | The data table state class.
75 | """
76 |
77 | class DataTableState(BaseState):
78 | _data: list = []
79 | _columns: list[str] = ["col1", "col2"]
80 |
81 | @rx.var
82 | def data(self):
83 | return self._data
84 |
85 | @rx.var
86 | def columns(self) -> list:
87 | return self._columns
88 |
89 | return DataTableState
90 |
--------------------------------------------------------------------------------
/tests/units/components/datadisplay/test_code.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from reflex.components.datadisplay.code import CodeBlock, Theme
4 |
5 |
6 | @pytest.mark.parametrize(
7 | ("theme", "expected"),
8 | [(Theme.one_light, "oneLight"), (Theme.one_dark, "oneDark")],
9 | )
10 | def test_code_light_dark_theme(theme, expected):
11 | code_block = CodeBlock.create(theme=theme)
12 |
13 | assert code_block.theme._js_expr == expected # pyright: ignore [reportAttributeAccessIssue]
14 |
--------------------------------------------------------------------------------
/tests/units/components/datadisplay/test_dataeditor.py:
--------------------------------------------------------------------------------
1 | from reflex.components.datadisplay.dataeditor import DataEditor
2 |
3 |
4 | def test_dataeditor():
5 | editor_wrapper = DataEditor.create().render()
6 | editor = editor_wrapper["children"][0]
7 | assert editor_wrapper["name"] == '"div"'
8 | assert editor_wrapper["props"] == [
9 | 'css:({ ["width"] : "100%", ["height"] : "100%" })'
10 | ]
11 | assert editor["name"] == "DataEditor"
12 |
--------------------------------------------------------------------------------
/tests/units/components/el/test_html.py:
--------------------------------------------------------------------------------
1 | from reflex.components.el.constants.html import ATTR_TO_ELEMENTS, ELEMENTS
2 |
3 |
4 | def test_html_constants():
5 | assert ELEMENTS
6 | assert ATTR_TO_ELEMENTS
7 |
--------------------------------------------------------------------------------
/tests/units/components/el/test_svg.py:
--------------------------------------------------------------------------------
1 | from reflex.components.el.elements.media import (
2 | Circle,
3 | Defs,
4 | Ellipse,
5 | G,
6 | Line,
7 | LinearGradient,
8 | Path,
9 | Polygon,
10 | RadialGradient,
11 | Rect,
12 | Stop,
13 | Svg,
14 | Text,
15 | )
16 |
17 |
18 | def test_circle():
19 | circle = Circle.create().render()
20 | assert circle["name"] == '"circle"'
21 |
22 |
23 | def test_defs():
24 | defs = Defs.create().render()
25 | assert defs["name"] == '"defs"'
26 |
27 |
28 | def test_ellipse():
29 | ellipse = Ellipse.create().render()
30 | assert ellipse["name"] == '"ellipse"'
31 |
32 |
33 | def test_line():
34 | line = Line.create().render()
35 | assert line["name"] == '"line"'
36 |
37 |
38 | def test_linear_gradient():
39 | linear_gradient = LinearGradient.create().render()
40 | assert linear_gradient["name"] == '"linearGradient"'
41 |
42 |
43 | def test_path():
44 | path = Path.create().render()
45 | assert path["name"] == '"path"'
46 |
47 |
48 | def test_polygon():
49 | polygon = Polygon.create().render()
50 | assert polygon["name"] == '"polygon"'
51 |
52 |
53 | def test_radial_gradient():
54 | radial_gradient = RadialGradient.create().render()
55 | assert radial_gradient["name"] == '"radialGradient"'
56 |
57 |
58 | def test_rect():
59 | rect = Rect.create().render()
60 | assert rect["name"] == '"rect"'
61 |
62 |
63 | def test_svg():
64 | svg = Svg.create().render()
65 | assert svg["name"] == '"svg"'
66 |
67 |
68 | def test_text():
69 | text = Text.create().render()
70 | assert text["name"] == '"text"'
71 |
72 |
73 | def test_stop():
74 | stop = Stop.create().render()
75 | assert stop["name"] == '"stop"'
76 |
77 |
78 | def test_g():
79 | g = G.create().render()
80 | assert g["name"] == '"g"'
81 |
--------------------------------------------------------------------------------
/tests/units/components/forms/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/forms/__init__.py
--------------------------------------------------------------------------------
/tests/units/components/forms/test_form.py:
--------------------------------------------------------------------------------
1 | from reflex.components.radix.primitives.form import Form
2 | from reflex.event import EventChain, prevent_default
3 | from reflex.vars.base import Var
4 |
5 |
6 | def test_render_on_submit():
7 | """Test that on_submit event chain is rendered as a separate function."""
8 | submit_it = Var(
9 | _js_expr="submit_it",
10 | _var_type=EventChain,
11 | )
12 | f = Form.create(on_submit=submit_it)
13 | exp_submit_name = f"handleSubmit_{f.handle_submit_unique_name}" # pyright: ignore [reportAttributeAccessIssue]
14 | assert f"onSubmit:{exp_submit_name}" in f.render()["props"]
15 |
16 |
17 | def test_render_no_on_submit():
18 | """A form without on_submit should render a prevent_default handler."""
19 | f = Form.create()
20 | assert isinstance(f.event_triggers["on_submit"], EventChain)
21 | assert len(f.event_triggers["on_submit"].events) == 1
22 | assert f.event_triggers["on_submit"].events[0] == prevent_default
23 |
--------------------------------------------------------------------------------
/tests/units/components/graphing/__init__.py:
--------------------------------------------------------------------------------
1 | """Graphing component tests."""
2 |
--------------------------------------------------------------------------------
/tests/units/components/graphing/test_plotly.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import plotly.graph_objects as go
3 | import pytest
4 |
5 | import reflex as rx
6 | from reflex.utils.serializers import serialize, serialize_figure
7 |
8 |
9 | @pytest.fixture
10 | def plotly_fig() -> go.Figure:
11 | """Get a plotly figure.
12 |
13 | Returns:
14 | A random plotly figure.
15 | """
16 | # Generate random data.
17 | rng = np.random.default_rng()
18 | data = rng.integers(0, 10, size=(10, 4))
19 | trace = go.Scatter(
20 | x=list(range(len(data))), y=data[:, 0], mode="lines", name="Trace 1"
21 | )
22 |
23 | # Create a graph.
24 | return go.Figure(data=[trace])
25 |
26 |
27 | def test_serialize_plotly(plotly_fig: go.Figure):
28 | """Test that serializing a plotly figure works.
29 |
30 | Args:
31 | plotly_fig: The figure to serialize.
32 | """
33 | value = serialize(plotly_fig)
34 | assert isinstance(value, dict)
35 | assert value == serialize_figure(plotly_fig)
36 |
37 |
38 | def test_plotly_config_option(plotly_fig: go.Figure):
39 | """Test that the plotly component can be created with a config option.
40 |
41 | Args:
42 | plotly_fig: The figure to display.
43 | """
44 | # This tests just confirm that the component can be created with a config option.
45 | _ = rx.plotly(data=plotly_fig, config={"showLink": True})
46 |
--------------------------------------------------------------------------------
/tests/units/components/graphing/test_recharts.py:
--------------------------------------------------------------------------------
1 | from reflex.components.recharts.charts import (
2 | AreaChart,
3 | BarChart,
4 | LineChart,
5 | PieChart,
6 | RadarChart,
7 | RadialBarChart,
8 | ScatterChart,
9 | )
10 | from reflex.components.recharts.general import ResponsiveContainer
11 |
12 |
13 | def test_area_chart():
14 | ac = AreaChart.create()
15 | assert isinstance(ac, ResponsiveContainer)
16 | assert isinstance(ac.children[0], AreaChart)
17 |
18 |
19 | def test_bar_chart():
20 | bc = BarChart.create()
21 | assert isinstance(bc, ResponsiveContainer)
22 | assert isinstance(bc.children[0], BarChart)
23 |
24 |
25 | def test_line_chart():
26 | lc = LineChart.create()
27 | assert isinstance(lc, ResponsiveContainer)
28 | assert isinstance(lc.children[0], LineChart)
29 |
30 |
31 | def test_pie_chart():
32 | pc = PieChart.create()
33 | assert isinstance(pc, ResponsiveContainer)
34 | assert isinstance(pc.children[0], PieChart)
35 |
36 |
37 | def test_radar_chart():
38 | rc = RadarChart.create()
39 | assert isinstance(rc, ResponsiveContainer)
40 | assert isinstance(rc.children[0], RadarChart)
41 |
42 |
43 | def test_radial_bar_chart():
44 | rbc = RadialBarChart.create()
45 | assert isinstance(rbc, ResponsiveContainer)
46 | assert isinstance(rbc.children[0], RadialBarChart)
47 |
48 |
49 | def test_scatter_chart():
50 | sc = ScatterChart.create()
51 | assert isinstance(sc, ResponsiveContainer)
52 | assert isinstance(sc.children[0], ScatterChart)
53 |
--------------------------------------------------------------------------------
/tests/units/components/layout/__init__.py:
--------------------------------------------------------------------------------
1 | """Layout component tests."""
2 |
--------------------------------------------------------------------------------
/tests/units/components/lucide/test_icon.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from reflex.components.lucide.icon import (
4 | LUCIDE_ICON_LIST,
5 | LUCIDE_ICON_MAPPING_OVERRIDE,
6 | Icon,
7 | )
8 | from reflex.utils import format
9 |
10 |
11 | @pytest.mark.parametrize("tag", LUCIDE_ICON_LIST)
12 | def test_icon(tag):
13 | icon = Icon.create(tag)
14 | assert icon.alias == "Lucide" + LUCIDE_ICON_MAPPING_OVERRIDE.get(
15 | tag, format.to_title_case(tag)
16 | )
17 |
18 |
19 | def test_icon_missing_tag():
20 | with pytest.raises(AttributeError):
21 | _ = Icon.create()
22 |
23 |
24 | def test_icon_invalid_tag():
25 | invalid = Icon.create("invalid-tag")
26 | assert invalid.tag == "CircleHelp"
27 |
28 |
29 | def test_icon_multiple_children():
30 | with pytest.raises(AttributeError):
31 | _ = Icon.create("activity", "child1", "child2")
32 |
33 |
34 | def test_icon_add_style():
35 | ic = Icon.create("activity")
36 | assert ic.add_style() is None
37 |
--------------------------------------------------------------------------------
/tests/units/components/markdown/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/markdown/__init__.py
--------------------------------------------------------------------------------
/tests/units/components/media/__init__.py:
--------------------------------------------------------------------------------
1 | """Media component tests."""
2 |
--------------------------------------------------------------------------------
/tests/units/components/radix/test_icon_button.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from reflex.components.lucide.icon import Icon
4 | from reflex.components.radix.themes.components.icon_button import IconButton
5 | from reflex.style import Style
6 | from reflex.vars.base import LiteralVar
7 |
8 |
9 | def test_icon_button():
10 | ib1 = IconButton.create("activity")
11 | assert isinstance(ib1, IconButton)
12 |
13 | ib2 = IconButton.create(Icon.create("activity"))
14 | assert isinstance(ib2, IconButton)
15 |
16 | assert isinstance(ib1.add_style(), Style)
17 | assert isinstance(ib2.add_style(), Style)
18 |
19 |
20 | def test_icon_button_no_child():
21 | with pytest.raises(ValueError):
22 | _ = IconButton.create()
23 |
24 |
25 | def test_icon_button_size_prop():
26 | ib1 = IconButton.create("activity", size="2")
27 | assert isinstance(ib1, IconButton)
28 |
29 | ib2 = IconButton.create("activity", size=LiteralVar.create("2"))
30 | assert isinstance(ib2, IconButton)
31 |
--------------------------------------------------------------------------------
/tests/units/components/radix/test_layout.py:
--------------------------------------------------------------------------------
1 | from reflex.components.radix.themes.layout.base import LayoutComponent
2 |
3 |
4 | def test_layout_component():
5 | lc = LayoutComponent.create()
6 | assert isinstance(lc, LayoutComponent)
7 |
--------------------------------------------------------------------------------
/tests/units/components/recharts/test_cartesian.py:
--------------------------------------------------------------------------------
1 | from reflex.components.recharts import (
2 | Area,
3 | Bar,
4 | Brush,
5 | Line,
6 | Scatter,
7 | XAxis,
8 | YAxis,
9 | ZAxis,
10 | )
11 |
12 |
13 | def test_xaxis():
14 | x_axis = XAxis.create("x").render()
15 | assert x_axis["name"] == "RechartsXAxis"
16 |
17 |
18 | def test_yaxis():
19 | x_axis = YAxis.create("y").render()
20 | assert x_axis["name"] == "RechartsYAxis"
21 |
22 |
23 | def test_zaxis():
24 | x_axis = ZAxis.create("z").render()
25 | assert x_axis["name"] == "RechartsZAxis"
26 |
27 |
28 | def test_brush():
29 | brush = Brush.create().render()
30 | assert brush["name"] == "RechartsBrush"
31 |
32 |
33 | def test_area():
34 | area = Area.create().render()
35 | assert area["name"] == "RechartsArea"
36 |
37 |
38 | def test_bar():
39 | bar = Bar.create().render()
40 | assert bar["name"] == "RechartsBar"
41 |
42 |
43 | def test_line():
44 | line = Line.create().render()
45 | assert line["name"] == "RechartsLine"
46 |
47 |
48 | def test_scatter():
49 | scatter = Scatter.create().render()
50 | assert scatter["name"] == "RechartsScatter"
51 |
--------------------------------------------------------------------------------
/tests/units/components/recharts/test_polar.py:
--------------------------------------------------------------------------------
1 | from reflex.components.recharts import (
2 | Pie,
3 | PolarAngleAxis,
4 | PolarGrid,
5 | PolarRadiusAxis,
6 | Radar,
7 | RadialBar,
8 | )
9 |
10 |
11 | def test_pie():
12 | pie = Pie.create().render()
13 | assert pie["name"] == "RechartsPie"
14 |
15 |
16 | def test_radar():
17 | radar = Radar.create().render()
18 | assert radar["name"] == "RechartsRadar"
19 |
20 |
21 | def test_radialbar():
22 | radialbar = RadialBar.create().render()
23 | assert radialbar["name"] == "RechartsRadialBar"
24 |
25 |
26 | def test_polarangleaxis():
27 | polarangleaxis = PolarAngleAxis.create().render()
28 | assert polarangleaxis["name"] == "RechartsPolarAngleAxis"
29 |
30 |
31 | def test_polargrid():
32 | polargrid = PolarGrid.create().render()
33 | assert polargrid["name"] == "RechartsPolarGrid"
34 |
35 |
36 | def test_polarradiusaxis():
37 | polarradiusaxis = PolarRadiusAxis.create().render()
38 | assert polarradiusaxis["name"] == "RechartsPolarRadiusAxis"
39 |
--------------------------------------------------------------------------------
/tests/units/components/test_component_future_annotations.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import Any
4 |
5 | from reflex.components.component import Component
6 | from reflex.event import EventHandler, input_event, no_args_event_spec
7 |
8 |
9 | # This is a repeat of its namesake in test_component.py.
10 | def test_custom_component_declare_event_handlers_in_fields():
11 | class ReferenceComponent(Component):
12 | def get_event_triggers(self) -> dict[str, Any]:
13 | """Test controlled triggers.
14 |
15 | Returns:
16 | Test controlled triggers.
17 | """
18 | return {
19 | **super().get_event_triggers(),
20 | "on_a": lambda e: [e],
21 | "on_b": lambda e: [e.target.value],
22 | "on_c": lambda e: [],
23 | "on_d": no_args_event_spec,
24 | }
25 |
26 | class TestComponent(Component):
27 | on_a: EventHandler[lambda e0: [e0]]
28 | on_b: EventHandler[input_event]
29 | on_c: EventHandler[no_args_event_spec]
30 | on_d: EventHandler[no_args_event_spec]
31 |
32 | custom_component = ReferenceComponent.create()
33 | test_component = TestComponent.create()
34 | assert (
35 | custom_component.get_event_triggers().keys()
36 | == test_component.get_event_triggers().keys()
37 | )
38 |
--------------------------------------------------------------------------------
/tests/units/components/test_component_state.py:
--------------------------------------------------------------------------------
1 | """Ensure that Components returned by ComponentState.create have independent State classes."""
2 |
3 | import pytest
4 |
5 | import reflex as rx
6 | from reflex.components.base.bare import Bare
7 | from reflex.utils.exceptions import ReflexRuntimeError
8 |
9 |
10 | def test_component_state():
11 | """Create two components with independent state classes."""
12 |
13 | class CS(rx.ComponentState):
14 | count: int = 0
15 |
16 | def increment(self):
17 | self.count += 1
18 |
19 | @classmethod
20 | def get_component(cls, *children, **props):
21 | return rx.el.div(
22 | *children,
23 | **props,
24 | )
25 |
26 | cs1, cs2 = CS.create("a", id="a"), CS.create("b", id="b")
27 | assert isinstance(cs1, rx.Component)
28 | assert isinstance(cs2, rx.Component)
29 | assert cs1.State is not None
30 | assert cs2.State is not None
31 | assert cs1.State != cs2.State
32 | assert issubclass(cs1.State, CS)
33 | assert issubclass(cs1.State, rx.State)
34 | assert issubclass(cs2.State, CS)
35 | assert issubclass(cs2.State, rx.State)
36 | assert CS._per_component_state_instance_count == 2
37 | assert isinstance(cs1.State.increment, rx.event.EventHandler)
38 | assert cs1.State.increment != cs2.State.increment
39 |
40 | assert len(cs1.children) == 1
41 | assert cs1.children[0].render() == Bare.create("a").render()
42 | assert cs1.id == "a"
43 | assert len(cs2.children) == 1
44 | assert cs2.children[0].render() == Bare.create("b").render()
45 | assert cs2.id == "b"
46 |
47 |
48 | def test_init_component_state() -> None:
49 | """Ensure that ComponentState subclasses cannot be instantiated directly."""
50 |
51 | class CS(rx.ComponentState):
52 | @classmethod
53 | def get_component(cls, *children, **props):
54 | return rx.el.div()
55 |
56 | with pytest.raises(ReflexRuntimeError):
57 | CS()
58 |
59 | class SubCS(CS):
60 | pass
61 |
62 | with pytest.raises(ReflexRuntimeError):
63 | SubCS()
64 |
--------------------------------------------------------------------------------
/tests/units/components/typography/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/typography/__init__.py
--------------------------------------------------------------------------------
/tests/units/components/typography/test_markdown.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import reflex as rx
4 | from reflex.components.markdown import Markdown
5 |
6 |
7 | @pytest.mark.parametrize(
8 | ("tag", "expected"),
9 | [
10 | ("h1", "Heading"),
11 | ("h2", "Heading"),
12 | ("h3", "Heading"),
13 | ("h4", "Heading"),
14 | ("h5", "Heading"),
15 | ("h6", "Heading"),
16 | ("p", "Text"),
17 | ("ul", "ul"),
18 | ("ol", "ol"),
19 | ("li", "li"),
20 | ("a", "Link"),
21 | ("code", "Code"),
22 | ],
23 | )
24 | def test_get_component(tag, expected):
25 | """Test getting a component from the component map.
26 |
27 | Args:
28 | tag: The tag to get.
29 | expected: The expected component.
30 | """
31 | md = Markdown.create("# Hello")
32 | assert tag in md.component_map # pyright: ignore [reportAttributeAccessIssue]
33 | assert md.get_component(tag).tag == expected
34 |
35 |
36 | def test_set_component_map():
37 | """Test setting the component map."""
38 | component_map = {
39 | "h1": lambda value: rx.box(rx.heading(value, as_="h1"), padding="1em"),
40 | "p": lambda value: rx.box(rx.text(value), padding="1em"),
41 | }
42 | md = Markdown.create("# Hello", component_map=component_map)
43 |
44 | # Check that the new tags have been added.
45 | assert md.get_component("h1").tag == "Box"
46 | assert md.get_component("p").tag == "Box"
47 |
48 | # Make sure the old tags are still there.
49 | assert md.get_component("h2").tag == "Heading"
50 |
--------------------------------------------------------------------------------
/tests/units/middleware/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/middleware/__init__.py
--------------------------------------------------------------------------------
/tests/units/middleware/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from reflex.event import Event
4 | from reflex.state import State
5 |
6 |
7 | def create_event(name):
8 | return Event(
9 | token="",
10 | name=name,
11 | router_data={
12 | "pathname": "/",
13 | "query": {},
14 | "token": "",
15 | "sid": "",
16 | "headers": {},
17 | "ip": "127.0.0.1",
18 | },
19 | payload={},
20 | )
21 |
22 |
23 | @pytest.fixture
24 | def event1():
25 | return create_event(f"{State.get_name()}.hydrate")
26 |
--------------------------------------------------------------------------------
/tests/units/middleware/test_hydrate_middleware.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import pytest
4 | from pytest_mock import MockerFixture
5 |
6 | from reflex.app import App
7 | from reflex.middleware.hydrate_middleware import HydrateMiddleware
8 | from reflex.state import State, StateUpdate
9 |
10 |
11 | class TestState(State):
12 | """A test state with no return in handler."""
13 |
14 | __test__ = False
15 |
16 | num: int = 0
17 |
18 | def test_handler(self):
19 | """Test handler."""
20 | self.num += 1
21 |
22 |
23 | @pytest.fixture
24 | def hydrate_middleware() -> HydrateMiddleware:
25 | """Fixture creates an instance of HydrateMiddleware per test case.
26 |
27 | Returns:
28 | instance of HydrateMiddleware
29 | """
30 | return HydrateMiddleware()
31 |
32 |
33 | @pytest.mark.asyncio
34 | async def test_preprocess_no_events(hydrate_middleware, event1, mocker: MockerFixture):
35 | """Test that app without on_load is processed correctly.
36 |
37 | Args:
38 | hydrate_middleware: Instance of HydrateMiddleware
39 | event1: An Event.
40 | mocker: pytest mock object.
41 | """
42 | mocker.patch("reflex.state.State.class_subclasses", {TestState})
43 | state = State()
44 | update = await hydrate_middleware.preprocess(
45 | app=App(_state=State),
46 | event=event1,
47 | state=state,
48 | )
49 | assert isinstance(update, StateUpdate)
50 | assert update.delta == state.dict()
51 | assert not update.events
52 |
--------------------------------------------------------------------------------
/tests/units/states/__init__.py:
--------------------------------------------------------------------------------
1 | """Common rx.BaseState subclasses for use in tests."""
2 |
3 | import reflex as rx
4 | from reflex.state import BaseState
5 |
6 | from .mutation import DictMutationTestState, ListMutationTestState, MutableTestState
7 | from .upload import (
8 | ChildFileUploadState,
9 | FileStateBase1,
10 | FileStateBase2,
11 | FileUploadState,
12 | GrandChildFileUploadState,
13 | SubUploadState,
14 | UploadState,
15 | )
16 |
17 |
18 | class GenState(BaseState):
19 | """A state with event handlers that generate multiple updates."""
20 |
21 | value: int
22 |
23 | def go(self, c: int):
24 | """Increment the value c times and update each time.
25 |
26 | Args:
27 | c: The number of times to increment.
28 |
29 | Yields:
30 | After each increment.
31 | """
32 | for _ in range(c):
33 | self.value += 1
34 | yield
35 |
--------------------------------------------------------------------------------
/tests/units/test_telemetry.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from packaging.version import parse as parse_python_version
3 | from pytest_mock import MockerFixture
4 |
5 | from reflex.utils import telemetry
6 |
7 |
8 | def test_telemetry():
9 | """Test that telemetry is sent correctly."""
10 | # Check that the user OS is one of the supported operating systems.
11 | user_os = telemetry.get_os()
12 | assert user_os is not None
13 | assert user_os in ["Linux", "Darwin", "Java", "Windows"]
14 |
15 | # Check that the CPU count and memory are greater than 0.
16 | assert telemetry.get_cpu_count() > 0
17 |
18 | # Check that the available memory is greater than 0
19 | assert telemetry.get_memory() > 0
20 |
21 | # Check that the Reflex version is not None.
22 | assert telemetry.get_reflex_version() is not None
23 |
24 | # Check that the Python version is greater than 3.7.
25 | python_version = telemetry.get_python_version()
26 | assert python_version is not None
27 | assert parse_python_version(python_version) >= parse_python_version("3.7")
28 |
29 |
30 | def test_disable():
31 | """Test that disabling telemetry works."""
32 | assert not telemetry._send("test", telemetry_enabled=False)
33 |
34 |
35 | @pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"])
36 | def test_send(mocker: MockerFixture, event):
37 | httpx_post_mock = mocker.patch("httpx.post")
38 |
39 | # Mock the read_text method of Path
40 | mocker.patch(
41 | "pathlib.Path.read_text",
42 | return_value='{"project_hash": "78285505863498957834586115958872998605"}',
43 | )
44 |
45 | mocker.patch("platform.platform", return_value="Mocked Platform")
46 |
47 | telemetry._send(event, telemetry_enabled=True)
48 | httpx_post_mock.assert_called_once()
49 |
--------------------------------------------------------------------------------
/tests/units/test_testing.py:
--------------------------------------------------------------------------------
1 | """Unit tests for the included testing tools."""
2 |
3 | import pytest
4 |
5 | from reflex.constants import IS_WINDOWS
6 | from reflex.testing import AppHarness
7 |
8 |
9 | @pytest.mark.skip("Slow test that makes network requests.")
10 | def test_app_harness(tmp_path):
11 | """Ensure that AppHarness can compile and start an app.
12 |
13 | Args:
14 | tmp_path: pytest tmp_path fixture
15 | """
16 | # Skip in Windows CI.
17 | if IS_WINDOWS:
18 | return
19 |
20 | def BasicApp():
21 | import reflex as rx
22 |
23 | class State(rx.State):
24 | pass
25 |
26 | app = rx.App(_state=State)
27 | app.add_page(lambda: rx.text("Basic App"), route="/", title="index")
28 | app._compile()
29 |
30 | with AppHarness.create(
31 | root=tmp_path,
32 | app_source=BasicApp,
33 | ) as harness:
34 | assert harness.app_instance is not None
35 | assert harness.backend is not None
36 | assert harness.frontend_url is not None
37 | assert harness.frontend_process is not None
38 | assert harness.frontend_process.poll() is None
39 |
40 | assert harness.frontend_process.poll() is not None
41 |
--------------------------------------------------------------------------------
/tests/units/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/utils/__init__.py
--------------------------------------------------------------------------------
/tests/units/vars/test_base.py:
--------------------------------------------------------------------------------
1 | from collections.abc import Mapping, Sequence
2 |
3 | import pytest
4 |
5 | from reflex.state import State
6 | from reflex.vars.base import computed_var, figure_out_type
7 |
8 |
9 | class CustomDict(dict[str, str]):
10 | """A custom dict with generic arguments."""
11 |
12 |
13 | class ChildCustomDict(CustomDict):
14 | """A child of CustomDict."""
15 |
16 |
17 | class GenericDict(dict):
18 | """A generic dict with no generic arguments."""
19 |
20 |
21 | class ChildGenericDict(GenericDict):
22 | """A child of GenericDict."""
23 |
24 |
25 | @pytest.mark.parametrize(
26 | ("value", "expected"),
27 | [
28 | (1, int),
29 | (1.0, float),
30 | ("a", str),
31 | ([1, 2, 3], Sequence[int]),
32 | ([1, 2.0, "a"], Sequence[int | float | str]),
33 | ({"a": 1, "b": 2}, Mapping[str, int]),
34 | ({"a": 1, 2: "b"}, Mapping[int | str, str | int]),
35 | (CustomDict(), CustomDict),
36 | (ChildCustomDict(), ChildCustomDict),
37 | (GenericDict({1: 1}), Mapping[int, int]),
38 | (ChildGenericDict({1: 1}), Mapping[int, int]),
39 | ],
40 | )
41 | def test_figure_out_type(value, expected):
42 | assert figure_out_type(value) == expected
43 |
44 |
45 | def test_computed_var_replace() -> None:
46 | class StateTest(State):
47 | @computed_var(cache=True)
48 | def cv(self) -> int:
49 | return 1
50 |
51 | cv = StateTest.cv
52 | assert cv._var_type is int
53 |
54 | replaced = cv._replace(_var_type=float)
55 | assert replaced._var_type is float
56 |
--------------------------------------------------------------------------------