
* use serializer system * add checks for unsupported operands * and and or are now supported * format * remove unnecessary call to JSON * put base before rest * fix failing testcase * add hinting to get static analysis to complain * damn * big changes * get typeguard from extensions * please darglint * dangit darglint * remove one from vars * add without data and use it in plotly * DARGLINT * change format for special props * add pyi * delete instances of Var.create * modify client state to work * fixed so much * remove every Var.create * delete all basevar stuff * checkpoint * fix pyi * get older python to work * dangit darglint * add simple fix to last failing testcase * remove var name unwrapped and put client state on immutable var * fix older python * fox event issues * change forms pyi * make test less strict * use rx state directly * add typeignore to page_id * implement foreach * delete .web states folder silly * update reflex chakra * fix issue when on mount or on unmount is not set * nuke Var * run pyi * import immutablevar in critical location * delete unwrap vars * bring back array ref * fix style props in app * /health endpoint for K8 Liveness and Readiness probes (#3855) * Added API Endpoint * Added API Endpoint * Added Unit Tests * Added Unit Tests * main * Apply suggestions from Code Review * Fix Ruff Formatting * Update Socket Events * Async Functions * Update find_replace (#3886) * [REF-3592]Promote `rx.progress` from radix themes (#3878) * Promote `rx.progress` from radix themes * fix pyi * add warning when accessing `rx._x.progress` * Use correct flexgen backend URL (#3891) * Remove demo template (#3888) * gitignore .web (#3885) * update overflowY in AUTO_HEIGHT_JS from hidden to scroll (#3882) * Retain mutability inside `async with self` block (#3884) When emitting a state update, restore `_self_mutable` to the value it had previously so that `yield` in the middle of `async with self` does not result in an immutable StateProxy. Fix #3869 * Include child imports in markdown component_map (#3883) If a component in the markdown component_map contains children components, use `_get_all_imports` to recursively enumerate them. Fix #3880 * [REF-3570] Remove deprecated REDIS_URL syntax (#3892) * mixin computed vars should only be applied to highest level state (#3833) * improve state hierarchy validation, drop old testing special case (#3894) * fix var dependency dicts (#3842) * Adding array to array pluck operation. (#3868) * fix initial state without cv fallback (#3670) * add fragment to foreach (#3877) * Update docker-example (#3324) * /health endpoint for K8 Liveness and Readiness probes (#3855) * Added API Endpoint * Added API Endpoint * Added Unit Tests * Added Unit Tests * main * Apply suggestions from Code Review * Fix Ruff Formatting * Update Socket Events * Async Functions * /health endpoint for K8 Liveness and Readiness probes (#3855) * Added API Endpoint * Added API Endpoint * Added Unit Tests * Added Unit Tests * main * Apply suggestions from Code Review * Fix Ruff Formatting * Update Socket Events * Async Functions * Merge branch 'main' into use-old-serializer-in-literalvar * [REF-3570] Remove deprecated REDIS_URL syntax (#3892) * /health endpoint for K8 Liveness and Readiness probes (#3855) * Added API Endpoint * Added API Endpoint * Added Unit Tests * Added Unit Tests * main * Apply suggestions from Code Review * Fix Ruff Formatting * Update Socket Events * Async Functions * [REF-3570] Remove deprecated REDIS_URL syntax (#3892) * remove extra var Co-authored-by: Masen Furer <m_github@0x26.net> * resolve typo * write better doc for var.create * return var value when we know it's literal var * fix unit test * less bloat for ToOperations * simplify ImmutableComputedVar.__get__ (#3902) * simplify ImmutableComputedVar.__get__ * ruff it --------- Co-authored-by: Samarth Bhadane <samarthbhadane119@gmail.com> Co-authored-by: Elijah Ahianyo <elijahahianyo@gmail.com> Co-authored-by: Masen Furer <m_github@0x26.net> Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com> Co-authored-by: Vishnu Deva <vishnu.deva12@gmail.com> Co-authored-by: abulvenz <a.eismann@senbax.de>
314 lines
12 KiB
Python
314 lines
12 KiB
Python
from typing import Dict, List, Tuple
|
|
|
|
import pytest
|
|
|
|
import reflex as rx
|
|
from reflex.components.core.match import Match
|
|
from reflex.ivars.base import ImmutableVar
|
|
from reflex.state import BaseState
|
|
from reflex.utils.exceptions import MatchTypeError
|
|
|
|
|
|
class MatchState(BaseState):
|
|
"""A test state."""
|
|
|
|
value: int = 0
|
|
num: int = 5
|
|
string: str = "random string"
|
|
|
|
|
|
def test_match_components():
|
|
"""Test matching cases with return values as components."""
|
|
match_case_tuples = (
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
([1, 2], rx.text("third value")),
|
|
("random", rx.text("fourth value")),
|
|
({"foo": "bar"}, rx.text("fifth value")),
|
|
(MatchState.num + 1, rx.text("sixth value")),
|
|
rx.text("default value"),
|
|
)
|
|
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
|
match_dict = match_comp.render() # type: ignore
|
|
assert match_dict["name"] == "Fragment"
|
|
|
|
[match_child] = match_dict["children"]
|
|
|
|
assert match_child["name"] == "match"
|
|
assert str(match_child["cond"]) == f"{MatchState.get_name()}.value"
|
|
|
|
match_cases = match_child["match_cases"]
|
|
assert len(match_cases) == 6
|
|
|
|
assert match_cases[0][0]._var_name == "1"
|
|
assert match_cases[0][0]._var_type == int
|
|
first_return_value_render = match_cases[0][1].render()
|
|
assert first_return_value_render["name"] == "RadixThemesText"
|
|
assert first_return_value_render["children"][0]["contents"] == '{"first value"}'
|
|
|
|
assert match_cases[1][0]._var_name == "2"
|
|
assert match_cases[1][0]._var_type == int
|
|
assert match_cases[1][1]._var_name == "3"
|
|
assert match_cases[1][1]._var_type == int
|
|
second_return_value_render = match_cases[1][2].render()
|
|
assert second_return_value_render["name"] == "RadixThemesText"
|
|
assert second_return_value_render["children"][0]["contents"] == '{"second value"}'
|
|
|
|
assert match_cases[2][0]._var_name == "[1, 2]"
|
|
assert match_cases[2][0]._var_type == List[int]
|
|
third_return_value_render = match_cases[2][1].render()
|
|
assert third_return_value_render["name"] == "RadixThemesText"
|
|
assert third_return_value_render["children"][0]["contents"] == '{"third value"}'
|
|
|
|
assert match_cases[3][0]._var_name == '"random"'
|
|
assert match_cases[3][0]._var_type == str
|
|
fourth_return_value_render = match_cases[3][1].render()
|
|
assert fourth_return_value_render["name"] == "RadixThemesText"
|
|
assert fourth_return_value_render["children"][0]["contents"] == '{"fourth value"}'
|
|
|
|
assert match_cases[4][0]._var_name == '({ ["foo"] : "bar" })'
|
|
assert match_cases[4][0]._var_type == Dict[str, str]
|
|
fifth_return_value_render = match_cases[4][1].render()
|
|
assert fifth_return_value_render["name"] == "RadixThemesText"
|
|
assert fifth_return_value_render["children"][0]["contents"] == '{"fifth value"}'
|
|
|
|
assert match_cases[5][0]._var_name == f"({MatchState.get_name()}.num + 1)"
|
|
assert match_cases[5][0]._var_type == int
|
|
fifth_return_value_render = match_cases[5][1].render()
|
|
assert fifth_return_value_render["name"] == "RadixThemesText"
|
|
assert fifth_return_value_render["children"][0]["contents"] == '{"sixth value"}'
|
|
|
|
default = match_child["default"].render()
|
|
|
|
assert default["name"] == "RadixThemesText"
|
|
assert default["children"][0]["contents"] == '{"default value"}'
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cases, expected",
|
|
[
|
|
(
|
|
(
|
|
(1, "first"),
|
|
(2, 3, "second value"),
|
|
([1, 2], "third-value"),
|
|
("random", "fourth_value"),
|
|
({"foo": "bar"}, "fifth value"),
|
|
(MatchState.num + 1, "sixth value"),
|
|
(f"{MatchState.value} - string", MatchState.string),
|
|
(MatchState.string, f"{MatchState.value} - string"),
|
|
"default value",
|
|
),
|
|
f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return '
|
|
'("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): '
|
|
'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); '
|
|
f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): '
|
|
f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: '
|
|
'return ("default value"); break;};})()',
|
|
),
|
|
(
|
|
(
|
|
(1, "first"),
|
|
(2, 3, "second value"),
|
|
([1, 2], "third-value"),
|
|
("random", "fourth_value"),
|
|
({"foo": "bar"}, "fifth value"),
|
|
(MatchState.num + 1, "sixth value"),
|
|
(f"{MatchState.value} - string", MatchState.string),
|
|
(MatchState.string, f"{MatchState.value} - string"),
|
|
MatchState.string,
|
|
),
|
|
f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return '
|
|
'("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): '
|
|
'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); '
|
|
f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): '
|
|
f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: '
|
|
f"return ({MatchState.get_name()}.string); break;}};}})()",
|
|
),
|
|
],
|
|
)
|
|
def test_match_vars(cases, expected):
|
|
"""Test matching cases with return values as Vars.
|
|
|
|
Args:
|
|
cases: The match cases.
|
|
expected: The expected var full name.
|
|
"""
|
|
match_comp = Match.create(MatchState.value, *cases)
|
|
assert isinstance(match_comp, ImmutableVar)
|
|
assert str(match_comp) == expected
|
|
|
|
|
|
def test_match_on_component_without_default():
|
|
"""Test that matching cases with return values as components returns a Fragment
|
|
as the default case if not provided.
|
|
"""
|
|
from reflex.components.base.fragment import Fragment
|
|
|
|
match_case_tuples = (
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
)
|
|
|
|
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
|
default = match_comp.render()["children"][0]["default"] # type: ignore
|
|
|
|
assert isinstance(default, Fragment)
|
|
|
|
|
|
def test_match_on_var_no_default():
|
|
"""Test that an error is thrown when cases with return Values as Var do not have a default case."""
|
|
match_case_tuples = (
|
|
(1, "red"),
|
|
(2, 3, "blue"),
|
|
([1, 2], "green"),
|
|
)
|
|
|
|
with pytest.raises(
|
|
ValueError,
|
|
match="For cases with return types as Vars, a default case must be provided",
|
|
):
|
|
Match.create(MatchState.value, *match_case_tuples)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"match_case",
|
|
[
|
|
(
|
|
(1, "red"),
|
|
(2, 3, "blue"),
|
|
"black",
|
|
([1, 2], "green"),
|
|
),
|
|
(
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
([1, 2], rx.text("third value")),
|
|
rx.text("default value"),
|
|
("random", rx.text("fourth value")),
|
|
({"foo": "bar"}, rx.text("fifth value")),
|
|
(MatchState.num + 1, rx.text("sixth value")),
|
|
),
|
|
],
|
|
)
|
|
def test_match_default_not_last_arg(match_case):
|
|
"""Test that an error is thrown when the default case is not the last arg.
|
|
|
|
Args:
|
|
match_case: The cases to match.
|
|
"""
|
|
with pytest.raises(
|
|
ValueError,
|
|
match="rx.match should have tuples of cases and a default case as the last argument.",
|
|
):
|
|
Match.create(MatchState.value, *match_case)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"match_case",
|
|
[
|
|
(
|
|
(1, "red"),
|
|
(2, 3, "blue"),
|
|
("green",),
|
|
"black",
|
|
),
|
|
(
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
([1, 2],),
|
|
rx.text("default value"),
|
|
),
|
|
],
|
|
)
|
|
def test_match_case_tuple_elements(match_case):
|
|
"""Test that a match has at least 2 elements(a condition and a return value).
|
|
|
|
Args:
|
|
match_case: The cases to match.
|
|
"""
|
|
with pytest.raises(
|
|
ValueError,
|
|
match="A case tuple should have at least a match case element and a return value.",
|
|
):
|
|
Match.create(MatchState.value, *match_case)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cases, error_msg",
|
|
[
|
|
(
|
|
(
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
([1, 2], rx.text("third value")),
|
|
("random", "red"),
|
|
({"foo": "bar"}, "green"),
|
|
(MatchState.num + 1, "black"),
|
|
rx.text("default value"),
|
|
),
|
|
'Match cases should have the same return types. Case 3 with return value `"red"` of type '
|
|
"<class 'reflex.ivars.sequence.LiteralStringVar'> is not <class 'reflex.components.component.BaseComponent'>",
|
|
),
|
|
(
|
|
(
|
|
("random", "red"),
|
|
({"foo": "bar"}, "green"),
|
|
(MatchState.num + 1, "black"),
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
([1, 2], rx.text("third value")),
|
|
rx.text("default value"),
|
|
),
|
|
'Match cases should have the same return types. Case 3 with return value `<RadixThemesText as={"p"}> {"first value"} </RadixThemesText>` '
|
|
"of type <class 'reflex.components.radix.themes.typography.text.Text'> is not <class 'reflex.ivars.base.ImmutableVar'>",
|
|
),
|
|
],
|
|
)
|
|
def test_match_different_return_types(cases: Tuple, error_msg: str):
|
|
"""Test that an error is thrown when the return values are of different types.
|
|
|
|
Args:
|
|
cases: The match cases.
|
|
error_msg: Expected error message.
|
|
"""
|
|
with pytest.raises(MatchTypeError, match=error_msg):
|
|
Match.create(MatchState.value, *cases)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"match_case",
|
|
[
|
|
(
|
|
(1, "red"),
|
|
(2, 3, "blue"),
|
|
([1, 2], "green"),
|
|
"black",
|
|
"white",
|
|
),
|
|
(
|
|
(1, rx.text("first value")),
|
|
(2, 3, rx.text("second value")),
|
|
([1, 2], rx.text("third value")),
|
|
("random", rx.text("fourth value")),
|
|
({"foo": "bar"}, rx.text("fifth value")),
|
|
(MatchState.num + 1, rx.text("sixth value")),
|
|
rx.text("default value"),
|
|
rx.text("another default value"),
|
|
),
|
|
],
|
|
)
|
|
def test_match_multiple_default_cases(match_case):
|
|
"""Test that there is only one default case.
|
|
|
|
Args:
|
|
match_case: the cases to match.
|
|
"""
|
|
with pytest.raises(ValueError, match="rx.match can only have one default case."):
|
|
Match.create(MatchState.value, *match_case)
|
|
|
|
|
|
def test_match_no_cond():
|
|
with pytest.raises(ValueError):
|
|
_ = Match.create(None)
|