123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- import re
- from collections.abc import Mapping, Sequence
- import pytest
- import reflex as rx
- from reflex.components.component import Component
- from reflex.components.core.match import Match
- from reflex.state import BaseState
- from reflex.utils.exceptions import MatchTypeError
- from reflex.vars.base import Var
- 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)
- assert isinstance(match_comp, Component)
- match_dict = match_comp.render()
- 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]._js_expr == "1"
- assert match_cases[0][0]._var_type is int
- first_return_value_render = match_cases[0][1]
- assert first_return_value_render["name"] == "RadixThemesText"
- assert first_return_value_render["children"][0]["contents"] == '"first value"'
- assert match_cases[1][0]._js_expr == "2"
- assert match_cases[1][0]._var_type is int
- assert match_cases[1][1]._js_expr == "3"
- assert match_cases[1][1]._var_type is int
- second_return_value_render = match_cases[1][2]
- assert second_return_value_render["name"] == "RadixThemesText"
- assert second_return_value_render["children"][0]["contents"] == '"second value"'
- assert match_cases[2][0]._js_expr == "[1, 2]"
- assert match_cases[2][0]._var_type == Sequence[int]
- third_return_value_render = match_cases[2][1]
- assert third_return_value_render["name"] == "RadixThemesText"
- assert third_return_value_render["children"][0]["contents"] == '"third value"'
- assert match_cases[3][0]._js_expr == '"random"'
- assert match_cases[3][0]._var_type is str
- fourth_return_value_render = match_cases[3][1]
- assert fourth_return_value_render["name"] == "RadixThemesText"
- assert fourth_return_value_render["children"][0]["contents"] == '"fourth value"'
- assert match_cases[4][0]._js_expr == '({ ["foo"] : "bar" })'
- assert match_cases[4][0]._var_type == Mapping[str, str]
- fifth_return_value_render = match_cases[4][1]
- assert fifth_return_value_render["name"] == "RadixThemesText"
- assert fifth_return_value_render["children"][0]["contents"] == '"fifth value"'
- assert match_cases[5][0]._js_expr == f"({MatchState.get_name()}.num + 1)"
- assert match_cases[5][0]._var_type is int
- fifth_return_value_render = match_cases[5][1]
- assert fifth_return_value_render["name"] == "RadixThemesText"
- assert fifth_return_value_render["children"][0]["contents"] == '"sixth value"'
- default = match_child["default"]
- 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, Var)
- 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)
- assert isinstance(match_comp, Component)
- default = match_comp.render()["children"][0]["default"]
- assert isinstance(default, dict) and default["name"] == Fragment.__name__
- 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.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 `jsx( RadixThemesText, {as:"p"}, "first value" ,)` '
- "of type <class 'reflex.components.radix.themes.typography.text.Text'> is not <class 'reflex.vars.base.Var'>",
- ),
- ],
- )
- 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=re.escape(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)
|