reflex/tests/components/core/test_match.py
benedikt-bartscher 3039b54a75
add module prefix to generated state names (#3214)
* add module prefix to state names

* fix state names in test_app

* update state names in test_state

* fix state names in test_var

* fix state name in test_component

* fix state names in test_format

* fix state names in test_foreach

* fix state names in test_cond

* fix state names in test_datatable

* fix state names in test_colors

* fix state names in test_script

* fix state names in test_match

* fix state name in event1 fixture

* fix pyright and darglint

* fix state names in state_tree

* fix state names in redis only test

* fix state names in test_client_storage

* fix state name in js template

* add `get_state_name` and `get_full_state_name` helpers for `AppHarness`

* fix state names in test_dynamic_routes

* use new state name helpers in test_client_storage

* fix state names in test_event_actions

* fix state names in test_event_chain

* fix state names in test_upload

* fix state name in test_login_flow

* fix state names in test_input

* fix state names in test_form_submit

* ruff

* validate state module names

* wtf is going on here?

* remove comments leftover from refactoring

* adjust new test_add_style_embedded_vars

* fix state name in state.js

* fix integration/test_client_state.py

new SessionStorage feature was added with more full state names that need to be formatted in

* fix pre-commit issues in test_client_storage.py

* adjust test_computed_vars

* adjust safe-guards

* fix redis tests with new exception state

---------

Co-authored-by: Masen Furer <m_github@0x26.net>
2024-07-11 11:13:57 -07:00

314 lines
11 KiB
Python

from typing import Tuple
import pytest
import reflex as rx
from reflex.components.core.match import Match
from reflex.state import BaseState
from reflex.utils.exceptions import MatchTypeError
from reflex.vars import BaseVar
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
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
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, BaseVar)
assert match_comp._var_full_name == 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.vars.BaseVar'> 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.vars.BaseVar'>",
),
],
)
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)