KeyboardEventTests.cs 11 KB

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