123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792 |
- from __future__ import annotations
- import datetime
- from typing import Any, List
- import pytest
- from reflex.components.tags.tag import Tag
- from reflex.event import EventChain, EventHandler, EventSpec, FrontendEvent
- from reflex.style import Style
- from reflex.utils import format
- from reflex.vars import BaseVar, Var
- from tests.test_state import (
- ChildState,
- ChildState2,
- DateTimeState,
- GrandchildState,
- TestState,
- )
- def mock_event(arg):
- pass
- @pytest.mark.parametrize(
- "input,output",
- [
- ("{", "}"),
- ("(", ")"),
- ("[", "]"),
- ("<", ">"),
- ('"', '"'),
- ("'", "'"),
- ],
- )
- def test_get_close_char(input: str, output: str):
- """Test getting the close character for a given open character.
- Args:
- input: The open character.
- output: The expected close character.
- """
- assert format.get_close_char(input) == output
- @pytest.mark.parametrize(
- "text,open,expected",
- [
- ("", "{", False),
- ("{wrap}", "{", True),
- ("{wrap", "{", False),
- ("{wrap}", "(", False),
- ("(wrap)", "(", True),
- ],
- )
- def test_is_wrapped(text: str, open: str, expected: bool):
- """Test checking if a string is wrapped in the given open and close characters.
- Args:
- text: The text to check.
- open: The open character.
- expected: Whether the text is wrapped.
- """
- assert format.is_wrapped(text, open) == expected
- @pytest.mark.parametrize(
- "text,open,check_first,num,expected",
- [
- ("", "{", True, 1, "{}"),
- ("wrap", "{", True, 1, "{wrap}"),
- ("wrap", "(", True, 1, "(wrap)"),
- ("wrap", "(", True, 2, "((wrap))"),
- ("(wrap)", "(", True, 1, "(wrap)"),
- ("{wrap}", "{", True, 2, "{wrap}"),
- ("(wrap)", "{", True, 1, "{(wrap)}"),
- ("(wrap)", "(", False, 1, "((wrap))"),
- ],
- )
- def test_wrap(text: str, open: str, expected: str, check_first: bool, num: int):
- """Test wrapping a string.
- Args:
- text: The text to wrap.
- open: The open character.
- expected: The expected output string.
- check_first: Whether to check if the text is already wrapped.
- num: The number of times to wrap the text.
- """
- assert format.wrap(text, open, check_first=check_first, num=num) == expected
- @pytest.mark.parametrize(
- "text,indent_level,expected",
- [
- ("", 2, ""),
- ("hello", 2, "hello"),
- ("hello\nworld", 2, " hello\n world\n"),
- ("hello\nworld", 4, " hello\n world\n"),
- (" hello\n world", 2, " hello\n world\n"),
- ],
- )
- def test_indent(text: str, indent_level: int, expected: str, windows_platform: bool):
- """Test indenting a string.
- Args:
- text: The text to indent.
- indent_level: The number of spaces to indent by.
- expected: The expected output string.
- windows_platform: Whether the system is windows.
- """
- assert format.indent(text, indent_level) == (
- expected.replace("\n", "\r\n") if windows_platform else expected
- )
- @pytest.mark.parametrize(
- "input,output",
- [
- ("", ""),
- ("hello", "hello"),
- ("Hello", "hello"),
- ("camelCase", "camel_case"),
- ("camelTwoHumps", "camel_two_humps"),
- ("_start_with_underscore", "_start_with_underscore"),
- ("__start_with_double_underscore", "__start_with_double_underscore"),
- ("kebab-case", "kebab_case"),
- ("double-kebab-case", "double_kebab_case"),
- (":start-with-colon", ":start_with_colon"),
- (":-start-with-colon-dash", ":_start_with_colon_dash"),
- ],
- )
- def test_to_snake_case(input: str, output: str):
- """Test converting strings to snake case.
- Args:
- input: The input string.
- output: The expected output string.
- """
- assert format.to_snake_case(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ("", ""),
- ("hello", "hello"),
- ("Hello", "Hello"),
- ("snake_case", "snakeCase"),
- ("snake_case_two", "snakeCaseTwo"),
- ("kebab-case", "kebabCase"),
- ("kebab-case-two", "kebabCaseTwo"),
- ("snake_kebab-case", "snakeKebabCase"),
- ("_hover", "_hover"),
- ("-starts-with-hyphen", "-startsWithHyphen"),
- ("--starts-with-double-hyphen", "--startsWithDoubleHyphen"),
- ("_starts_with_underscore", "_startsWithUnderscore"),
- ("__starts_with_double_underscore", "__startsWithDoubleUnderscore"),
- (":start-with-colon", ":startWithColon"),
- (":-start-with-colon-dash", ":StartWithColonDash"),
- ],
- )
- def test_to_camel_case(input: str, output: str):
- """Test converting strings to camel case.
- Args:
- input: The input string.
- output: The expected output string.
- """
- assert format.to_camel_case(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ("", ""),
- ("hello", "Hello"),
- ("Hello", "Hello"),
- ("snake_case", "SnakeCase"),
- ("snake_case_two", "SnakeCaseTwo"),
- ],
- )
- def test_to_title_case(input: str, output: str):
- """Test converting strings to title case.
- Args:
- input: The input string.
- output: The expected output string.
- """
- assert format.to_title_case(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ("", ""),
- ("hello", "hello"),
- ("Hello", "hello"),
- ("snake_case", "snake-case"),
- ("snake_case_two", "snake-case-two"),
- (":startWithColon", ":start-with-colon"),
- (":StartWithColonDash", ":-start-with-colon-dash"),
- (":start_with_colon", ":start-with-colon"),
- (":_start_with_colon_dash", ":-start-with-colon-dash"),
- ],
- )
- def test_to_kebab_case(input: str, output: str):
- """Test converting strings to kebab case.
- Args:
- input: the input string.
- output: the output string.
- """
- assert format.to_kebab_case(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ("", "{``}"),
- ("hello", "{`hello`}"),
- ("hello world", "{`hello world`}"),
- ("hello=`world`", "{`hello=\\`world\\``}"),
- ],
- )
- def test_format_string(input: str, output: str):
- """Test formating the input as JS string literal.
- Args:
- input: the input string.
- output: the output string.
- """
- assert format.format_string(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- (Var.create(value="test"), "{`test`}"),
- (Var.create(value="test", _var_is_local=True), "{`test`}"),
- (Var.create(value="test", _var_is_local=False), "{test}"),
- (Var.create(value="test", _var_is_string=True), "{`test`}"),
- (Var.create(value="test", _var_is_string=False), "{`test`}"),
- (Var.create(value="test", _var_is_local=False, _var_is_string=False), "{test}"),
- ],
- )
- def test_format_var(input: Var, output: str):
- assert format.format_var(input) == output
- @pytest.mark.parametrize(
- "route,format_case,expected",
- [
- ("", True, "index"),
- ("/", True, "index"),
- ("custom-route", True, "custom-route"),
- ("custom-route", False, "custom-route"),
- ("custom-route/", True, "custom-route"),
- ("custom-route/", False, "custom-route"),
- ("/custom-route", True, "custom-route"),
- ("/custom-route", False, "custom-route"),
- ("/custom_route", True, "custom-route"),
- ("/custom_route", False, "custom_route"),
- ("/CUSTOM_route", True, "custom-route"),
- ("/CUSTOM_route", False, "CUSTOM_route"),
- ],
- )
- def test_format_route(route: str, format_case: bool, expected: bool):
- """Test formatting a route.
- Args:
- route: The route to format.
- format_case: Whether to change casing to snake_case.
- expected: The expected formatted route.
- """
- assert format.format_route(route, format_case=format_case) == expected
- @pytest.mark.parametrize(
- "condition,true_value,false_value,is_prop,expected",
- [
- ("cond", "<C1>", '""', False, '{isTrue(cond) ? <C1> : ""}'),
- ("cond", "<C1>", "<C2>", False, "{isTrue(cond) ? <C1> : <C2>}"),
- (
- "cond",
- Var.create_safe("<C1>"),
- "<C2>",
- False,
- "{isTrue(cond) ? <C1> : <C2>}",
- ),
- (
- "cond",
- Var.create_safe("<C1>"),
- Var.create_safe("<C2>"),
- False,
- "{isTrue(cond) ? <C1> : <C2>}",
- ),
- (
- "cond",
- Var.create_safe("<C1>", _var_is_local=False),
- Var.create_safe("<C2>"),
- False,
- "{isTrue(cond) ? ${<C1>} : <C2>}",
- ),
- (
- "cond",
- Var.create_safe("<C1>", _var_is_string=True),
- Var.create_safe("<C2>"),
- False,
- "{isTrue(cond) ? {`<C1>`} : <C2>}",
- ),
- ("cond", "<C1>", '""', True, 'isTrue(cond) ? `<C1>` : `""`'),
- ("cond", "<C1>", "<C2>", True, "isTrue(cond) ? `<C1>` : `<C2>`"),
- (
- "cond",
- Var.create_safe("<C1>"),
- "<C2>",
- True,
- "isTrue(cond) ? <C1> : `<C2>`",
- ),
- (
- "cond",
- Var.create_safe("<C1>"),
- Var.create_safe("<C2>"),
- True,
- "isTrue(cond) ? <C1> : <C2>",
- ),
- (
- "cond",
- Var.create_safe("<C1>", _var_is_local=False),
- Var.create_safe("<C2>"),
- True,
- "isTrue(cond) ? <C1> : <C2>",
- ),
- (
- "cond",
- Var.create_safe("<C1>"),
- Var.create_safe("<C2>", _var_is_local=False),
- True,
- "isTrue(cond) ? <C1> : <C2>",
- ),
- (
- "cond",
- Var.create_safe("<C1>", _var_is_string=True),
- Var.create_safe("<C2>"),
- True,
- "isTrue(cond) ? `<C1>` : <C2>",
- ),
- ],
- )
- def test_format_cond(
- condition: str,
- true_value: str | Var,
- false_value: str | Var,
- is_prop: bool,
- expected: str,
- ):
- """Test formatting a cond.
- Args:
- condition: The condition to check.
- true_value: The value to return if the condition is true.
- false_value: The value to return if the condition is false.
- is_prop: Whether the values are rendered as props or not.
- expected: The expected output string.
- """
- orig_true_value = (
- true_value._replace() if isinstance(true_value, Var) else Var.create_safe("")
- )
- orig_false_value = (
- false_value._replace() if isinstance(false_value, Var) else Var.create_safe("")
- )
- assert format.format_cond(condition, true_value, false_value, is_prop) == expected
- # Ensure the formatting operation didn't change the original Var
- if isinstance(true_value, Var):
- assert true_value.equals(orig_true_value)
- if isinstance(false_value, Var):
- assert false_value.equals(orig_false_value)
- @pytest.mark.parametrize(
- "condition, match_cases, default,expected",
- [
- (
- "state__state.value",
- [
- [Var.create(1), Var.create("red", _var_is_string=True)],
- [Var.create(2), Var.create(3), Var.create("blue", _var_is_string=True)],
- [TestState.mapping, TestState.num1],
- [
- Var.create(f"{TestState.map_key}-key", _var_is_string=True),
- Var.create("return-key", _var_is_string=True),
- ],
- ],
- Var.create("yellow", _var_is_string=True),
- "(() => { switch (JSON.stringify(state__state.value)) {case JSON.stringify(1): return (`red`); break;case JSON.stringify(2): case JSON.stringify(3): "
- "return (`blue`); break;case JSON.stringify(test_state.mapping): return "
- "(test_state.num1); break;case JSON.stringify(`${test_state.map_key}-key`): return (`return-key`);"
- " break;default: return (`yellow`); break;};})()",
- )
- ],
- )
- def test_format_match(
- condition: str, match_cases: List[BaseVar], default: BaseVar, expected: str
- ):
- """Test formatting a match statement.
- Args:
- condition: The condition to match.
- match_cases: List of match cases to be matched.
- default: Catchall case for the match statement.
- expected: The expected string output.
- """
- assert format.format_match(condition, match_cases, default) == expected
- @pytest.mark.parametrize(
- "prop,formatted",
- [
- ("string", '"string"'),
- ("{wrapped_string}", "{wrapped_string}"),
- (True, "{true}"),
- (False, "{false}"),
- (123, "{123}"),
- (3.14, "{3.14}"),
- ([1, 2, 3], "{[1, 2, 3]}"),
- (["a", "b", "c"], '{["a", "b", "c"]}'),
- ({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
- ({"a": 'foo "bar" baz'}, r'{{"a": "foo \"bar\" baz"}}'),
- (
- {
- "a": 'foo "{ "bar" }" baz',
- "b": BaseVar(_var_name="val", _var_type="str"),
- },
- r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
- ),
- (
- EventChain(
- events=[EventSpec(handler=EventHandler(fn=mock_event))],
- args_spec=lambda: [],
- ),
- '{(_e) => addEvents([Event("mock_event", {})], (_e), {})}',
- ),
- (
- EventChain(
- events=[
- EventSpec(
- handler=EventHandler(fn=mock_event),
- args=(
- (
- Var.create_safe("arg"),
- BaseVar(
- _var_name="_e",
- _var_type=FrontendEvent,
- ).target.value,
- ),
- ),
- )
- ],
- args_spec=lambda: [],
- ),
- '{(_e) => addEvents([Event("mock_event", {arg:_e.target.value})], (_e), {})}',
- ),
- (
- EventChain(
- events=[EventSpec(handler=EventHandler(fn=mock_event))],
- args_spec=lambda: [],
- event_actions={"stopPropagation": True},
- ),
- '{(_e) => addEvents([Event("mock_event", {})], (_e), {"stopPropagation": true})}',
- ),
- (
- EventChain(
- events=[EventSpec(handler=EventHandler(fn=mock_event))],
- args_spec=lambda: [],
- event_actions={"preventDefault": True},
- ),
- '{(_e) => addEvents([Event("mock_event", {})], (_e), {"preventDefault": true})}',
- ),
- ({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
- (BaseVar(_var_name="var", _var_type="int"), "{var}"),
- (
- BaseVar(
- _var_name="_",
- _var_type=Any,
- _var_is_local=True,
- _var_is_string=False,
- ),
- "{_}",
- ),
- (
- BaseVar(_var_name='state.colors["a"]', _var_type="str"),
- '{state.colors["a"]}',
- ),
- ({"a": BaseVar(_var_name="val", _var_type="str")}, '{{"a": val}}'),
- ({"a": BaseVar(_var_name='"val"', _var_type="str")}, '{{"a": "val"}}'),
- (
- {"a": BaseVar(_var_name='state.colors["val"]', _var_type="str")},
- '{{"a": state.colors["val"]}}',
- ),
- # tricky real-world case from markdown component
- (
- {
- "h1": f"{{({{node, ...props}}) => <Heading {{...props}} {''.join(Tag(name='', props=Style({'as_': 'h1'})).format_props())} />}}"
- },
- '{{"h1": ({node, ...props}) => <Heading {...props} as={`h1`} />}}',
- ),
- ],
- )
- def test_format_prop(prop: Var, formatted: str):
- """Test that the formatted value of an prop is correct.
- Args:
- prop: The prop to test.
- formatted: The expected formatted value.
- """
- assert format.format_prop(prop) == formatted
- @pytest.mark.parametrize(
- "single_props,key_value_props,output",
- [
- (["string"], {"key": 42}, ["key={42}", "string"]),
- ],
- )
- def test_format_props(single_props, key_value_props, output):
- """Test the result of formatting a set of props (both single and keyvalue).
- Args:
- single_props: the list of single props
- key_value_props: the dict of key value props
- output: the expected output
- """
- assert format.format_props(*single_props, **key_value_props) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- (EventHandler(fn=mock_event), ("", "mock_event")),
- ],
- )
- def test_get_handler_parts(input, output):
- assert format.get_event_handler_parts(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- (TestState.do_something, "test_state.do_something"),
- (ChildState.change_both, "test_state.child_state.change_both"),
- (
- GrandchildState.do_nothing,
- "test_state.child_state.grandchild_state.do_nothing",
- ),
- ],
- )
- def test_format_event_handler(input, output):
- """Test formatting an event handler.
- Args:
- input: The event handler input.
- output: The expected output.
- """
- assert format.format_event_handler(input) == output # type: ignore
- @pytest.mark.parametrize(
- "input,output",
- [
- (EventSpec(handler=EventHandler(fn=mock_event)), 'Event("mock_event", {})'),
- ],
- )
- def test_format_event(input, output):
- assert format.format_event(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- (
- EventChain(
- events=[
- EventSpec(handler=EventHandler(fn=mock_event)),
- EventSpec(handler=EventHandler(fn=mock_event)),
- ],
- args_spec=None,
- ),
- 'addEvents([Event("mock_event", {}),Event("mock_event", {})])',
- ),
- (
- EventChain(
- events=[
- EventSpec(handler=EventHandler(fn=mock_event)),
- EventSpec(handler=EventHandler(fn=mock_event)),
- ],
- args_spec=lambda e0: [e0],
- ),
- 'addEvents([Event("mock_event", {}),Event("mock_event", {})])',
- ),
- ],
- )
- def test_format_event_chain(input, output):
- assert format.format_event_chain(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ({"query": {"k1": 1, "k2": 2}}, {"k1": 1, "k2": 2}),
- ({"query": {"k1": 1, "k-2": 2}}, {"k1": 1, "k_2": 2}),
- ],
- )
- def test_format_query_params(input, output):
- assert format.format_query_params(input) == output
- formatted_router = {
- "session": {"client_token": "", "client_ip": "", "session_id": ""},
- "headers": {
- "host": "",
- "origin": "",
- "upgrade": "",
- "connection": "",
- "pragma": "",
- "cache_control": "",
- "user_agent": "",
- "sec_websocket_version": "",
- "sec_websocket_key": "",
- "sec_websocket_extensions": "",
- "accept_encoding": "",
- "accept_language": "",
- },
- "page": {
- "host": "",
- "path": "",
- "raw_path": "",
- "full_path": "",
- "full_raw_path": "",
- "params": {},
- },
- }
- @pytest.mark.parametrize(
- "input, output",
- [
- (
- TestState().dict(), # type: ignore
- {
- TestState.get_full_name(): {
- "array": [1, 2, 3.14],
- "complex": {
- 1: {"prop1": 42, "prop2": "hello"},
- 2: {"prop1": 42, "prop2": "hello"},
- },
- "dt": "1989-11-09 18:53:00+01:00",
- "fig": [],
- "key": "",
- "map_key": "a",
- "mapping": {"a": [1, 2, 3], "b": [4, 5, 6]},
- "num1": 0,
- "num2": 3.14,
- "obj": {"prop1": 42, "prop2": "hello"},
- "sum": 3.14,
- "upper": "",
- "router": formatted_router,
- },
- ChildState.get_full_name(): {
- "count": 23,
- "value": "",
- },
- ChildState2.get_full_name(): {"value": ""},
- GrandchildState.get_full_name(): {"value2": ""},
- },
- ),
- (
- DateTimeState().dict(),
- {
- DateTimeState.get_full_name(): {
- "d": "1989-11-09",
- "dt": "1989-11-09 18:53:00+01:00",
- "t": "18:53:00+01:00",
- "td": "11 days, 0:11:00",
- "router": formatted_router,
- },
- },
- ),
- ],
- )
- def test_format_state(input, output):
- """Test that the format state is correct.
- Args:
- input: The state to format.
- output: The expected formatted state.
- """
- assert format.format_state(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ("input1", "ref_input1"),
- ("input 1", "ref_input_1"),
- ("input-1", "ref_input_1"),
- ("input_1", "ref_input_1"),
- ("a long test?1! name", "ref_a_long_test_1_name"),
- ],
- )
- def test_format_ref(input, output):
- """Test formatting a ref.
- Args:
- input: The name to format.
- output: The expected formatted name.
- """
- assert format.format_ref(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- (("my_array", None), "refs_my_array"),
- (("my_array", Var.create(0)), "refs_my_array[0]"),
- (("my_array", Var.create(1)), "refs_my_array[1]"),
- ],
- )
- def test_format_array_ref(input, output):
- assert format.format_array_ref(input[0], input[1]) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- ("/foo", [("foo", "/foo")]),
- ("/foo/bar", [("foo", "/foo"), ("bar", "/foo/bar")]),
- (
- "/foo/bar/baz",
- [("foo", "/foo"), ("bar", "/foo/bar"), ("baz", "/foo/bar/baz")],
- ),
- ],
- )
- def test_format_breadcrumbs(input, output):
- assert format.format_breadcrumbs(input) == output
- @pytest.mark.parametrize(
- "input, output",
- [
- ("library@^0.1.2", "library"),
- ("library", "library"),
- ("@library@^0.1.2", "@library"),
- ("@library", "@library"),
- ],
- )
- def test_format_library_name(input: str, output: str):
- """Test formating a library name to remove the @version part.
- Args:
- input: the input string.
- output: the output string.
- """
- assert format.format_library_name(input) == output
- @pytest.mark.parametrize(
- "input,output",
- [
- (None, "null"),
- (True, "true"),
- (1, "1"),
- (1.0, "1.0"),
- ([], "[]"),
- ([1, 2, 3], "[1, 2, 3]"),
- ({}, "{}"),
- ({"k1": False, "k2": True}, '{"k1": false, "k2": true}'),
- (
- [datetime.timedelta(1, 1, 1), datetime.timedelta(1, 1, 2)],
- '["1 day, 0:00:01.000001", "1 day, 0:00:01.000002"]',
- ),
- (
- {"key1": datetime.timedelta(1, 1, 1), "key2": datetime.timedelta(1, 1, 2)},
- '{"key1": "1 day, 0:00:01.000001", "key2": "1 day, 0:00:01.000002"}',
- ),
- ],
- )
- def test_json_dumps(input, output):
- assert format.json_dumps(input) == output
|