KeyboardEventTests.cs 17 KB

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