InteractiveTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Interactivity;
  5. using Avalonia.VisualTree;
  6. using Xunit;
  7. namespace Avalonia.Base.UnitTests.Interactivity
  8. {
  9. public class InteractiveTests
  10. {
  11. [Fact]
  12. public void Direct_Event_Should_Go_Straight_To_Source()
  13. {
  14. var ev = new RoutedEvent("test", RoutingStrategies.Direct, typeof(RoutedEventArgs), typeof(TestInteractive));
  15. var invoked = new List<string?>();
  16. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  17. var target = CreateTree(ev, handler, RoutingStrategies.Direct);
  18. var args = new RoutedEventArgs(ev, target);
  19. target.RaiseEvent(args);
  20. Assert.Equal(new[] { "2b" }, invoked);
  21. }
  22. [Fact]
  23. public void Direct_Event_Should_Have_Route_Set_To_Direct()
  24. {
  25. var ev = new RoutedEvent("test", RoutingStrategies.Direct, typeof(RoutedEventArgs), typeof(TestInteractive));
  26. bool called = false;
  27. EventHandler<RoutedEventArgs> handler = (s, e) =>
  28. {
  29. Assert.Equal(RoutingStrategies.Direct, e.Route);
  30. called = true;
  31. };
  32. var target = CreateTree(ev, handler, RoutingStrategies.Direct);
  33. var args = new RoutedEventArgs(ev, target);
  34. target.RaiseEvent(args);
  35. Assert.True(called);
  36. }
  37. [Fact]
  38. public void Bubbling_Event_Should_Bubble_Up()
  39. {
  40. var ev = new RoutedEvent("test", RoutingStrategies.Bubble, typeof(RoutedEventArgs), typeof(TestInteractive));
  41. var invoked = new List<string?>();
  42. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  43. var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
  44. var args = new RoutedEventArgs(ev, target);
  45. target.RaiseEvent(args);
  46. Assert.Equal(new[] { "2b", "1" }, invoked);
  47. }
  48. [Fact]
  49. public void Tunneling_Event_Should_Tunnel()
  50. {
  51. var ev = new RoutedEvent("test", RoutingStrategies.Tunnel, typeof(RoutedEventArgs), typeof(TestInteractive));
  52. var invoked = new List<string?>();
  53. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  54. var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
  55. var args = new RoutedEventArgs(ev, target);
  56. target.RaiseEvent(args);
  57. Assert.Equal(new[] { "1", "2b" }, invoked);
  58. }
  59. [Fact]
  60. public void Tunneling_Bubbling_Event_Should_Tunnel_Then_Bubble_Up()
  61. {
  62. var ev = new RoutedEvent(
  63. "test",
  64. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  65. typeof(RoutedEventArgs),
  66. typeof(TestInteractive));
  67. var invoked = new List<string?>();
  68. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  69. var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
  70. var args = new RoutedEventArgs(ev, target);
  71. target.RaiseEvent(args);
  72. Assert.Equal(new[] { "1", "2b", "2b", "1" }, invoked);
  73. }
  74. [Fact]
  75. public void Events_Should_Have_Route_Set()
  76. {
  77. var ev = new RoutedEvent(
  78. "test",
  79. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  80. typeof(RoutedEventArgs),
  81. typeof(TestInteractive));
  82. var invoked = new List<RoutingStrategies>();
  83. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(e.Route);
  84. var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
  85. var args = new RoutedEventArgs(ev, target);
  86. target.RaiseEvent(args);
  87. Assert.Equal(new[]
  88. {
  89. RoutingStrategies.Tunnel,
  90. RoutingStrategies.Tunnel,
  91. RoutingStrategies.Bubble,
  92. RoutingStrategies.Bubble,
  93. },
  94. invoked);
  95. }
  96. [Fact]
  97. public void Handled_Bubbled_Event_Should_Not_Propogate_Further()
  98. {
  99. var ev = new RoutedEvent("test", RoutingStrategies.Bubble, typeof(RoutedEventArgs), typeof(TestInteractive));
  100. var invoked = new List<string?>();
  101. EventHandler<RoutedEventArgs> handler = (s, e) =>
  102. {
  103. var t = (TestInteractive)s!;
  104. invoked.Add(t.Name);
  105. e.Handled = t.Name == "2b";
  106. };
  107. var target = CreateTree(ev, handler, RoutingStrategies.Bubble);
  108. var args = new RoutedEventArgs(ev, target);
  109. target.RaiseEvent(args);
  110. Assert.Equal(new[] { "2b" }, invoked);
  111. }
  112. [Fact]
  113. public void Handled_Tunnelled_Event_Should_Not_Propogate_Further()
  114. {
  115. var ev = new RoutedEvent(
  116. "test",
  117. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  118. typeof(RoutedEventArgs),
  119. typeof(TestInteractive));
  120. var invoked = new List<string?>();
  121. EventHandler<RoutedEventArgs> handler = (s, e) =>
  122. {
  123. var t = (TestInteractive)s!;
  124. invoked.Add(t.Name);
  125. e.Handled = t.Name == "2b";
  126. };
  127. var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
  128. var args = new RoutedEventArgs(ev, target);
  129. target.RaiseEvent(args);
  130. Assert.Equal(new[] { "1", "2b" }, invoked);
  131. }
  132. [Fact]
  133. public void Direct_Subscription_Should_Not_Catch_Tunneling_Or_Bubbling()
  134. {
  135. var ev = new RoutedEvent(
  136. "test",
  137. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  138. typeof(RoutedEventArgs),
  139. typeof(TestInteractive));
  140. var count = 0;
  141. EventHandler<RoutedEventArgs> handler = (s, e) =>
  142. {
  143. ++count;
  144. };
  145. var target = CreateTree(ev, handler, RoutingStrategies.Direct);
  146. var args = new RoutedEventArgs(ev, target);
  147. target.RaiseEvent(args);
  148. Assert.Equal(0, count);
  149. }
  150. [Fact]
  151. public void Bubbling_Subscription_Should_Not_Catch_Tunneling()
  152. {
  153. var ev = new RoutedEvent(
  154. "test",
  155. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  156. typeof(RoutedEventArgs),
  157. typeof(TestInteractive));
  158. var count = 0;
  159. EventHandler<RoutedEventArgs> handler = (s, e) =>
  160. {
  161. Assert.Equal(RoutingStrategies.Bubble, e.Route);
  162. ++count;
  163. };
  164. var target = CreateTree(ev, handler, RoutingStrategies.Bubble);
  165. var args = new RoutedEventArgs(ev, target);
  166. target.RaiseEvent(args);
  167. Assert.Equal(2, count);
  168. }
  169. [Fact]
  170. public void Tunneling_Subscription_Should_Not_Catch_Bubbling()
  171. {
  172. var ev = new RoutedEvent(
  173. "test",
  174. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  175. typeof(RoutedEventArgs),
  176. typeof(TestInteractive));
  177. var count = 0;
  178. EventHandler<RoutedEventArgs> handler = (s, e) =>
  179. {
  180. Assert.Equal(RoutingStrategies.Tunnel, e.Route);
  181. ++count;
  182. };
  183. var target = CreateTree(ev, handler, RoutingStrategies.Tunnel);
  184. var args = new RoutedEventArgs(ev, target);
  185. target.RaiseEvent(args);
  186. Assert.Equal(2, count);
  187. }
  188. [Fact]
  189. public void Event_Should_Should_Keep_Propogating_To_HandedEventsToo_Handlers()
  190. {
  191. var ev = new RoutedEvent(
  192. "test",
  193. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  194. typeof(RoutedEventArgs),
  195. typeof(TestInteractive));
  196. var invoked = new List<string?>();
  197. EventHandler<RoutedEventArgs> handler = (s, e) =>
  198. {
  199. invoked.Add(((TestInteractive)s!).Name);
  200. e.Handled = true;
  201. };
  202. var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel, true);
  203. var args = new RoutedEventArgs(ev, target);
  204. target.RaiseEvent(args);
  205. Assert.Equal(new[] { "1", "2b", "2b", "1" }, invoked);
  206. }
  207. [Fact]
  208. public void Direct_Class_Handlers_Should_Be_Called()
  209. {
  210. var ev = new RoutedEvent(
  211. "test",
  212. RoutingStrategies.Direct,
  213. typeof(RoutedEventArgs),
  214. typeof(TestInteractive));
  215. var invoked = new List<string?>();
  216. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  217. var target = CreateTree(ev, null, 0);
  218. ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Direct);
  219. var args = new RoutedEventArgs(ev, target);
  220. target.RaiseEvent(args);
  221. Assert.Equal(new[] { "2b" }, invoked);
  222. }
  223. [Fact]
  224. public void Tunneling_Class_Handlers_Should_Be_Called()
  225. {
  226. var ev = new RoutedEvent(
  227. "test",
  228. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  229. typeof(RoutedEventArgs),
  230. typeof(TestInteractive));
  231. var invoked = new List<string?>();
  232. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  233. var target = CreateTree(ev, null, 0);
  234. ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Tunnel);
  235. var args = new RoutedEventArgs(ev, target);
  236. target.RaiseEvent(args);
  237. Assert.Equal(new[] { "1", "2b" }, invoked);
  238. }
  239. [Fact]
  240. public void Bubbling_Class_Handlers_Should_Be_Called()
  241. {
  242. var ev = new RoutedEvent(
  243. "test",
  244. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  245. typeof(RoutedEventArgs),
  246. typeof(TestInteractive));
  247. var invoked = new List<string?>();
  248. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  249. var target = CreateTree(ev, null, 0);
  250. ev.AddClassHandler(typeof(TestInteractive), handler, RoutingStrategies.Bubble);
  251. var args = new RoutedEventArgs(ev, target);
  252. target.RaiseEvent(args);
  253. Assert.Equal(new[] { "2b", "1" }, invoked);
  254. }
  255. [Fact]
  256. public void Typed_Class_Handlers_Should_Be_Called()
  257. {
  258. var ev = new RoutedEvent<RoutedEventArgs>(
  259. "test",
  260. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  261. typeof(TestInteractive));
  262. var target = CreateTree(ev, null, 0);
  263. ev.AddClassHandler<TestInteractive>((x, e) => x.ClassHandler(e), RoutingStrategies.Bubble);
  264. var args = new RoutedEventArgs(ev, target);
  265. target.RaiseEvent(args);
  266. Assert.True(target.ClassHandlerInvoked);
  267. var interactive = target.GetVisualParent<TestInteractive>();
  268. Assert.NotNull(interactive);
  269. Assert.True(interactive.ClassHandlerInvoked);
  270. }
  271. [Fact]
  272. public void Typed_Class_Handlers_Should_Be_Called_For_Handled_Events()
  273. {
  274. var ev = new RoutedEvent<RoutedEventArgs>(
  275. "test",
  276. RoutingStrategies.Bubble | RoutingStrategies.Tunnel,
  277. typeof(TestInteractive));
  278. var target = CreateTree(ev, null, 0);
  279. ev.AddClassHandler<TestInteractive>((x, e) => x.MarkEventAsHandled(e), RoutingStrategies.Bubble);
  280. ev.AddClassHandler<TestInteractive>((x, e) => x.ClassHandler(e), RoutingStrategies.Bubble, handledEventsToo: true);
  281. var args = new RoutedEventArgs(ev, target);
  282. target.RaiseEvent(args);
  283. Assert.True(args.Handled);
  284. Assert.True(target.ClassHandlerInvoked);
  285. var interactive = target.GetVisualParent<TestInteractive>();
  286. Assert.NotNull(interactive);
  287. Assert.True(interactive.ClassHandlerInvoked);
  288. }
  289. [Fact]
  290. public void GetObservable_Should_Listen_To_Event()
  291. {
  292. var ev = new RoutedEvent<RoutedEventArgs>("test", RoutingStrategies.Direct, typeof(TestInteractive));
  293. var target = new TestInteractive();
  294. var called = 0;
  295. var subscription = target.GetObservable(ev).Subscribe(_ => ++called);
  296. var args = new RoutedEventArgs(ev, target);
  297. target.RaiseEvent(args);
  298. subscription.Dispose();
  299. target.RaiseEvent(args);
  300. Assert.Equal(1, called);
  301. }
  302. [Fact]
  303. public void Removing_Control_In_Handler_Should_Not_Stop_Event()
  304. {
  305. // Issue #3176
  306. var ev = new RoutedEvent("test", RoutingStrategies.Bubble, typeof(RoutedEventArgs), typeof(TestInteractive));
  307. var invoked = new List<string?>();
  308. EventHandler<RoutedEventArgs> handler = (s, e) => invoked.Add(((TestInteractive)s!).Name);
  309. var parent = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
  310. var target = (Interactive)parent.GetVisualChildren().Single();
  311. EventHandler<RoutedEventArgs> removeHandler = (s, e) =>
  312. {
  313. parent.Children = Array.Empty<Visual>();
  314. };
  315. target.AddHandler(ev, removeHandler);
  316. var args = new RoutedEventArgs(ev, target);
  317. target.RaiseEvent(args);
  318. Assert.Equal(new[] { "3", "2b", "1" }, invoked);
  319. }
  320. private static TestInteractive CreateTree(
  321. RoutedEvent ev,
  322. EventHandler<RoutedEventArgs>? handler,
  323. RoutingStrategies handlerRoutes,
  324. bool handledEventsToo = false)
  325. {
  326. TestInteractive target;
  327. var tree = new TestInteractive
  328. {
  329. Name = "1",
  330. Children = new[]
  331. {
  332. new TestInteractive
  333. {
  334. Name = "2a",
  335. },
  336. (target = new TestInteractive
  337. {
  338. Name = "2b",
  339. Children = new[]
  340. {
  341. new TestInteractive
  342. {
  343. Name = "3",
  344. },
  345. },
  346. }),
  347. }
  348. };
  349. if (handler != null)
  350. {
  351. foreach (var i in tree.GetSelfAndVisualDescendants().Cast<Interactive>())
  352. {
  353. i.AddHandler(ev, handler, handlerRoutes, handledEventsToo);
  354. }
  355. }
  356. return target;
  357. }
  358. private class TestInteractive : Interactive
  359. {
  360. public bool ClassHandlerInvoked { get; private set; }
  361. public new string? Name { get; set; }
  362. public IEnumerable<Visual> Children
  363. {
  364. get
  365. {
  366. return VisualChildren.AsEnumerable();
  367. }
  368. set
  369. {
  370. VisualChildren.Clear();
  371. VisualChildren.AddRange(value);
  372. }
  373. }
  374. public void ClassHandler(RoutedEventArgs e)
  375. {
  376. ClassHandlerInvoked = true;
  377. }
  378. public void MarkEventAsHandled(RoutedEventArgs e)
  379. {
  380. e.Handled = true;
  381. }
  382. }
  383. }
  384. }