KeyboardTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. using Xunit.Abstractions;
  2. namespace Terminal.Gui.ApplicationTests;
  3. public class KeyboardTests
  4. {
  5. private readonly ITestOutputHelper _output;
  6. public KeyboardTests (ITestOutputHelper output)
  7. {
  8. _output = output;
  9. #if DEBUG_IDISPOSABLE
  10. Responder.Instances.Clear ();
  11. RunState.Instances.Clear ();
  12. #endif
  13. }
  14. [Fact]
  15. public void AlternateForwardKey_AlternateBackwardKey_Tests ()
  16. {
  17. Application.Init (new FakeDriver ());
  18. Toplevel top = new ();
  19. var w1 = new Window ();
  20. var v1 = new TextField ();
  21. var v2 = new TextView ();
  22. w1.Add (v1, v2);
  23. var w2 = new Window ();
  24. var v3 = new CheckBox ();
  25. var v4 = new Button ();
  26. w2.Add (v3, v4);
  27. top.Add (w1, w2);
  28. Application.Iteration += (s, a) =>
  29. {
  30. Assert.True (v1.HasFocus);
  31. // Using default keys.
  32. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  33. Assert.True (v2.HasFocus);
  34. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  35. Assert.True (v3.HasFocus);
  36. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  37. Assert.True (v4.HasFocus);
  38. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  39. Assert.True (v1.HasFocus);
  40. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  41. Assert.True (v4.HasFocus);
  42. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  43. Assert.True (v3.HasFocus);
  44. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  45. Assert.True (v2.HasFocus);
  46. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  47. Assert.True (v1.HasFocus);
  48. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  49. Assert.True (v2.HasFocus);
  50. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  51. Assert.True (v3.HasFocus);
  52. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  53. Assert.True (v4.HasFocus);
  54. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  55. Assert.True (v1.HasFocus);
  56. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  57. Assert.True (v4.HasFocus);
  58. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  59. Assert.True (v3.HasFocus);
  60. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  61. Assert.True (v2.HasFocus);
  62. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  63. Assert.True (v1.HasFocus);
  64. // Using another's alternate keys.
  65. Application.AlternateForwardKey = Key.F7;
  66. Application.AlternateBackwardKey = Key.F6;
  67. top.NewKeyDownEvent (Key.F7);
  68. Assert.True (v2.HasFocus);
  69. top.NewKeyDownEvent (Key.F7);
  70. Assert.True (v3.HasFocus);
  71. top.NewKeyDownEvent (Key.F7);
  72. Assert.True (v4.HasFocus);
  73. top.NewKeyDownEvent (Key.F7);
  74. Assert.True (v1.HasFocus);
  75. top.NewKeyDownEvent (Key.F6);
  76. Assert.True (v4.HasFocus);
  77. top.NewKeyDownEvent (Key.F6);
  78. Assert.True (v3.HasFocus);
  79. top.NewKeyDownEvent (Key.F6);
  80. Assert.True (v2.HasFocus);
  81. top.NewKeyDownEvent (Key.F6);
  82. Assert.True (v1.HasFocus);
  83. Application.RequestStop ();
  84. };
  85. Application.Run (top);
  86. // Replacing the defaults keys to avoid errors on others unit tests that are using it.
  87. Application.AlternateForwardKey = Key.PageDown.WithCtrl;
  88. Application.AlternateBackwardKey = Key.PageUp.WithCtrl;
  89. Application.QuitKey = Key.Q.WithCtrl;
  90. Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
  91. Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
  92. Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
  93. top.Dispose ();
  94. // Shutdown must be called to safely clean up Application if Init has been called
  95. Application.Shutdown ();
  96. }
  97. [Fact]
  98. [AutoInitShutdown]
  99. public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
  100. {
  101. Toplevel top = new ();
  102. var win = new Window
  103. {
  104. Title = "win",
  105. X = 0,
  106. Y = 0,
  107. Width = 20,
  108. Height = 10
  109. };
  110. var tf = new TextField { Width = 10 };
  111. win.Add (tf);
  112. var win2 = new Window
  113. {
  114. Title = "win2",
  115. X = 22,
  116. Y = 0,
  117. Width = 20,
  118. Height = 10
  119. };
  120. var tf2 = new TextField { Width = 10 };
  121. win2.Add (tf2);
  122. top.Add (win, win2);
  123. Application.Begin (top);
  124. Assert.True (win.CanFocus);
  125. Assert.True (win.HasFocus);
  126. Assert.True (win2.CanFocus);
  127. Assert.False (win2.HasFocus);
  128. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  129. win.CanFocus = false;
  130. Assert.False (win.CanFocus);
  131. Assert.False (win.HasFocus);
  132. Assert.True (win2.CanFocus);
  133. Assert.True (win2.HasFocus);
  134. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  135. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  136. Assert.True (win2.CanFocus);
  137. Assert.False (win.HasFocus);
  138. Assert.True (win2.CanFocus);
  139. Assert.True (win2.HasFocus);
  140. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  141. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  142. Assert.False (win.CanFocus);
  143. Assert.False (win.HasFocus);
  144. Assert.True (win2.CanFocus);
  145. Assert.True (win2.HasFocus);
  146. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  147. }
  148. [Fact]
  149. [AutoInitShutdown]
  150. public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_ ()
  151. {
  152. Toplevel top = new ();
  153. var win = new Window
  154. {
  155. Title = "win",
  156. X = 0,
  157. Y = 0,
  158. Width = 20,
  159. Height = 10
  160. };
  161. var tf = new TextField { Width = 10 };
  162. win.Add (tf);
  163. var win2 = new Window
  164. {
  165. Title = "win2",
  166. X = 22,
  167. Y = 0,
  168. Width = 20,
  169. Height = 10
  170. };
  171. var tf2 = new TextField { Width = 10 };
  172. win2.Add (tf2);
  173. top.Add (win, win2);
  174. Application.Begin (top);
  175. Assert.True (win.CanFocus);
  176. Assert.True (win.HasFocus);
  177. Assert.True (win2.CanFocus);
  178. Assert.False (win2.HasFocus);
  179. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  180. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  181. Assert.True (win.CanFocus);
  182. Assert.False (win.HasFocus);
  183. Assert.True (win2.CanFocus);
  184. Assert.True (win2.HasFocus);
  185. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  186. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  187. Assert.True (win.CanFocus);
  188. Assert.True (win.HasFocus);
  189. Assert.True (win2.CanFocus);
  190. Assert.False (win2.HasFocus);
  191. Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
  192. }
  193. [Fact]
  194. public void KeyUp_Event ()
  195. {
  196. Application.Init (new FakeDriver ());
  197. // Setup some fake keypresses (This)
  198. var input = "Tests";
  199. // Put a control-q in at the end
  200. FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('Q', ConsoleKey.Q, false, false, true));
  201. foreach (char c in input.Reverse ())
  202. {
  203. if (char.IsLetter (c))
  204. {
  205. FakeConsole.MockKeyPresses.Push (
  206. new ConsoleKeyInfo (
  207. c,
  208. (ConsoleKey)char.ToUpper (c),
  209. char.IsUpper (c),
  210. false,
  211. false
  212. )
  213. );
  214. }
  215. else
  216. {
  217. FakeConsole.MockKeyPresses.Push (
  218. new ConsoleKeyInfo (
  219. c,
  220. (ConsoleKey)c,
  221. false,
  222. false,
  223. false
  224. )
  225. );
  226. }
  227. }
  228. int stackSize = FakeConsole.MockKeyPresses.Count;
  229. var iterations = 0;
  230. Application.Iteration += (s, a) =>
  231. {
  232. iterations++;
  233. // Stop if we run out of control...
  234. if (iterations > 10)
  235. {
  236. Application.RequestStop ();
  237. }
  238. };
  239. var keyUps = 0;
  240. var output = string.Empty;
  241. var top = new Toplevel ();
  242. top.KeyUp += (sender, args) =>
  243. {
  244. if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
  245. {
  246. output += args.AsRune;
  247. }
  248. keyUps++;
  249. };
  250. Application.Run (top);
  251. // Input string should match output
  252. Assert.Equal (input, output);
  253. // # of key up events should match stack size
  254. //Assert.Equal (stackSize, keyUps);
  255. // We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
  256. // it must be literal (Linux only).
  257. Assert.Equal (6, keyUps);
  258. // # of key up events should match # of iterations
  259. Assert.Equal (stackSize, iterations);
  260. top.Dispose ();
  261. Application.Shutdown ();
  262. Assert.Null (Application.Current);
  263. Assert.Null (Application.Top);
  264. Assert.Null (Application.MainLoop);
  265. Assert.Null (Application.Driver);
  266. }
  267. [Fact]
  268. [AutoInitShutdown]
  269. public void OnKeyDown_Application_KeyBinding ()
  270. {
  271. var view = new ScopedKeyBindingView ();
  272. var invoked = false;
  273. view.InvokingKeyBindings += (s, e) => invoked = true;
  274. var top = new Toplevel ();
  275. top.Add (view);
  276. Application.Begin (top);
  277. Application.OnKeyDown (Key.A);
  278. Assert.True (invoked);
  279. Assert.True (view.ApplicationCommand);
  280. invoked = false;
  281. view.ApplicationCommand = false;
  282. view.KeyBindings.Remove (KeyCode.A);
  283. Application.OnKeyDown (Key.A); // old
  284. Assert.False (invoked);
  285. Assert.False (view.ApplicationCommand);
  286. view.KeyBindings.Add (Key.A.WithCtrl, KeyBindingScope.Application, Command.Save);
  287. Application.OnKeyDown (Key.A); // old
  288. Assert.False (invoked);
  289. Assert.False (view.ApplicationCommand);
  290. Application.OnKeyDown (Key.A.WithCtrl); // new
  291. Assert.True (invoked);
  292. Assert.True (view.ApplicationCommand);
  293. invoked = false;
  294. Application.OnKeyDown (Key.H);
  295. Assert.True (invoked);
  296. invoked = false;
  297. Assert.False (view.HasFocus);
  298. Application.OnKeyDown (Key.F);
  299. Assert.False (invoked);
  300. Assert.True (view.ApplicationCommand);
  301. Assert.True (view.HotKeyCommand);
  302. Assert.False (view.FocusedCommand);
  303. }
  304. [Fact]
  305. [AutoInitShutdown]
  306. public void OnKeyDown_Application_KeyBinding_Negative ()
  307. {
  308. var view = new ScopedKeyBindingView ();
  309. var invoked = false;
  310. view.InvokingKeyBindings += (s, e) => invoked = true;
  311. var top = new Toplevel ();
  312. top.Add (view);
  313. Application.Begin (top);
  314. Application.OnKeyDown (Key.A.WithCtrl);
  315. Assert.False (invoked);
  316. Assert.False (view.ApplicationCommand);
  317. Assert.False (view.HotKeyCommand);
  318. Assert.False (view.FocusedCommand);
  319. invoked = false;
  320. Assert.False (view.HasFocus);
  321. Application.OnKeyDown (Key.Z);
  322. Assert.False (invoked);
  323. Assert.False (view.ApplicationCommand);
  324. Assert.False (view.HotKeyCommand);
  325. Assert.False (view.FocusedCommand);
  326. }
  327. [Fact]
  328. [AutoInitShutdown]
  329. public void QuitKey_Getter_Setter ()
  330. {
  331. Toplevel top = new ();
  332. var isQuiting = false;
  333. top.Closing += (s, e) =>
  334. {
  335. isQuiting = true;
  336. e.Cancel = true;
  337. };
  338. Application.Begin (top);
  339. top.Running = true;
  340. Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
  341. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  342. Assert.True (isQuiting);
  343. isQuiting = false;
  344. Application.OnKeyDown (Key.Q.WithCtrl);
  345. Assert.True (isQuiting);
  346. isQuiting = false;
  347. Application.QuitKey = Key.C.WithCtrl;
  348. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  349. Assert.False (isQuiting);
  350. Application.OnKeyDown (Key.Q.WithCtrl);
  351. Assert.False (isQuiting);
  352. Application.OnKeyDown (Application.QuitKey);
  353. Assert.True (isQuiting);
  354. // Reset the QuitKey to avoid throws errors on another tests
  355. Application.QuitKey = Key.Q.WithCtrl;
  356. }
  357. // test Application key Bindings
  358. public class ScopedKeyBindingView : View
  359. {
  360. public ScopedKeyBindingView ()
  361. {
  362. AddCommand (Command.Save, () => ApplicationCommand = true);
  363. AddCommand (Command.HotKey, () => HotKeyCommand = true);
  364. AddCommand (Command.Left, () => FocusedCommand = true);
  365. KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
  366. HotKey = KeyCode.H;
  367. KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
  368. }
  369. public bool ApplicationCommand { get; set; }
  370. public bool FocusedCommand { get; set; }
  371. public bool HotKeyCommand { get; set; }
  372. }
  373. }