KeyboardTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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. top.Dispose ();
  148. }
  149. [Fact]
  150. [AutoInitShutdown]
  151. public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_ ()
  152. {
  153. Toplevel top = new ();
  154. var win = new Window
  155. {
  156. Title = "win",
  157. X = 0,
  158. Y = 0,
  159. Width = 20,
  160. Height = 10
  161. };
  162. var tf = new TextField { Width = 10 };
  163. win.Add (tf);
  164. var win2 = new Window
  165. {
  166. Title = "win2",
  167. X = 22,
  168. Y = 0,
  169. Width = 20,
  170. Height = 10
  171. };
  172. var tf2 = new TextField { Width = 10 };
  173. win2.Add (tf2);
  174. top.Add (win, win2);
  175. Application.Begin (top);
  176. Assert.True (win.CanFocus);
  177. Assert.True (win.HasFocus);
  178. Assert.True (win2.CanFocus);
  179. Assert.False (win2.HasFocus);
  180. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  181. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  182. Assert.True (win.CanFocus);
  183. Assert.False (win.HasFocus);
  184. Assert.True (win2.CanFocus);
  185. Assert.True (win2.HasFocus);
  186. Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
  187. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  188. Assert.True (win.CanFocus);
  189. Assert.True (win.HasFocus);
  190. Assert.True (win2.CanFocus);
  191. Assert.False (win2.HasFocus);
  192. Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
  193. top.Dispose ();
  194. }
  195. [Fact]
  196. public void KeyUp_Event ()
  197. {
  198. Application.Init (new FakeDriver ());
  199. // Setup some fake keypresses (This)
  200. var input = "Tests";
  201. // Put a control-q in at the end
  202. FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('Q', ConsoleKey.Q, false, false, true));
  203. foreach (char c in input.Reverse ())
  204. {
  205. if (char.IsLetter (c))
  206. {
  207. FakeConsole.MockKeyPresses.Push (
  208. new ConsoleKeyInfo (
  209. c,
  210. (ConsoleKey)char.ToUpper (c),
  211. char.IsUpper (c),
  212. false,
  213. false
  214. )
  215. );
  216. }
  217. else
  218. {
  219. FakeConsole.MockKeyPresses.Push (
  220. new ConsoleKeyInfo (
  221. c,
  222. (ConsoleKey)c,
  223. false,
  224. false,
  225. false
  226. )
  227. );
  228. }
  229. }
  230. int stackSize = FakeConsole.MockKeyPresses.Count;
  231. var iterations = 0;
  232. Application.Iteration += (s, a) =>
  233. {
  234. iterations++;
  235. // Stop if we run out of control...
  236. if (iterations > 10)
  237. {
  238. Application.RequestStop ();
  239. }
  240. };
  241. var keyUps = 0;
  242. var output = string.Empty;
  243. var top = new Toplevel ();
  244. top.KeyUp += (sender, args) =>
  245. {
  246. if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
  247. {
  248. output += args.AsRune;
  249. }
  250. keyUps++;
  251. };
  252. Application.Run (top);
  253. // Input string should match output
  254. Assert.Equal (input, output);
  255. // # of key up events should match stack size
  256. //Assert.Equal (stackSize, keyUps);
  257. // We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
  258. // it must be literal (Linux only).
  259. Assert.Equal (6, keyUps);
  260. // # of key up events should match # of iterations
  261. Assert.Equal (stackSize, iterations);
  262. top.Dispose ();
  263. Application.Shutdown ();
  264. Assert.Null (Application.Current);
  265. Assert.Null (Application.Top);
  266. Assert.Null (Application.MainLoop);
  267. Assert.Null (Application.Driver);
  268. }
  269. [Fact]
  270. [AutoInitShutdown]
  271. public void OnKeyDown_Application_KeyBinding ()
  272. {
  273. var view = new ScopedKeyBindingView ();
  274. var invoked = false;
  275. view.InvokingKeyBindings += (s, e) => invoked = true;
  276. var top = new Toplevel ();
  277. top.Add (view);
  278. Application.Begin (top);
  279. Application.OnKeyDown (Key.A);
  280. Assert.True (invoked);
  281. Assert.True (view.ApplicationCommand);
  282. invoked = false;
  283. view.ApplicationCommand = false;
  284. view.KeyBindings.Remove (KeyCode.A);
  285. Application.OnKeyDown (Key.A); // old
  286. Assert.False (invoked);
  287. Assert.False (view.ApplicationCommand);
  288. view.KeyBindings.Add (Key.A.WithCtrl, KeyBindingScope.Application, Command.Save);
  289. Application.OnKeyDown (Key.A); // old
  290. Assert.False (invoked);
  291. Assert.False (view.ApplicationCommand);
  292. Application.OnKeyDown (Key.A.WithCtrl); // new
  293. Assert.True (invoked);
  294. Assert.True (view.ApplicationCommand);
  295. invoked = false;
  296. Application.OnKeyDown (Key.H);
  297. Assert.True (invoked);
  298. invoked = false;
  299. Assert.False (view.HasFocus);
  300. Application.OnKeyDown (Key.F);
  301. Assert.False (invoked);
  302. Assert.True (view.ApplicationCommand);
  303. Assert.True (view.HotKeyCommand);
  304. Assert.False (view.FocusedCommand);
  305. top.Dispose ();
  306. }
  307. [Fact]
  308. [AutoInitShutdown]
  309. public void OnKeyDown_Application_KeyBinding_Negative ()
  310. {
  311. var view = new ScopedKeyBindingView ();
  312. var invoked = false;
  313. view.InvokingKeyBindings += (s, e) => invoked = true;
  314. var top = new Toplevel ();
  315. top.Add (view);
  316. Application.Begin (top);
  317. Application.OnKeyDown (Key.A.WithCtrl);
  318. Assert.False (invoked);
  319. Assert.False (view.ApplicationCommand);
  320. Assert.False (view.HotKeyCommand);
  321. Assert.False (view.FocusedCommand);
  322. invoked = false;
  323. Assert.False (view.HasFocus);
  324. Application.OnKeyDown (Key.Z);
  325. Assert.False (invoked);
  326. Assert.False (view.ApplicationCommand);
  327. Assert.False (view.HotKeyCommand);
  328. Assert.False (view.FocusedCommand);
  329. top.Dispose ();
  330. }
  331. [Fact]
  332. [AutoInitShutdown]
  333. public void QuitKey_Getter_Setter ()
  334. {
  335. Toplevel top = new ();
  336. var isQuiting = false;
  337. top.Closing += (s, e) =>
  338. {
  339. isQuiting = true;
  340. e.Cancel = true;
  341. };
  342. Application.Begin (top);
  343. top.Running = true;
  344. Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
  345. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  346. Assert.True (isQuiting);
  347. isQuiting = false;
  348. Application.OnKeyDown (Key.Q.WithCtrl);
  349. Assert.True (isQuiting);
  350. isQuiting = false;
  351. Application.QuitKey = Key.C.WithCtrl;
  352. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  353. Assert.False (isQuiting);
  354. Application.OnKeyDown (Key.Q.WithCtrl);
  355. Assert.False (isQuiting);
  356. Application.OnKeyDown (Application.QuitKey);
  357. Assert.True (isQuiting);
  358. // Reset the QuitKey to avoid throws errors on another tests
  359. Application.QuitKey = Key.Q.WithCtrl;
  360. top.Dispose ();
  361. }
  362. // test Application key Bindings
  363. public class ScopedKeyBindingView : View
  364. {
  365. public ScopedKeyBindingView ()
  366. {
  367. AddCommand (Command.Save, () => ApplicationCommand = true);
  368. AddCommand (Command.HotKey, () => HotKeyCommand = true);
  369. AddCommand (Command.Left, () => FocusedCommand = true);
  370. KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
  371. HotKey = KeyCode.H;
  372. KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
  373. }
  374. public bool ApplicationCommand { get; set; }
  375. public bool FocusedCommand { get; set; }
  376. public bool HotKeyCommand { get; set; }
  377. }
  378. }