123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- import re
- 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.base import Var
- class MatchState(BaseState):
- """A test state."""
- value: int = 0
- num: int = 5
- string: str = "random string"
- @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(MatchState.value, *cases) # pyright: ignore[reportCallIssue]
- 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.
- """
- match_case_tuples = (
- (1, rx.text("first value")),
- (2, 3, rx.text("second value")),
- )
- match_comp = match(MatchState.value, *match_case_tuples)
- assert isinstance(match_comp, Var)
- 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(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(MatchState.value, *match_case) # pyright: ignore[reportCallIssue]
- @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(MatchState.value, *match_case) # pyright: ignore[reportCallIssue]
- @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. Expected return types to be of type Component or Var[Component]. Return type of case 3 is <class 'str'>. Return type of case 4 is <class 'str'>. Return type of case 5 is <class 'str'>",
- ),
- (
- (
- ("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. Expected return types to be of type Component or Var[Component]. Return type of case 0 is <class 'str'>. Return type of case 1 is <class 'str'>. Return type of case 2 is <class 'str'>",
- ),
- ],
- )
- 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(MatchState.value, *cases) # pyright: ignore[reportCallIssue]
- @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(MatchState.value, *match_case) # pyright: ignore[reportCallIssue]
- def test_match_no_cond():
- with pytest.raises(ValueError):
- _ = match(None) # pyright: ignore[reportCallIssue]
|