HotKeyTests.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. using System;
  2. using Xunit;
  3. using Xunit.Abstractions;
  4. using System.Text;
  5. namespace Terminal.Gui.ViewTests;
  6. public class HotKeyTests {
  7. readonly ITestOutputHelper _output;
  8. public HotKeyTests (ITestOutputHelper output)
  9. {
  10. this._output = output;
  11. }
  12. [Fact]
  13. public void Defaults ()
  14. {
  15. var view = new View ();
  16. Assert.Equal (string.Empty, view.Title);
  17. Assert.Equal (KeyCode.Null, view.HotKey);
  18. // Verify key bindings were set
  19. var commands = view.KeyBindings.GetCommands (KeyCode.Null);
  20. Assert.Empty (commands);
  21. }
  22. [Theory]
  23. [InlineData (KeyCode.A)]
  24. [InlineData ((KeyCode)'a')]
  25. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  26. [InlineData (KeyCode.D1)]
  27. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)]
  28. [InlineData ((KeyCode)'!')]
  29. [InlineData ((KeyCode)'х')] // Cyrillic x
  30. [InlineData ((KeyCode)'你')] // Chinese ni
  31. [InlineData ((KeyCode)'ö')] // German o umlaut
  32. [InlineData (KeyCode.Null)]
  33. public void Set_Sets_WithValidKey (KeyCode key)
  34. {
  35. var view = new View ();
  36. view.HotKey = key;
  37. Assert.Equal (key, view.HotKey);
  38. }
  39. [Theory]
  40. [InlineData (KeyCode.A)]
  41. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  42. [InlineData (KeyCode.D1)]
  43. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
  44. [InlineData ((KeyCode)'х')] // Cyrillic x
  45. [InlineData ((KeyCode)'你')] // Chinese ni
  46. [InlineData ((KeyCode)'ö')] // German o umlaut
  47. public void Set_SetsKeyBindings (Key key)
  48. {
  49. var view = new View ();
  50. view.HotKey = key;
  51. Assert.Equal (string.Empty, view.Title);
  52. Assert.Equal (key, view.HotKey);
  53. // Verify key bindings were set
  54. // As passed
  55. var commands = view.KeyBindings.GetCommands (key);
  56. Assert.Contains (Command.Accept, commands);
  57. var baseKey = key.NoShift;
  58. // If A...Z, with and without shift
  59. if (baseKey.IsKeyCodeAtoZ) {
  60. commands = view.KeyBindings.GetCommands (key.WithShift);
  61. Assert.Contains (Command.Accept, commands);
  62. commands = view.KeyBindings.GetCommands (key.NoShift);
  63. Assert.Contains (Command.Accept, commands);
  64. commands = view.KeyBindings.GetCommands (key.WithAlt);
  65. Assert.Contains (Command.Accept, commands);
  66. commands = view.KeyBindings.GetCommands (key.NoShift.WithAlt);
  67. Assert.Contains (Command.Accept, commands);
  68. } else {
  69. // Non A..Z keys should not have shift bindings
  70. if (key.IsShift) {
  71. commands = view.KeyBindings.GetCommands (key.NoShift);
  72. Assert.Empty (commands);
  73. } else {
  74. commands = view.KeyBindings.GetCommands (key.WithShift);
  75. Assert.Empty (commands);
  76. }
  77. }
  78. }
  79. [Fact]
  80. public void Set_RemovesOldKeyBindings ()
  81. {
  82. var view = new View ();
  83. view.HotKey = KeyCode.A;
  84. Assert.Equal (string.Empty, view.Title);
  85. Assert.Equal (KeyCode.A, view.HotKey);
  86. // Verify key bindings were set
  87. var commands = view.KeyBindings.GetCommands (KeyCode.A);
  88. Assert.Contains (Command.Accept, commands);
  89. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
  90. Assert.Contains (Command.Accept, commands);
  91. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
  92. Assert.Contains (Command.Accept, commands);
  93. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
  94. Assert.Contains (Command.Accept, commands);
  95. // Now set again
  96. view.HotKey = KeyCode.B;
  97. Assert.Equal (string.Empty, view.Title);
  98. Assert.Equal (KeyCode.B, view.HotKey);
  99. commands = view.KeyBindings.GetCommands (KeyCode.A);
  100. Assert.DoesNotContain (Command.Accept, commands);
  101. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
  102. Assert.DoesNotContain (Command.Accept, commands);
  103. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
  104. Assert.DoesNotContain (Command.Accept, commands);
  105. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
  106. Assert.DoesNotContain (Command.Accept, commands);
  107. }
  108. [Fact]
  109. public void Set_Throws_If_Modifiers_Are_Included ()
  110. {
  111. var view = new View ();
  112. // A..Z must be naked (Alt is assumed)
  113. view.HotKey = KeyCode.A | KeyCode.AltMask;
  114. Assert.Throws<ArgumentException> (() => view.HotKey = KeyCode.A | KeyCode.CtrlMask);
  115. Assert.Throws<ArgumentException> (() => view.HotKey = KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask);
  116. // All others must not have Ctrl (Alt is assumed)
  117. view.HotKey = KeyCode.D1 | KeyCode.AltMask;
  118. Assert.Throws<ArgumentException> (() => view.HotKey = KeyCode.D1 | KeyCode.CtrlMask);
  119. Assert.Throws<ArgumentException> (() => view.HotKey = KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask);
  120. // Shift is ok (e.g. this is '!')
  121. view.HotKey = KeyCode.D1 | KeyCode.ShiftMask;
  122. }
  123. [Theory]
  124. [InlineData (KeyCode.A)]
  125. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  126. [InlineData (KeyCode.D1)]
  127. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
  128. [InlineData ((KeyCode)'х')] // Cyrillic x
  129. [InlineData ((KeyCode)'你')] // Chinese ni
  130. public void AddKeyBindingsForHotKey_Sets (KeyCode key)
  131. {
  132. var view = new View ();
  133. view.HotKey = KeyCode.Z;
  134. Assert.Equal (string.Empty, view.Title);
  135. Assert.Equal (KeyCode.Z, view.HotKey);
  136. view.AddKeyBindingsForHotKey (KeyCode.Null, key);
  137. // Verify key bindings were set
  138. // As passed
  139. var commands = view.KeyBindings.GetCommands (key);
  140. Assert.Contains (Command.Accept, commands);
  141. commands = view.KeyBindings.GetCommands (key | KeyCode.AltMask);
  142. Assert.Contains (Command.Accept, commands);
  143. var baseKey = key & ~KeyCode.ShiftMask;
  144. // If A...Z, with and without shift
  145. if (baseKey is >= KeyCode.A and <= KeyCode.Z) {
  146. commands = view.KeyBindings.GetCommands (key | KeyCode.ShiftMask);
  147. Assert.Contains (Command.Accept, commands);
  148. commands = view.KeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
  149. Assert.Contains (Command.Accept, commands);
  150. commands = view.KeyBindings.GetCommands (key | KeyCode.AltMask);
  151. Assert.Contains (Command.Accept, commands);
  152. commands = view.KeyBindings.GetCommands (key & ~KeyCode.ShiftMask | KeyCode.AltMask);
  153. Assert.Contains (Command.Accept, commands);
  154. } else {
  155. // Non A..Z keys should not have shift bindings
  156. if (key.HasFlag (KeyCode.ShiftMask)) {
  157. commands = view.KeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
  158. Assert.Empty (commands);
  159. } else {
  160. commands = view.KeyBindings.GetCommands (key | KeyCode.ShiftMask);
  161. Assert.Empty (commands);
  162. }
  163. }
  164. }
  165. [Theory]
  166. [InlineData (KeyCode.Delete)]
  167. [InlineData (KeyCode.Backspace)]
  168. [InlineData (KeyCode.Tab)]
  169. [InlineData (KeyCode.Enter)]
  170. [InlineData (KeyCode.Esc)]
  171. [InlineData (KeyCode.Space)]
  172. [InlineData (KeyCode.CursorLeft)]
  173. [InlineData (KeyCode.F1)]
  174. [InlineData (KeyCode.Null | KeyCode.ShiftMask)]
  175. public void Set_Throws_With_Invalid_Key (KeyCode key)
  176. {
  177. var view = new View ();
  178. Assert.Throws<ArgumentException> (() => view.HotKey = key);
  179. }
  180. [Theory]
  181. [InlineData ("Test", KeyCode.T)]
  182. [InlineData ("^Test", KeyCode.T)]
  183. [InlineData ("T^est", KeyCode.E)]
  184. [InlineData ("Te^st", KeyCode.S)]
  185. [InlineData ("Tes^t", KeyCode.T)]
  186. [InlineData ("other", KeyCode.Null)]
  187. [InlineData ("oTher", KeyCode.T)]
  188. [InlineData ("^Öther", (KeyCode)'Ö')]
  189. [InlineData ("^öther", (KeyCode)'ö')]
  190. // BUGBUG: '!' should be supported. Line 968 of TextFormatter filters on char.IsLetterOrDigit
  191. //[InlineData ("Test^!", (Key)'!')]
  192. public void Text_Change_Sets_HotKey (string text, KeyCode expectedHotKey)
  193. {
  194. var view = new View () {
  195. HotKeySpecifier = new Rune ('^'),
  196. Text = "^Hello"
  197. };
  198. Assert.Equal (KeyCode.H, view.HotKey);
  199. view.Text = text;
  200. Assert.Equal (expectedHotKey, view.HotKey);
  201. }
  202. [Theory]
  203. [InlineData("^Test")]
  204. public void Text_Empty_Sets_HotKey_To_Null (string text)
  205. {
  206. var view = new View () {
  207. HotKeySpecifier = (Rune)'^',
  208. Text = text
  209. };
  210. Assert.Equal (text, view.Text);
  211. Assert.Equal (KeyCode.T, view.HotKey);
  212. view.Text = string.Empty;
  213. Assert.Equal ("", view.Text);
  214. Assert.Equal (KeyCode.Null, view.HotKey);
  215. }
  216. [Theory]
  217. [InlineData (KeyCode.Null, true)] // non-shift
  218. [InlineData (KeyCode.ShiftMask, true)]
  219. [InlineData (KeyCode.AltMask, true)]
  220. [InlineData (KeyCode.ShiftMask | KeyCode.AltMask, true)]
  221. [InlineData (KeyCode.CtrlMask, false)]
  222. [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, false)]
  223. public void KeyPress_Runs_Default_HotKey_Command (KeyCode mask, bool expected)
  224. {
  225. var view = new View () {
  226. HotKeySpecifier = (Rune)'^',
  227. Text = "^Test"
  228. };
  229. view.CanFocus = true;
  230. Assert.False (view.HasFocus);
  231. view.NewKeyDownEvent (new (KeyCode.T | mask));
  232. Assert.Equal (expected, view.HasFocus);
  233. }
  234. [Fact]
  235. public void ProcessKeyDown_Invokes_HotKey_Command_With_SuperView ()
  236. {
  237. var view = new View () {
  238. HotKeySpecifier = (Rune)'^',
  239. Text = "^Test"
  240. };
  241. var superView = new View ();
  242. superView.Add (view);
  243. view.CanFocus = true;
  244. Assert.False (view.HasFocus);
  245. var ke = new Key (KeyCode.T);
  246. superView.NewKeyDownEvent (ke);
  247. Assert.True (view.HasFocus);
  248. }
  249. [Fact]
  250. public void ProcessKeyDown_Ignores_KeyBindings_Out_Of_Scope_SuperView ()
  251. {
  252. var view = new View ();
  253. view.KeyBindings.Add (KeyCode.A, Command.Default);
  254. view.InvokingKeyBindings += (s, e) => {
  255. Assert.Fail ();
  256. };
  257. var superView = new View ();
  258. superView.Add (view);
  259. var ke = new Key (KeyCode.A);
  260. superView.NewKeyDownEvent (ke);
  261. }
  262. }