KeyboardEventTests.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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_NewKeyDownEvent_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.KeyDownNotHandled += (s, a) =>
  35. {
  36. a.Handled = true;
  37. keyDownProcessed = true;
  38. };
  39. // Key.Empty is invalid, but it's used here to test that the event is fired
  40. Assert.True (view.NewKeyDownEvent (Key.Empty)); // this will be true because the ProcessKeyDown event handled it
  41. Assert.True (keyDown);
  42. Assert.True (invokingKeyBindings);
  43. Assert.True (keyDownProcessed);
  44. view.Dispose ();
  45. }
  46. /// <summary>
  47. /// This tests that when a new key up event is sent to the view the view will fire the 1 key-up related event:
  48. /// KeyUp
  49. /// </summary>
  50. [Theory]
  51. [MemberData (nameof (AllViewTypes))]
  52. public void AllViews_NewKeyUpEvent_All_EventsFire (Type viewType)
  53. {
  54. var view = CreateInstanceIfNotGeneric (viewType);
  55. if (view == null)
  56. {
  57. output.WriteLine ($"ERROR: Generic view {viewType}");
  58. return;
  59. }
  60. output.WriteLine ($"Testing {view.GetType ().Name}");
  61. var keyUp = false;
  62. view.KeyUp += (s, a) =>
  63. {
  64. a.Handled = true;
  65. keyUp = true;
  66. };
  67. Assert.True (view.NewKeyUpEvent (Key.A)); // this will be true because the KeyUp event handled it
  68. Assert.True (keyUp);
  69. view.Dispose ();
  70. }
  71. [Theory]
  72. [InlineData (true, false, false)]
  73. [InlineData (true, true, false)]
  74. [InlineData (true, true, true)]
  75. public void NewKeyDownUpEvents_Events_Are_Raised_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 OnNewKeyTestView ();
  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.True (view.OnKeyDownCalled);
  90. keyDown = true;
  91. };
  92. view.KeyDownNotHandled += (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.True (view.OnKeyUpCalled);
  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.OnKeyDownCalled);
  117. Assert.True (view.OnProcessKeyDownCalled);
  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.OnKeyUpCalled);
  128. }
  129. [Fact]
  130. public void NewKeyDownEvent_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.KeyDownNotHandled += (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 NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing ()
  172. {
  173. var keyDown = false;
  174. var invokingKeyBindings = false;
  175. var keyPressed = false;
  176. var view = new OnNewKeyTestView ();
  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.True (view.OnKeyDownCalled);
  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.True (view.OnInvokingKeyBindingsCalled);
  192. e.Handled = true;
  193. invokingKeyBindings = true;
  194. };
  195. view.KeyDownNotHandled += (s, e) =>
  196. {
  197. Assert.Equal (KeyCode.A, e.KeyCode);
  198. Assert.False (keyPressed);
  199. Assert.False (view.OnProcessKeyDownCalled);
  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.OnKeyDownCalled);
  208. Assert.True (view.OnInvokingKeyBindingsCalled);
  209. Assert.False (view.OnProcessKeyDownCalled);
  210. }
  211. [Fact]
  212. public void NewKeyDownEvent_Handled_True_Stops_Processing ()
  213. {
  214. var keyDown = false;
  215. var invokingKeyBindings = false;
  216. var keyPressed = false;
  217. var view = new OnNewKeyTestView ();
  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.True (view.OnKeyDownCalled);
  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.OnInvokingKeyBindingsCalled);
  233. e.Handled = true;
  234. invokingKeyBindings = true;
  235. };
  236. view.KeyDownNotHandled += (s, e) =>
  237. {
  238. Assert.Equal (KeyCode.A, e.KeyCode);
  239. Assert.False (keyPressed);
  240. Assert.False (view.OnProcessKeyDownCalled);
  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.True (view.OnKeyDownCalled);
  249. Assert.False (view.OnInvokingKeyBindingsCalled);
  250. Assert.False (view.OnProcessKeyDownCalled);
  251. }
  252. [Fact]
  253. public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing ()
  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.KeyDownNotHandled += (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 NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing ()
  289. {
  290. var keyDown = false;
  291. var invokingKeyBindings = false;
  292. var processKeyDown = false;
  293. var view = new OnNewKeyTestView ();
  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.True (view.OnKeyDownCalled);
  301. e.Handled = false;
  302. keyDown = true;
  303. };
  304. view.InvokingKeyBindings += (s, e) =>
  305. {
  306. Assert.Equal (KeyCode.A, e.KeyCode);
  307. Assert.False (processKeyDown);
  308. Assert.True (view.OnInvokingKeyBindingsCalled);
  309. e.Handled = false;
  310. invokingKeyBindings = true;
  311. };
  312. view.KeyDownNotHandled += (s, e) =>
  313. {
  314. Assert.Equal (KeyCode.A, e.KeyCode);
  315. Assert.False (processKeyDown);
  316. Assert.True (view.OnProcessKeyDownCalled);
  317. e.Handled = true;
  318. processKeyDown = true;
  319. };
  320. view.NewKeyDownEvent (Key.A);
  321. Assert.True (keyDown);
  322. Assert.True (invokingKeyBindings);
  323. Assert.True (processKeyDown);
  324. Assert.True (view.OnKeyDownCalled);
  325. Assert.True (view.OnInvokingKeyBindingsCalled);
  326. Assert.True (view.OnProcessKeyDownCalled);
  327. }
  328. [Fact]
  329. public void NewKeyUpEvent_KeyUp_Handled_True_Stops_Processing ()
  330. {
  331. var keyUp = false;
  332. var view = new OnNewKeyTestView ();
  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.OnProcessKeyDownCalled);
  340. e.Handled = true;
  341. keyUp = true;
  342. };
  343. view.NewKeyUpEvent (Key.A);
  344. Assert.True (keyUp);
  345. Assert.True (view.OnKeyUpCalled);
  346. Assert.False (view.OnKeyDownCalled);
  347. Assert.False (view.OnInvokingKeyBindingsCalled);
  348. Assert.False (view.OnProcessKeyDownCalled);
  349. }
  350. [Theory]
  351. [InlineData (null, null)]
  352. [InlineData (true, true)]
  353. [InlineData (false, false)]
  354. public void RaiseInvokingKeyBindingsAndInvokeCommands_Returns_Nullable_Properly (bool? toReturn, bool? expected)
  355. {
  356. var view = new KeyBindingsTestView ();
  357. view.CommandReturns = toReturn;
  358. bool? result = view.RaiseInvokingKeyBindingsAndInvokeCommands (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 OnNewKeyTestView : View
  374. {
  375. public OnNewKeyTestView () { CanFocus = true; }
  376. public bool CancelVirtualMethods { set; private get; }
  377. public bool OnInvokingKeyBindingsCalled { get; set; }
  378. public bool OnKeyDownCalled { get; set; }
  379. public bool OnProcessKeyDownCalled { get; set; }
  380. public bool OnKeyUpCalled { get; set; }
  381. public override string Text { get; set; }
  382. protected override bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
  383. {
  384. OnInvokingKeyBindingsCalled = true;
  385. return CancelVirtualMethods;
  386. }
  387. protected override bool OnKeyDown (Key keyEvent)
  388. {
  389. OnKeyDownCalled = true;
  390. return CancelVirtualMethods;
  391. }
  392. public override bool OnKeyUp (Key keyEvent)
  393. {
  394. OnKeyUpCalled = true;
  395. return CancelVirtualMethods;
  396. }
  397. protected override bool OnKeyDownNotHandled (Key keyEvent)
  398. {
  399. OnProcessKeyDownCalled = true;
  400. return CancelVirtualMethods;
  401. }
  402. }
  403. }