KeyboardEventTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. using Xunit.Abstractions;
  2. // Alias Console to MockConsole so we don't accidentally use Console
  3. namespace Terminal.Gui.ViewTests;
  4. public class KeyboardEventTests (ITestOutputHelper output)
  5. {
  6. public static TheoryData<View, string> AllViews => TestHelpers.GetAllViewsTheoryData ();
  7. /// <summary>
  8. /// This tests that when a new key down event is sent to the view will fire the 3 key-down related
  9. /// events: KeyDown, InvokingKeyBindings, and ProcessKeyDown. Note that KeyUp is independent.
  10. /// </summary>
  11. [Theory]
  12. [MemberData (nameof (AllViews))]
  13. public void AllViews_KeyDown_All_EventsFire (View view, string viewName)
  14. {
  15. if (view == null)
  16. {
  17. output.WriteLine ($"ERROR: Skipping generic view: {viewName}");
  18. return;
  19. }
  20. output.WriteLine ($"Testing {viewName}");
  21. var keyDown = false;
  22. view.KeyDown += (s, a) =>
  23. {
  24. a.Handled = false; // don't handle it so the other events are called
  25. keyDown = true;
  26. };
  27. var invokingKeyBindings = false;
  28. view.InvokingKeyBindings += (s, a) =>
  29. {
  30. a.Handled = false; // don't handle it so the other events are called
  31. invokingKeyBindings = true;
  32. };
  33. var keyDownProcessed = false;
  34. view.ProcessKeyDown += (s, a) =>
  35. {
  36. a.Handled = true;
  37. keyDownProcessed = true;
  38. };
  39. Assert.True (view.NewKeyDownEvent (Key.A)); // this will be true because the ProcessKeyDown event handled it
  40. Assert.True (keyDown);
  41. Assert.True (invokingKeyBindings);
  42. Assert.True (keyDownProcessed);
  43. view.Dispose ();
  44. }
  45. /// <summary>
  46. /// This tests that when a new key up event is sent to the view the view will fire the 1 key-up related event:
  47. /// KeyUp
  48. /// </summary>
  49. [Fact]
  50. public void AllViews_KeyUp_All_EventsFire ()
  51. {
  52. foreach (View view in TestHelpers.GetAllViews ())
  53. {
  54. if (view == null)
  55. {
  56. output.WriteLine ($"ERROR: null view from {nameof (TestHelpers.GetAllViews)}");
  57. continue;
  58. }
  59. output.WriteLine ($"Testing {view.GetType ().Name}");
  60. var keyUp = false;
  61. view.KeyUp += (s, a) =>
  62. {
  63. a.Handled = true;
  64. keyUp = true;
  65. };
  66. Assert.True (view.NewKeyUpEvent (Key.A)); // this will be true because the KeyUp event handled it
  67. Assert.True (keyUp);
  68. view.Dispose ();
  69. }
  70. }
  71. [Theory]
  72. [InlineData (true, false, false)]
  73. [InlineData (true, true, false)]
  74. [InlineData (true, true, true)]
  75. public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, bool control)
  76. {
  77. var keyDown = false;
  78. var keyPressed = false;
  79. var keyUp = false;
  80. var view = new OnKeyTestView ();
  81. view.CancelVirtualMethods = false;
  82. view.KeyDown += (s, e) =>
  83. {
  84. Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
  85. Assert.Equal (shift, e.IsShift);
  86. Assert.Equal (alt, e.IsAlt);
  87. Assert.Equal (control, e.IsCtrl);
  88. Assert.False (keyDown);
  89. Assert.False (view.OnKeyDownContinued);
  90. keyDown = true;
  91. };
  92. view.ProcessKeyDown += (s, e) => { keyPressed = true; };
  93. view.KeyUp += (s, e) =>
  94. {
  95. Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
  96. Assert.Equal (shift, e.IsShift);
  97. Assert.Equal (alt, e.IsAlt);
  98. Assert.Equal (control, e.IsCtrl);
  99. Assert.False (keyUp);
  100. Assert.False (view.OnKeyUpContinued);
  101. keyUp = true;
  102. };
  103. //view.ProcessKeyDownEvent (new (Key.Null | (shift ? Key.ShiftMask : 0) | (alt ? Key.AltMask : 0) | (control ? Key.CtrlMask : 0)));
  104. //Assert.True (keyDown);
  105. //Assert.True (view.OnKeyDownWasCalled);
  106. //Assert.True (view.OnProcessKeyDownWasCalled);
  107. view.NewKeyDownEvent (
  108. new (
  109. KeyCode.Null
  110. | (shift ? KeyCode.ShiftMask : 0)
  111. | (alt ? KeyCode.AltMask : 0)
  112. | (control ? KeyCode.CtrlMask : 0)
  113. )
  114. );
  115. Assert.True (keyPressed);
  116. Assert.True (view.OnKeyDownContinued);
  117. Assert.True (view.OnKeyPressedContinued);
  118. view.NewKeyUpEvent (
  119. new (
  120. KeyCode.Null
  121. | (shift ? KeyCode.ShiftMask : 0)
  122. | (alt ? KeyCode.AltMask : 0)
  123. | (control ? KeyCode.CtrlMask : 0)
  124. )
  125. );
  126. Assert.True (keyUp);
  127. Assert.True (view.OnKeyUpContinued);
  128. }
  129. [Fact]
  130. public void InvokingKeyBindings_Handled_Cancels ()
  131. {
  132. var view = new View ();
  133. var keyPressInvoked = false;
  134. var invokingKeyBindingsInvoked = false;
  135. var processKeyPressInvoked = false;
  136. var setHandledTo = false;
  137. view.KeyDown += (s, e) =>
  138. {
  139. keyPressInvoked = true;
  140. Assert.False (e.Handled);
  141. Assert.Equal (KeyCode.N, e.KeyCode);
  142. };
  143. view.InvokingKeyBindings += (s, e) =>
  144. {
  145. invokingKeyBindingsInvoked = true;
  146. e.Handled = setHandledTo;
  147. Assert.Equal (setHandledTo, e.Handled);
  148. Assert.Equal (KeyCode.N, e.KeyCode);
  149. };
  150. view.ProcessKeyDown += (s, e) =>
  151. {
  152. processKeyPressInvoked = true;
  153. processKeyPressInvoked = true;
  154. Assert.False (e.Handled);
  155. Assert.Equal (KeyCode.N, e.KeyCode);
  156. };
  157. view.NewKeyDownEvent (Key.N);
  158. Assert.True (keyPressInvoked);
  159. Assert.True (invokingKeyBindingsInvoked);
  160. Assert.True (processKeyPressInvoked);
  161. keyPressInvoked = false;
  162. invokingKeyBindingsInvoked = false;
  163. processKeyPressInvoked = false;
  164. setHandledTo = true;
  165. view.NewKeyDownEvent (Key.N);
  166. Assert.True (keyPressInvoked);
  167. Assert.True (invokingKeyBindingsInvoked);
  168. Assert.False (processKeyPressInvoked);
  169. }
  170. [Fact]
  171. public void InvokingKeyBindings_Handled_True_Stops_Processing ()
  172. {
  173. var keyDown = false;
  174. var invokingKeyBindings = false;
  175. var keyPressed = false;
  176. var view = new OnKeyTestView ();
  177. Assert.True (view.CanFocus);
  178. view.CancelVirtualMethods = false;
  179. view.KeyDown += (s, e) =>
  180. {
  181. Assert.Equal (KeyCode.A, e.KeyCode);
  182. Assert.False (keyDown);
  183. Assert.False (view.OnKeyDownContinued);
  184. e.Handled = false;
  185. keyDown = true;
  186. };
  187. view.InvokingKeyBindings += (s, e) =>
  188. {
  189. Assert.Equal (KeyCode.A, e.KeyCode);
  190. Assert.False (keyPressed);
  191. Assert.False (view.OnInvokingKeyBindingsContinued);
  192. e.Handled = true;
  193. invokingKeyBindings = true;
  194. };
  195. view.ProcessKeyDown += (s, e) =>
  196. {
  197. Assert.Equal (KeyCode.A, e.KeyCode);
  198. Assert.False (keyPressed);
  199. Assert.False (view.OnKeyPressedContinued);
  200. e.Handled = true;
  201. keyPressed = true;
  202. };
  203. view.NewKeyDownEvent (Key.A);
  204. Assert.True (keyDown);
  205. Assert.True (invokingKeyBindings);
  206. Assert.False (keyPressed);
  207. Assert.True (view.OnKeyDownContinued);
  208. Assert.False (view.OnInvokingKeyBindingsContinued);
  209. Assert.False (view.OnKeyPressedContinued);
  210. }
  211. [Fact]
  212. public void KeyDown_Handled_True_Stops_Processing ()
  213. {
  214. var keyDown = false;
  215. var invokingKeyBindings = false;
  216. var keyPressed = false;
  217. var view = new OnKeyTestView ();
  218. Assert.True (view.CanFocus);
  219. view.CancelVirtualMethods = false;
  220. view.KeyDown += (s, e) =>
  221. {
  222. Assert.Equal (KeyCode.A, e.KeyCode);
  223. Assert.False (keyDown);
  224. Assert.False (view.OnKeyDownContinued);
  225. e.Handled = true;
  226. keyDown = true;
  227. };
  228. view.InvokingKeyBindings += (s, e) =>
  229. {
  230. Assert.Equal (KeyCode.A, e.KeyCode);
  231. Assert.False (keyPressed);
  232. Assert.False (view.OnInvokingKeyBindingsContinued);
  233. e.Handled = true;
  234. invokingKeyBindings = true;
  235. };
  236. view.ProcessKeyDown += (s, e) =>
  237. {
  238. Assert.Equal (KeyCode.A, e.KeyCode);
  239. Assert.False (keyPressed);
  240. Assert.False (view.OnKeyPressedContinued);
  241. e.Handled = true;
  242. keyPressed = true;
  243. };
  244. view.NewKeyDownEvent (Key.A);
  245. Assert.True (keyDown);
  246. Assert.False (invokingKeyBindings);
  247. Assert.False (keyPressed);
  248. Assert.False (view.OnKeyDownContinued);
  249. Assert.False (view.OnInvokingKeyBindingsContinued);
  250. Assert.False (view.OnKeyPressedContinued);
  251. }
  252. [Fact]
  253. public void KeyPress_Handled_Cancels ()
  254. {
  255. var view = new View ();
  256. var invokingKeyBindingsInvoked = false;
  257. var processKeyPressInvoked = false;
  258. var setHandledTo = false;
  259. view.KeyDown += (s, e) =>
  260. {
  261. e.Handled = setHandledTo;
  262. Assert.Equal (setHandledTo, e.Handled);
  263. Assert.Equal (KeyCode.N, e.KeyCode);
  264. };
  265. view.InvokingKeyBindings += (s, e) =>
  266. {
  267. invokingKeyBindingsInvoked = true;
  268. Assert.False (e.Handled);
  269. Assert.Equal (KeyCode.N, e.KeyCode);
  270. };
  271. view.ProcessKeyDown += (s, e) =>
  272. {
  273. processKeyPressInvoked = true;
  274. Assert.False (e.Handled);
  275. Assert.Equal (KeyCode.N, e.KeyCode);
  276. };
  277. view.NewKeyDownEvent (Key.N);
  278. Assert.True (invokingKeyBindingsInvoked);
  279. Assert.True (processKeyPressInvoked);
  280. invokingKeyBindingsInvoked = false;
  281. processKeyPressInvoked = false;
  282. setHandledTo = true;
  283. view.NewKeyDownEvent (Key.N);
  284. Assert.False (invokingKeyBindingsInvoked);
  285. Assert.False (processKeyPressInvoked);
  286. }
  287. [Fact]
  288. public void KeyPressed_Handled_True_Stops_Processing ()
  289. {
  290. var keyDown = false;
  291. var invokingKeyBindings = false;
  292. var keyPressed = false;
  293. var view = new OnKeyTestView ();
  294. Assert.True (view.CanFocus);
  295. view.CancelVirtualMethods = false;
  296. view.KeyDown += (s, e) =>
  297. {
  298. Assert.Equal (KeyCode.A, e.KeyCode);
  299. Assert.False (keyDown);
  300. Assert.False (view.OnKeyDownContinued);
  301. e.Handled = false;
  302. keyDown = true;
  303. };
  304. view.InvokingKeyBindings += (s, e) =>
  305. {
  306. Assert.Equal (KeyCode.A, e.KeyCode);
  307. Assert.False (keyPressed);
  308. Assert.False (view.OnInvokingKeyBindingsContinued);
  309. e.Handled = false;
  310. invokingKeyBindings = true;
  311. };
  312. view.ProcessKeyDown += (s, e) =>
  313. {
  314. Assert.Equal (KeyCode.A, e.KeyCode);
  315. Assert.False (keyPressed);
  316. Assert.False (view.OnKeyPressedContinued);
  317. e.Handled = true;
  318. keyPressed = true;
  319. };
  320. view.NewKeyDownEvent (Key.A);
  321. Assert.True (keyDown);
  322. Assert.True (invokingKeyBindings);
  323. Assert.True (keyPressed);
  324. Assert.True (view.OnKeyDownContinued);
  325. Assert.True (view.OnInvokingKeyBindingsContinued);
  326. Assert.False (view.OnKeyPressedContinued);
  327. }
  328. [Fact]
  329. public void KeyUp_Handled_True_Stops_Processing ()
  330. {
  331. var keyUp = false;
  332. var view = new OnKeyTestView ();
  333. Assert.True (view.CanFocus);
  334. view.CancelVirtualMethods = false;
  335. view.KeyUp += (s, e) =>
  336. {
  337. Assert.Equal (KeyCode.A, e.KeyCode);
  338. Assert.False (keyUp);
  339. Assert.False (view.OnKeyPressedContinued);
  340. e.Handled = true;
  341. keyUp = true;
  342. };
  343. view.NewKeyUpEvent (Key.A);
  344. Assert.True (keyUp);
  345. Assert.False (view.OnKeyUpContinued);
  346. Assert.False (view.OnKeyDownContinued);
  347. Assert.False (view.OnInvokingKeyBindingsContinued);
  348. Assert.False (view.OnKeyPressedContinued);
  349. }
  350. [Theory]
  351. [InlineData (null, null)]
  352. [InlineData (true, true)]
  353. [InlineData (false, false)]
  354. public void OnInvokingKeyBindings_Returns_Nullable_Properly (bool? toReturn, bool? expected)
  355. {
  356. var view = new KeyBindingsTestView ();
  357. view.CommandReturns = toReturn;
  358. bool? result = view.OnInvokingKeyBindings (Key.A);
  359. Assert.Equal (expected, result);
  360. }
  361. /// <summary>A view that overrides the OnKey* methods so we can test that they are called.</summary>
  362. public class KeyBindingsTestView : View
  363. {
  364. public KeyBindingsTestView ()
  365. {
  366. CanFocus = true;
  367. AddCommand (Command.HotKey, () => CommandReturns);
  368. KeyBindings.Add (Key.A, Command.HotKey);
  369. }
  370. public bool? CommandReturns { get; set; }
  371. }
  372. /// <summary>A view that overrides the OnKey* methods so we can test that they are called.</summary>
  373. public class OnKeyTestView : View
  374. {
  375. public OnKeyTestView () { CanFocus = true; }
  376. public bool CancelVirtualMethods { set; private get; }
  377. public bool OnInvokingKeyBindingsContinued { get; set; }
  378. public bool OnKeyDownContinued { get; set; }
  379. public bool OnKeyPressedContinued { get; set; }
  380. public bool OnKeyUpContinued { get; set; }
  381. public override string Text { get; set; }
  382. public override bool? OnInvokingKeyBindings (Key keyEvent)
  383. {
  384. bool? handled = base.OnInvokingKeyBindings (keyEvent);
  385. if (handled != null && (bool)handled)
  386. {
  387. return true;
  388. }
  389. OnInvokingKeyBindingsContinued = true;
  390. return CancelVirtualMethods;
  391. }
  392. public override bool OnKeyDown (Key keyEvent)
  393. {
  394. if (base.OnKeyDown (keyEvent))
  395. {
  396. return true;
  397. }
  398. OnKeyDownContinued = true;
  399. return CancelVirtualMethods;
  400. }
  401. public override bool OnKeyUp (Key keyEvent)
  402. {
  403. if (base.OnKeyUp (keyEvent))
  404. {
  405. return true;
  406. }
  407. OnKeyUpContinued = true;
  408. return CancelVirtualMethods;
  409. }
  410. public override bool OnProcessKeyDown (Key keyEvent)
  411. {
  412. if (base.OnProcessKeyDown (keyEvent))
  413. {
  414. return true;
  415. }
  416. OnKeyPressedContinued = true;
  417. return CancelVirtualMethods;
  418. }
  419. }
  420. }