KeyboardEventTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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 key-down related
  8. /// events: KeyDown and KeyDownNotHandled. Note that KeyUp is independent.
  9. /// </summary>
  10. [Theory]
  11. [SetupFakeDriver] // Required for spinner view that wants to register timeouts
  12. [MemberData (nameof (AllViewTypes))]
  13. public void AllViews_NewKeyDownEvent_All_EventsFire (Type viewType)
  14. {
  15. var view = CreateInstanceIfNotGeneric (viewType);
  16. if (view == null)
  17. {
  18. output.WriteLine ($"ERROR: Skipping generic view: {viewType}");
  19. return;
  20. }
  21. output.WriteLine ($"Testing {viewType}");
  22. var keyDown = false;
  23. view.KeyDown += (s, a) =>
  24. {
  25. a.Handled = false; // don't handle it so the other events are called
  26. keyDown = true;
  27. };
  28. var keyDownNotHandled = false;
  29. view.KeyDownNotHandled += (s, a) =>
  30. {
  31. a.Handled = true;
  32. keyDownNotHandled = true;
  33. };
  34. // Key.Empty is invalid, but it's used here to test that the event is fired
  35. Assert.True (view.NewKeyDownEvent (Key.Empty)); // this will be true because the ProcessKeyDown event handled it
  36. Assert.True (keyDown);
  37. Assert.True (keyDownNotHandled);
  38. view.Dispose ();
  39. }
  40. /// <summary>
  41. /// This tests that when a new key up event is sent to the view the view will fire the 1 key-up related event:
  42. /// KeyUp
  43. /// </summary>
  44. [Theory]
  45. [SetupFakeDriver] // Required for spinner view that wants to register timeouts
  46. [MemberData (nameof (AllViewTypes))]
  47. public void AllViews_NewKeyUpEvent_All_EventsFire (Type viewType)
  48. {
  49. var view = CreateInstanceIfNotGeneric (viewType);
  50. if (view == null)
  51. {
  52. output.WriteLine ($"ERROR: Generic view {viewType}");
  53. return;
  54. }
  55. output.WriteLine ($"Testing {view.GetType ().Name}");
  56. var keyUp = false;
  57. view.KeyUp += (s, a) =>
  58. {
  59. a.Handled = true;
  60. keyUp = true;
  61. };
  62. Assert.True (view.NewKeyUpEvent (Key.A)); // this will be true because the KeyUp event handled it
  63. Assert.True (keyUp);
  64. view.Dispose ();
  65. }
  66. [Theory]
  67. [InlineData (true, false, false)]
  68. [InlineData (true, true, false)]
  69. [InlineData (true, true, true)]
  70. public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool shift, bool alt, bool control)
  71. {
  72. var keyDown = false;
  73. var keyDownNotHandled = false;
  74. var keyUp = false;
  75. var view = new OnNewKeyTestView ();
  76. view.CancelVirtualMethods = false;
  77. view.KeyDown += (s, e) =>
  78. {
  79. Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
  80. Assert.Equal (shift, e.IsShift);
  81. Assert.Equal (alt, e.IsAlt);
  82. Assert.Equal (control, e.IsCtrl);
  83. Assert.False (keyDown);
  84. Assert.True (view.OnKeyDownCalled);
  85. keyDown = true;
  86. };
  87. view.KeyDownNotHandled += (s, e) => { keyDownNotHandled = true; };
  88. view.KeyUp += (s, e) =>
  89. {
  90. Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
  91. Assert.Equal (shift, e.IsShift);
  92. Assert.Equal (alt, e.IsAlt);
  93. Assert.Equal (control, e.IsCtrl);
  94. Assert.False (keyUp);
  95. Assert.True (view.OnKeyUpCalled);
  96. keyUp = true;
  97. };
  98. view.NewKeyDownEvent (
  99. new (
  100. KeyCode.Null
  101. | (shift ? KeyCode.ShiftMask : 0)
  102. | (alt ? KeyCode.AltMask : 0)
  103. | (control ? KeyCode.CtrlMask : 0)
  104. )
  105. );
  106. Assert.True (keyDownNotHandled);
  107. Assert.True (view.OnKeyDownCalled);
  108. Assert.True (view.OnProcessKeyDownCalled);
  109. view.NewKeyUpEvent (
  110. new (
  111. KeyCode.Null
  112. | (shift ? KeyCode.ShiftMask : 0)
  113. | (alt ? KeyCode.AltMask : 0)
  114. | (control ? KeyCode.CtrlMask : 0)
  115. )
  116. );
  117. Assert.True (keyUp);
  118. Assert.True (view.OnKeyUpCalled);
  119. }
  120. [Fact]
  121. public void NewKeyDownEvent_Handled_True_Stops_Processing ()
  122. {
  123. var keyDown = false;
  124. var keyDownNotHandled = false;
  125. var view = new OnNewKeyTestView ();
  126. Assert.True (view.CanFocus);
  127. view.CancelVirtualMethods = false;
  128. view.KeyDown += (s, e) =>
  129. {
  130. Assert.Equal (KeyCode.A, e.KeyCode);
  131. Assert.False (keyDown);
  132. Assert.True (view.OnKeyDownCalled);
  133. e.Handled = true;
  134. keyDown = true;
  135. };
  136. view.KeyDownNotHandled += (s, e) =>
  137. {
  138. Assert.Equal (KeyCode.A, e.KeyCode);
  139. Assert.False (keyDownNotHandled);
  140. Assert.False (view.OnProcessKeyDownCalled);
  141. e.Handled = true;
  142. keyDownNotHandled = true;
  143. };
  144. view.NewKeyDownEvent (Key.A);
  145. Assert.True (keyDown);
  146. Assert.False (keyDownNotHandled);
  147. Assert.True (view.OnKeyDownCalled);
  148. Assert.False (view.OnProcessKeyDownCalled);
  149. }
  150. [Fact]
  151. public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing ()
  152. {
  153. var view = new View ();
  154. var keyDownNotHandled = false;
  155. var setHandledTo = false;
  156. view.KeyDown += (s, e) =>
  157. {
  158. e.Handled = setHandledTo;
  159. Assert.Equal (setHandledTo, e.Handled);
  160. Assert.Equal (KeyCode.N, e.KeyCode);
  161. };
  162. view.KeyDownNotHandled += (s, e) =>
  163. {
  164. keyDownNotHandled = true;
  165. Assert.False (e.Handled);
  166. Assert.Equal (KeyCode.N, e.KeyCode);
  167. };
  168. view.NewKeyDownEvent (Key.N);
  169. Assert.True (keyDownNotHandled);
  170. keyDownNotHandled = false;
  171. setHandledTo = true;
  172. view.NewKeyDownEvent (Key.N);
  173. Assert.False (keyDownNotHandled);
  174. }
  175. [Fact]
  176. public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing ()
  177. {
  178. var keyDown = false;
  179. var keyDownNotHandled = false;
  180. var view = new OnNewKeyTestView ();
  181. Assert.True (view.CanFocus);
  182. view.CancelVirtualMethods = false;
  183. view.KeyDown += (s, e) =>
  184. {
  185. Assert.Equal (KeyCode.A, e.KeyCode);
  186. Assert.False (keyDown);
  187. Assert.True (view.OnKeyDownCalled);
  188. e.Handled = false;
  189. keyDown = true;
  190. };
  191. view.KeyDownNotHandled += (s, e) =>
  192. {
  193. Assert.Equal (KeyCode.A, e.KeyCode);
  194. Assert.False (keyDownNotHandled);
  195. Assert.True (view.OnProcessKeyDownCalled);
  196. e.Handled = true;
  197. keyDownNotHandled = true;
  198. };
  199. view.NewKeyDownEvent (Key.A);
  200. Assert.True (keyDown);
  201. Assert.True (keyDownNotHandled);
  202. Assert.True (view.OnKeyDownCalled);
  203. Assert.True (view.OnProcessKeyDownCalled);
  204. }
  205. [Fact]
  206. public void NewKeyUpEvent_KeyUp_Handled_True_Stops_Processing ()
  207. {
  208. var keyUp = false;
  209. var view = new OnNewKeyTestView ();
  210. Assert.True (view.CanFocus);
  211. view.CancelVirtualMethods = false;
  212. view.KeyUp += (s, e) =>
  213. {
  214. Assert.Equal (KeyCode.A, e.KeyCode);
  215. Assert.False (keyUp);
  216. Assert.False (view.OnProcessKeyDownCalled);
  217. e.Handled = true;
  218. keyUp = true;
  219. };
  220. view.NewKeyUpEvent (Key.A);
  221. Assert.True (keyUp);
  222. Assert.True (view.OnKeyUpCalled);
  223. Assert.False (view.OnKeyDownCalled);
  224. Assert.False (view.OnProcessKeyDownCalled);
  225. }
  226. [Theory]
  227. [InlineData (null, null)]
  228. [InlineData (true, true)]
  229. [InlineData (false, false)]
  230. public void InvokeCommands_Returns_Nullable_Properly (bool? toReturn, bool? expected)
  231. {
  232. var view = new KeyBindingsTestView ();
  233. view.CommandReturns = toReturn;
  234. bool? result = view.InvokeCommands (Key.A);
  235. Assert.Equal (expected, result);
  236. }
  237. /// <summary>A view that overrides the OnKey* methods so we can test that they are called.</summary>
  238. public class KeyBindingsTestView : View
  239. {
  240. public KeyBindingsTestView ()
  241. {
  242. CanFocus = true;
  243. AddCommand (Command.HotKey, () => CommandReturns);
  244. KeyBindings.Add (Key.A, Command.HotKey);
  245. }
  246. public bool? CommandReturns { get; set; }
  247. }
  248. /// <summary>A view that overrides the OnKey* methods so we can test that they are called.</summary>
  249. public class OnNewKeyTestView : View
  250. {
  251. public OnNewKeyTestView () { CanFocus = true; }
  252. public bool CancelVirtualMethods { set; private get; }
  253. public bool OnKeyDownCalled { get; set; }
  254. public bool OnProcessKeyDownCalled { get; set; }
  255. public bool OnKeyUpCalled { get; set; }
  256. public override string Text { get; set; }
  257. protected override bool OnKeyDown (Key keyEvent)
  258. {
  259. OnKeyDownCalled = true;
  260. return CancelVirtualMethods;
  261. }
  262. public override bool OnKeyUp (Key keyEvent)
  263. {
  264. OnKeyUpCalled = true;
  265. return CancelVirtualMethods;
  266. }
  267. protected override bool OnKeyDownNotHandled (Key keyEvent)
  268. {
  269. OnProcessKeyDownCalled = true;
  270. return CancelVirtualMethods;
  271. }
  272. }
  273. }