test_event.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import json
  2. import pytest
  3. from reflex import event
  4. from reflex.event import Event, EventHandler, EventSpec, fix_events
  5. from reflex.state import State
  6. from reflex.utils import format
  7. from reflex.vars import Var
  8. def make_var(value) -> Var:
  9. """Make a variable.
  10. Args:
  11. value: The value of the var.
  12. Returns:
  13. The var.
  14. """
  15. var = Var.create(value)
  16. assert var is not None
  17. return var
  18. def test_create_event():
  19. """Test creating an event."""
  20. event = Event(token="token", name="state.do_thing", payload={"arg": "value"})
  21. assert event.token == "token"
  22. assert event.name == "state.do_thing"
  23. assert event.payload == {"arg": "value"}
  24. def test_call_event_handler():
  25. """Test that calling an event handler creates an event spec."""
  26. def test_fn():
  27. pass
  28. test_fn.__qualname__ = "test_fn"
  29. def test_fn_with_args(_, arg1, arg2):
  30. pass
  31. test_fn_with_args.__qualname__ = "test_fn_with_args"
  32. handler = EventHandler(fn=test_fn)
  33. event_spec = handler()
  34. assert event_spec.handler == handler
  35. assert event_spec.args == ()
  36. assert format.format_event(event_spec) == 'Event("test_fn", {})'
  37. handler = EventHandler(fn=test_fn_with_args)
  38. event_spec = handler(make_var("first"), make_var("second"))
  39. # Test passing vars as args.
  40. assert event_spec.handler == handler
  41. assert event_spec.args[0][0].equals(Var.create_safe("arg1"))
  42. assert event_spec.args[0][1].equals(Var.create_safe("first"))
  43. assert event_spec.args[1][0].equals(Var.create_safe("arg2"))
  44. assert event_spec.args[1][1].equals(Var.create_safe("second"))
  45. assert (
  46. format.format_event(event_spec)
  47. == 'Event("test_fn_with_args", {arg1:first,arg2:second})'
  48. )
  49. # Passing args as strings should format differently.
  50. event_spec = handler("first", "second") # type: ignore
  51. assert (
  52. format.format_event(event_spec)
  53. == 'Event("test_fn_with_args", {arg1:`first`,arg2:`second`})'
  54. )
  55. first, second = 123, "456"
  56. handler = EventHandler(fn=test_fn_with_args)
  57. event_spec = handler(first, second) # type: ignore
  58. assert (
  59. format.format_event(event_spec)
  60. == 'Event("test_fn_with_args", {arg1:123,arg2:`456`})'
  61. )
  62. assert event_spec.handler == handler
  63. assert event_spec.args[0][0].equals(Var.create_safe("arg1"))
  64. assert event_spec.args[0][1].equals(Var.create_safe(first))
  65. assert event_spec.args[1][0].equals(Var.create_safe("arg2"))
  66. assert event_spec.args[1][1].equals(Var.create_safe(second))
  67. handler = EventHandler(fn=test_fn_with_args)
  68. with pytest.raises(TypeError):
  69. handler(test_fn) # type: ignore
  70. @pytest.mark.parametrize(
  71. ("arg1", "arg2"),
  72. (
  73. (1, 2),
  74. (1, "2"),
  75. ({"a": 1}, {"b": 2}),
  76. ),
  77. )
  78. def test_fix_events(arg1, arg2):
  79. """Test that chaining an event handler with args formats the payload correctly.
  80. Args:
  81. arg1: The first arg passed to the handler.
  82. arg2: The second arg passed to the handler.
  83. """
  84. def test_fn_with_args(_, arg1, arg2):
  85. pass
  86. test_fn_with_args.__qualname__ = "test_fn_with_args"
  87. handler = EventHandler(fn=test_fn_with_args)
  88. event_spec = handler(arg1, arg2)
  89. event = fix_events([event_spec], token="foo")[0]
  90. assert event.name == test_fn_with_args.__qualname__
  91. assert event.token == "foo"
  92. assert event.payload == {"arg1": arg1, "arg2": arg2}
  93. @pytest.mark.parametrize(
  94. "input,output",
  95. [
  96. (("/path", None), 'Event("_redirect", {path:`/path`,external:false})'),
  97. (("/path", True), 'Event("_redirect", {path:`/path`,external:true})'),
  98. (("/path", False), 'Event("_redirect", {path:`/path`,external:false})'),
  99. (
  100. (Var.create_safe("path"), None),
  101. 'Event("_redirect", {path:path,external:false})',
  102. ),
  103. ],
  104. )
  105. def test_event_redirect(input, output):
  106. """Test the event redirect function.
  107. Args:
  108. input: The input for running the test.
  109. output: The expected output to validate the test.
  110. """
  111. path, external = input
  112. if external is None:
  113. spec = event.redirect(path)
  114. else:
  115. spec = event.redirect(path, external=external)
  116. assert isinstance(spec, EventSpec)
  117. assert spec.handler.fn.__qualname__ == "_redirect"
  118. # this asserts need comment about what it's testing (they fail with Var as input)
  119. # assert spec.args[0][0].equals(Var.create_safe("path"))
  120. # assert spec.args[0][1].equals(Var.create_safe("/path"))
  121. assert format.format_event(spec) == output
  122. def test_event_console_log():
  123. """Test the event console log function."""
  124. spec = event.console_log("message")
  125. assert isinstance(spec, EventSpec)
  126. assert spec.handler.fn.__qualname__ == "_console"
  127. assert spec.args[0][0].equals(Var.create_safe("message"))
  128. assert spec.args[0][1].equals(Var.create_safe("message"))
  129. assert format.format_event(spec) == 'Event("_console", {message:`message`})'
  130. spec = event.console_log(Var.create_safe("message"))
  131. assert format.format_event(spec) == 'Event("_console", {message:message})'
  132. def test_event_window_alert():
  133. """Test the event window alert function."""
  134. spec = event.window_alert("message")
  135. assert isinstance(spec, EventSpec)
  136. assert spec.handler.fn.__qualname__ == "_alert"
  137. assert spec.args[0][0].equals(Var.create_safe("message"))
  138. assert spec.args[0][1].equals(Var.create_safe("message"))
  139. assert format.format_event(spec) == 'Event("_alert", {message:`message`})'
  140. spec = event.window_alert(Var.create_safe("message"))
  141. assert format.format_event(spec) == 'Event("_alert", {message:message})'
  142. def test_set_focus():
  143. """Test the event set focus function."""
  144. spec = event.set_focus("input1")
  145. assert isinstance(spec, EventSpec)
  146. assert spec.handler.fn.__qualname__ == "_set_focus"
  147. assert spec.args[0][0].equals(Var.create_safe("ref"))
  148. assert spec.args[0][1].equals(Var.create_safe("ref_input1"))
  149. assert format.format_event(spec) == 'Event("_set_focus", {ref:ref_input1})'
  150. spec = event.set_focus("input1")
  151. assert format.format_event(spec) == 'Event("_set_focus", {ref:ref_input1})'
  152. def test_set_value():
  153. """Test the event window alert function."""
  154. spec = event.set_value("input1", "")
  155. assert isinstance(spec, EventSpec)
  156. assert spec.handler.fn.__qualname__ == "_set_value"
  157. assert spec.args[0][0].equals(Var.create_safe("ref"))
  158. assert spec.args[0][1].equals(Var.create_safe("ref_input1"))
  159. assert spec.args[1][0].equals(Var.create_safe("value"))
  160. assert spec.args[1][1].equals(Var.create_safe(""))
  161. assert format.format_event(spec) == 'Event("_set_value", {ref:ref_input1,value:``})'
  162. spec = event.set_value("input1", Var.create_safe("message"))
  163. assert (
  164. format.format_event(spec)
  165. == 'Event("_set_value", {ref:ref_input1,value:message})'
  166. )
  167. def test_remove_cookie():
  168. """Test the event remove_cookie."""
  169. spec = event.remove_cookie("testkey")
  170. assert isinstance(spec, EventSpec)
  171. assert spec.handler.fn.__qualname__ == "_remove_cookie"
  172. assert spec.args[0][0].equals(Var.create_safe("key"))
  173. assert spec.args[0][1].equals(Var.create_safe("testkey"))
  174. assert spec.args[1][0].equals(Var.create_safe("options"))
  175. assert spec.args[1][1].equals(Var.create_safe({"path": "/"}))
  176. assert (
  177. format.format_event(spec)
  178. == 'Event("_remove_cookie", {key:`testkey`,options:{"path": "/"}})'
  179. )
  180. def test_remove_cookie_with_options():
  181. """Test the event remove_cookie with options."""
  182. options = {
  183. "path": "/foo",
  184. "domain": "example.com",
  185. "secure": True,
  186. "sameSite": "strict",
  187. }
  188. spec = event.remove_cookie("testkey", options)
  189. assert isinstance(spec, EventSpec)
  190. assert spec.handler.fn.__qualname__ == "_remove_cookie"
  191. assert spec.args[0][0].equals(Var.create_safe("key"))
  192. assert spec.args[0][1].equals(Var.create_safe("testkey"))
  193. assert spec.args[1][0].equals(Var.create_safe("options"))
  194. assert spec.args[1][1].equals(Var.create_safe(options))
  195. assert (
  196. format.format_event(spec)
  197. == f'Event("_remove_cookie", {{key:`testkey`,options:{json.dumps(options)}}})'
  198. )
  199. def test_clear_local_storage():
  200. """Test the event clear_local_storage."""
  201. spec = event.clear_local_storage()
  202. assert isinstance(spec, EventSpec)
  203. assert spec.handler.fn.__qualname__ == "_clear_local_storage"
  204. assert not spec.args
  205. assert format.format_event(spec) == 'Event("_clear_local_storage", {})'
  206. def test_remove_local_storage():
  207. """Test the event remove_local_storage."""
  208. spec = event.remove_local_storage("testkey")
  209. assert isinstance(spec, EventSpec)
  210. assert spec.handler.fn.__qualname__ == "_remove_local_storage"
  211. assert spec.args[0][0].equals(Var.create_safe("key"))
  212. assert spec.args[0][1].equals(Var.create_safe("testkey"))
  213. assert (
  214. format.format_event(spec) == 'Event("_remove_local_storage", {key:`testkey`})'
  215. )
  216. def test_event_actions():
  217. """Test DOM event actions, like stopPropagation and preventDefault."""
  218. # EventHandler
  219. handler = EventHandler(fn=lambda: None)
  220. assert not handler.event_actions
  221. sp_handler = handler.stop_propagation
  222. assert handler is not sp_handler
  223. assert sp_handler.event_actions == {"stopPropagation": True}
  224. pd_handler = handler.prevent_default
  225. assert handler is not pd_handler
  226. assert pd_handler.event_actions == {"preventDefault": True}
  227. both_handler = sp_handler.prevent_default
  228. assert both_handler is not sp_handler
  229. assert both_handler.event_actions == {
  230. "stopPropagation": True,
  231. "preventDefault": True,
  232. }
  233. assert not handler.event_actions
  234. # Convert to EventSpec should carry event actions
  235. sp_handler2 = handler.stop_propagation
  236. spec = sp_handler2()
  237. assert spec.event_actions == {"stopPropagation": True}
  238. assert spec.event_actions == sp_handler2.event_actions
  239. assert spec.event_actions is not sp_handler2.event_actions
  240. # But it should be a copy!
  241. assert spec.event_actions is not sp_handler2.event_actions
  242. spec2 = spec.prevent_default
  243. assert spec is not spec2
  244. assert spec2.event_actions == {"stopPropagation": True, "preventDefault": True}
  245. assert spec2.event_actions != spec.event_actions
  246. # The original handler should still not be touched.
  247. assert not handler.event_actions
  248. def test_event_actions_on_state():
  249. class EventActionState(State):
  250. def handler(self):
  251. pass
  252. handler = EventActionState.handler
  253. assert isinstance(handler, EventHandler)
  254. assert not handler.event_actions
  255. sp_handler = EventActionState.handler.stop_propagation
  256. assert sp_handler.event_actions == {"stopPropagation": True}
  257. # should NOT affect other references to the handler
  258. assert not handler.event_actions