CollectionNavigatorTests.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. using System;
  2. using System.Threading;
  3. using Terminal.Gui;
  4. using Xunit;
  5. namespace Terminal.Gui.TextTests {
  6. public class CollectionNavigatorTests {
  7. static string [] simpleStrings = new string []{
  8. "appricot", // 0
  9. "arm", // 1
  10. "bat", // 2
  11. "batman", // 3
  12. "candle" // 4
  13. };
  14. [Fact]
  15. public void ShouldAcceptNegativeOne ()
  16. {
  17. var n = new CollectionNavigator (simpleStrings);
  18. // Expect that index of -1 (i.e. no selection) should work correctly
  19. // and select the first entry of the letter 'b'
  20. Assert.Equal (2, n.GetNextMatchingItem (-1, 'b'));
  21. }
  22. [Fact]
  23. public void OutOfBoundsShouldBeIgnored ()
  24. {
  25. var n = new CollectionNavigator (simpleStrings);
  26. // Expect saying that index 500 is the current selection should not cause
  27. // error and just be ignored (treated as no selection)
  28. Assert.Equal (2, n.GetNextMatchingItem (500, 'b'));
  29. }
  30. [Fact]
  31. public void Cycling ()
  32. {
  33. // cycling with 'b'
  34. var n = new CollectionNavigator (simpleStrings);
  35. Assert.Equal (2, n.GetNextMatchingItem (0, 'b'));
  36. Assert.Equal (3, n.GetNextMatchingItem (2, 'b'));
  37. // if 4 (candle) is selected it should loop back to bat
  38. Assert.Equal (2, n.GetNextMatchingItem (4, 'b'));
  39. // cycling with 'a'
  40. n = new CollectionNavigator (simpleStrings);
  41. Assert.Equal (0, n.GetNextMatchingItem (-1, 'a'));
  42. Assert.Equal (1, n.GetNextMatchingItem (0, 'a'));
  43. // if 4 (candle) is selected it should loop back to appricot
  44. Assert.Equal (0, n.GetNextMatchingItem (4, 'a'));
  45. }
  46. [Fact]
  47. public void FullText ()
  48. {
  49. var strings = new string []{
  50. "appricot",
  51. "arm",
  52. "ta",
  53. "target",
  54. "text",
  55. "egg",
  56. "candle"
  57. };
  58. var n = new CollectionNavigator (strings);
  59. int current = 0;
  60. Assert.Equal (strings.IndexOf ("ta"), current = n.GetNextMatchingItem (current, 't'));
  61. // should match "te" in "text"
  62. Assert.Equal (strings.IndexOf ("text"), current = n.GetNextMatchingItem (current, 'e'));
  63. // still matches text
  64. Assert.Equal (strings.IndexOf ("text"), current = n.GetNextMatchingItem (current, 'x'));
  65. // nothing starts texa so it should NOT jump to appricot
  66. Assert.Equal (strings.IndexOf ("text"), current = n.GetNextMatchingItem (current, 'a'));
  67. Thread.Sleep (n.TypingDelay + 100);
  68. // nothing starts "texa". Since were past timedelay we DO jump to appricot
  69. Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
  70. }
  71. [Fact]
  72. public void Unicode ()
  73. {
  74. var strings = new string []{
  75. "appricot",
  76. "arm",
  77. "ta",
  78. "丗丙业丞",
  79. "丗丙丛",
  80. "text",
  81. "egg",
  82. "candle"
  83. };
  84. var n = new CollectionNavigator (strings);
  85. int current = 0;
  86. Assert.Equal (strings.IndexOf ("丗丙业丞"), current = n.GetNextMatchingItem (current, '丗'));
  87. // 丗丙业丞 is as good a match as 丗丙丛
  88. // so when doing multi character searches we should
  89. // prefer to stay on the same index unless we invalidate
  90. // our typed text
  91. Assert.Equal (strings.IndexOf ("丗丙业丞"), current = n.GetNextMatchingItem (current, '丙'));
  92. // No longer matches 丗丙业丞 and now only matches 丗丙丛
  93. // so we should move to the new match
  94. Assert.Equal (strings.IndexOf ("丗丙丛"), current = n.GetNextMatchingItem (current, '丛'));
  95. // nothing starts "丗丙丛a". Since were still in the timedelay we do not jump to appricot
  96. Assert.Equal (strings.IndexOf ("丗丙丛"), current = n.GetNextMatchingItem (current, 'a'));
  97. Thread.Sleep (n.TypingDelay + 100);
  98. // nothing starts "丗丙丛a". Since were past timedelay we DO jump to appricot
  99. Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
  100. }
  101. [Fact]
  102. public void AtSymbol ()
  103. {
  104. var strings = new string []{
  105. "appricot",
  106. "arm",
  107. "ta",
  108. "@bob",
  109. "@bb",
  110. "text",
  111. "egg",
  112. "candle"
  113. };
  114. var n = new CollectionNavigator (strings);
  115. Assert.Equal (3, n.GetNextMatchingItem (0, '@'));
  116. Assert.Equal (3, n.GetNextMatchingItem (3, 'b'));
  117. Assert.Equal (4, n.GetNextMatchingItem (3, 'b'));
  118. }
  119. [Fact]
  120. public void Word ()
  121. {
  122. var strings = new string []{
  123. "appricot",
  124. "arm",
  125. "bat",
  126. "batman",
  127. "bates hotel",
  128. "candle"
  129. };
  130. int current = 0;
  131. var n = new CollectionNavigator (strings);
  132. Assert.Equal (strings.IndexOf ("bat"), current = n.GetNextMatchingItem (current, 'b')); // match bat
  133. Assert.Equal (strings.IndexOf ("bat"), current = n.GetNextMatchingItem (current, 'a')); // match bat
  134. Assert.Equal (strings.IndexOf ("bat"), current = n.GetNextMatchingItem (current, 't')); // match bat
  135. Assert.Equal (strings.IndexOf ("bates hotel"), current = n.GetNextMatchingItem (current, 'e')); // match bates hotel
  136. Assert.Equal (strings.IndexOf ("bates hotel"), current = n.GetNextMatchingItem (current, 's')); // match bates hotel
  137. Assert.Equal (strings.IndexOf ("bates hotel"), current = n.GetNextMatchingItem (current, ' ')); // match bates hotel
  138. }
  139. [Fact]
  140. public void Symbols ()
  141. {
  142. var strings = new string []{
  143. "$$",
  144. "$100.00",
  145. "$101.00",
  146. "$101.10",
  147. "$200.00",
  148. "appricot"
  149. };
  150. int current = 0;
  151. var n = new CollectionNavigator (strings);
  152. Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
  153. Assert.Equal ("a", n.SearchString);
  154. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, '$'));
  155. Assert.Equal ("$", n.SearchString);
  156. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, '1'));
  157. Assert.Equal ("$1", n.SearchString);
  158. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, '0'));
  159. Assert.Equal ("$10", n.SearchString);
  160. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, '1'));
  161. Assert.Equal ("$101", n.SearchString);
  162. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, '.'));
  163. Assert.Equal ("$101.", n.SearchString);
  164. // stay on the same item becuase still in timedelay
  165. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, 'a'));
  166. Assert.Equal ("$101.", n.SearchString);
  167. Thread.Sleep (n.TypingDelay + 100);
  168. // another '$' means searching for "$" again
  169. Assert.Equal (strings.IndexOf ("$101.10"), current = n.GetNextMatchingItem (current, '$'));
  170. Assert.Equal ("$", n.SearchString);
  171. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, '$'));
  172. Assert.Equal ("$$", n.SearchString);
  173. }
  174. [Fact]
  175. public void Delay ()
  176. {
  177. var strings = new string []{
  178. "$$",
  179. "$100.00",
  180. "$101.00",
  181. "$101.10",
  182. "$200.00",
  183. "appricot"
  184. };
  185. int current = 0;
  186. var n = new CollectionNavigator (strings);
  187. // No delay
  188. Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
  189. Assert.Equal ("a", n.SearchString);
  190. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, '$'));
  191. Assert.Equal ("$", n.SearchString);
  192. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, '$'));
  193. Assert.Equal ("$$", n.SearchString);
  194. // Delay
  195. Thread.Sleep (n.TypingDelay + 10);
  196. Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
  197. Assert.Equal ("a", n.SearchString);
  198. Thread.Sleep (n.TypingDelay + 10);
  199. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, '$'));
  200. Assert.Equal ("$", n.SearchString);
  201. Thread.Sleep (n.TypingDelay + 10);
  202. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, '$'));
  203. Assert.Equal ("$", n.SearchString);
  204. Thread.Sleep (n.TypingDelay + 10);
  205. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, '$'));
  206. Assert.Equal ("$", n.SearchString);
  207. Thread.Sleep (n.TypingDelay + 10);
  208. Assert.Equal (strings.IndexOf ("$101.10"), current = n.GetNextMatchingItem (current, '$'));
  209. Assert.Equal ("$", n.SearchString);
  210. Thread.Sleep (n.TypingDelay + 10);
  211. Assert.Equal (strings.IndexOf ("$101.10"), current = n.GetNextMatchingItem (current, '2')); // Shouldn't move
  212. Assert.Equal ("2", n.SearchString);
  213. }
  214. [Fact]
  215. public void MutliKeySearchPlusWrongKeyStays ()
  216. {
  217. var strings = new string []{
  218. "a",
  219. "c",
  220. "can",
  221. "candle",
  222. "candy",
  223. "yellow",
  224. "zebra"
  225. };
  226. int current = 0;
  227. var n = new CollectionNavigator (strings);
  228. // https://github.com/gui-cs/Terminal.Gui/pull/2132#issuecomment-1298425573
  229. // One thing that it currently does that is different from Explorer is that as soon as you hit a wrong key then it jumps to that index.
  230. // So if you type cand then z it jumps you to something beginning with z. In the same situation Windows Explorer beeps (not the best!)
  231. // but remains on candle.
  232. // We might be able to update the behaviour so that a 'wrong' keypress (z) within 500ms of a 'right' keypress ("can" + 'd') is
  233. // simply ignored (possibly ending the search process though). That would give a short delay for user to realise the thing
  234. // they typed doesn't exist and then start a new search (which would be possible 500ms after the last 'good' keypress).
  235. // This would only apply for 2+ character searches where theres been a successful 2+ character match right before.
  236. Assert.Equal (strings.IndexOf ("a"), current = n.GetNextMatchingItem (current, 'a'));
  237. Assert.Equal ("a", n.SearchString);
  238. Assert.Equal (strings.IndexOf ("c"), current = n.GetNextMatchingItem (current, 'c'));
  239. Assert.Equal ("c", n.SearchString);
  240. Assert.Equal (strings.IndexOf ("can"), current = n.GetNextMatchingItem (current, 'a'));
  241. Assert.Equal ("ca", n.SearchString);
  242. Assert.Equal (strings.IndexOf ("can"), current = n.GetNextMatchingItem (current, 'n'));
  243. Assert.Equal ("can", n.SearchString);
  244. Assert.Equal (strings.IndexOf ("candle"), current = n.GetNextMatchingItem (current, 'd'));
  245. Assert.Equal ("cand", n.SearchString);
  246. // Same as above, but with a 'wrong' key (z)
  247. Thread.Sleep (n.TypingDelay + 10);
  248. Assert.Equal (strings.IndexOf ("a"), current = n.GetNextMatchingItem (current, 'a'));
  249. Assert.Equal ("a", n.SearchString);
  250. Assert.Equal (strings.IndexOf ("c"), current = n.GetNextMatchingItem (current, 'c'));
  251. Assert.Equal ("c", n.SearchString);
  252. Assert.Equal (strings.IndexOf ("can"), current = n.GetNextMatchingItem (current, 'a'));
  253. Assert.Equal ("ca", n.SearchString);
  254. Assert.Equal (strings.IndexOf ("can"), current = n.GetNextMatchingItem (current, 'n'));
  255. Assert.Equal ("can", n.SearchString);
  256. Assert.Equal (strings.IndexOf ("can"), current = n.GetNextMatchingItem (current, 'z')); // Shouldn't move
  257. Assert.Equal ("can", n.SearchString); // Shouldn't change
  258. }
  259. [Fact]
  260. public void MinimizeMovement_False_ShouldMoveIfMultipleMatches ()
  261. {
  262. var strings = new string [] {
  263. "$$",
  264. "$100.00",
  265. "$101.00",
  266. "$101.10",
  267. "$200.00",
  268. "appricot",
  269. "c",
  270. "car",
  271. "cart",
  272. };
  273. int current = 0;
  274. var n = new CollectionNavigator (strings);
  275. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", false));
  276. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$", false));
  277. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", false)); // back to top
  278. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$", false));
  279. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, "$", false));
  280. Assert.Equal (strings.IndexOf ("$101.10"), current = n.GetNextMatchingItem (current, "$", false));
  281. Assert.Equal (strings.IndexOf ("$200.00"), current = n.GetNextMatchingItem (current, "$", false));
  282. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$", false)); // back to top
  283. Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, "a", false));
  284. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$", false)); // back to top
  285. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$100.00", false));
  286. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, "$", false));
  287. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, "$101.00", false));
  288. Assert.Equal (strings.IndexOf ("$200.00"), current = n.GetNextMatchingItem (current, "$2", false));
  289. Assert.Equal (strings.IndexOf ("$200.00"), current = n.GetNextMatchingItem (current, "$200.00", false));
  290. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, "$101.00", false));
  291. Assert.Equal (strings.IndexOf ("$200.00"), current = n.GetNextMatchingItem (current, "$2", false));
  292. Assert.Equal (strings.IndexOf ("$101.00"), current = n.GetNextMatchingItem (current, "$101.00", false));
  293. Assert.Equal (strings.IndexOf ("$200.00"), current = n.GetNextMatchingItem (current, "$2", false));
  294. Assert.Equal (strings.IndexOf ("car"), current = n.GetNextMatchingItem (current, "car", false));
  295. Assert.Equal (strings.IndexOf ("cart"), current = n.GetNextMatchingItem (current, "car", false));
  296. Assert.Equal (-1, current = n.GetNextMatchingItem (current, "x", false));
  297. }
  298. [Fact]
  299. public void MinimizeMovement_True_ShouldStayOnCurrentIfMultipleMatches ()
  300. {
  301. var strings = new string [] {
  302. "$$",
  303. "$100.00",
  304. "$101.00",
  305. "$101.10",
  306. "$200.00",
  307. "appricot",
  308. "c",
  309. "car",
  310. "cart",
  311. };
  312. int current = 0;
  313. var n = new CollectionNavigator (strings);
  314. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", true));
  315. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$", true));
  316. Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", true)); // back to top
  317. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$1", true));
  318. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$", true));
  319. Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$", true));
  320. Assert.Equal (strings.IndexOf ("car"), current = n.GetNextMatchingItem (current, "car", true));
  321. Assert.Equal (strings.IndexOf ("car"), current = n.GetNextMatchingItem (current, "car", true));
  322. Assert.Equal (-1, current = n.GetNextMatchingItem (current, "x", true));
  323. }
  324. }
  325. }