"""Test that DebounceInput collapses nested forms.""" import pytest import reflex as rx from reflex.components.core.debounce import DEFAULT_DEBOUNCE_TIMEOUT from reflex.state import BaseState from reflex.vars.base import LiteralVar, Var def test_create_no_child(): """DebounceInput raises RuntimeError if no child is provided.""" with pytest.raises(RuntimeError): _ = rx.debounce_input() def test_create_no_child_recursive(): """DebounceInput raises RuntimeError if no child is provided.""" with pytest.raises(RuntimeError): _ = rx.debounce_input(rx.debounce_input(rx.debounce_input())) def test_create_many_child(): """DebounceInput raises RuntimeError if more than 1 child is provided.""" with pytest.raises(RuntimeError): _ = rx.debounce_input("foo", "bar") def test_create_no_on_change(): """DebounceInput raises ValueError if child has no on_change handler.""" with pytest.raises(ValueError): _ = rx.debounce_input(rx.input()) class S(BaseState): """Example state for debounce tests.""" value: str = "" @rx.event def on_change(self, v: str): """Dummy on_change handler. Args: v: The changed value. """ pass def test_render_child_props(): """DebounceInput should render props from child component.""" tag = rx.debounce_input( rx.input( foo="bar", baz="quuc", value="real", on_change=S.on_change, ) )._render() assert "css" in tag.props and isinstance(tag.props["css"], rx.vars.Var) for prop in ["foo", "bar", "baz", "quuc"]: assert prop in str(tag.props["css"]) assert tag.props["value"].equals( rx.cond(Var.create("real").is_not_none(), "real", "") ) assert len(tag.props["onChange"].events) == 1 assert tag.props["onChange"].events[0].handler == S.on_change assert tag.contents == "" def test_render_with_class_name(): tag = rx.debounce_input( rx.input( on_change=S.on_change, class_name="foo baz", ) )._render() assert isinstance(tag.props["className"], rx.vars.Var) assert "foo baz" in str(tag.props["className"]) def test_render_with_ref(): tag = rx.debounce_input( rx.input( on_change=S.on_change, id="foo_bar", ) )._render() assert isinstance(tag.props["inputRef"], rx.vars.Var) assert "foo_bar" in str(tag.props["inputRef"]) def test_render_with_key(): tag = rx.debounce_input( rx.input( on_change=S.on_change, key="foo_bar", ) )._render() assert isinstance(tag.props["key"], rx.vars.Var) assert "foo_bar" in str(tag.props["key"]) def test_render_with_special_props(): special_prop = Var(_js_expr="{foo_bar}") tag = rx.debounce_input( rx.input( on_change=S.on_change, special_props=[special_prop], ) )._render() assert len(tag.special_props) == 1 assert next(iter(tag.special_props)).equals(special_prop) def test_event_triggers(): debounced_input = rx.debounce_input( rx.input( on_change=S.on_change, ) ) assert tuple(debounced_input.get_event_triggers()) == ( *rx.Component.create().get_event_triggers(), # default event triggers "on_change", ) def test_render_child_props_recursive(): """DebounceInput should render props from child component. If the child component is a DebounceInput, then props will be copied from it recursively. """ tag = rx.debounce_input( rx.debounce_input( rx.debounce_input( rx.debounce_input( rx.input( foo="bar", baz="quuc", value="real", on_change=S.on_change, ), value="inner", debounce_timeout=666, force_notify_on_blur=False, ), debounce_timeout=42, ), value="outer", ), force_notify_by_enter=False, )._render() assert "css" in tag.props and isinstance(tag.props["css"], rx.vars.Var) for prop in ["foo", "bar", "baz", "quuc"]: assert prop in str(tag.props["css"]) assert tag.props["value"].equals(LiteralVar.create("outer")) assert tag.props["forceNotifyOnBlur"]._js_expr == "false" assert tag.props["forceNotifyByEnter"]._js_expr == "false" assert tag.props["debounceTimeout"]._js_expr == "42" assert len(tag.props["onChange"].events) == 1 assert tag.props["onChange"].events[0].handler == S.on_change assert tag.contents == "" def test_full_control_implicit_debounce(): """DebounceInput is used when value and on_change are used together.""" tag = rx.input( value=S.value, on_change=S.on_change, )._render() assert tag.props["debounceTimeout"]._js_expr == str(DEFAULT_DEBOUNCE_TIMEOUT) assert len(tag.props["onChange"].events) == 1 assert tag.props["onChange"].events[0].handler == S.on_change assert tag.contents == "" def test_full_control_implicit_debounce_text_area(): """DebounceInput is used when value and on_change are used together.""" tag = rx.text_area( value=S.value, on_change=S.on_change, )._render() assert tag.props["debounceTimeout"]._js_expr == str(DEFAULT_DEBOUNCE_TIMEOUT) assert len(tag.props["onChange"].events) == 1 assert tag.props["onChange"].events[0].handler == S.on_change assert tag.contents == ""