ListViewTests.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using Xunit;
  8. using Xunit.Abstractions;
  9. namespace Terminal.Gui.ViewsTests {
  10. public class ListViewTests {
  11. readonly ITestOutputHelper output;
  12. public ListViewTests (ITestOutputHelper output)
  13. {
  14. this.output = output;
  15. }
  16. [Fact]
  17. public void Constructors_Defaults ()
  18. {
  19. var lv = new ListView ();
  20. Assert.Null (lv.Source);
  21. Assert.True (lv.CanFocus);
  22. Assert.Equal (-1, lv.SelectedItem);
  23. lv = new ListView (new List<string> () { "One", "Two", "Three" });
  24. Assert.NotNull (lv.Source);
  25. Assert.Equal (-1, lv.SelectedItem);
  26. lv = new ListView (new NewListDataSource ());
  27. Assert.NotNull (lv.Source);
  28. Assert.Equal (-1, lv.SelectedItem);
  29. lv = new ListView (new Rect (0, 1, 10, 20), new List<string> () { "One", "Two", "Three" });
  30. Assert.NotNull (lv.Source);
  31. Assert.Equal (-1, lv.SelectedItem);
  32. Assert.Equal (new Rect (0, 1, 10, 20), lv.Frame);
  33. lv = new ListView (new Rect (0, 1, 10, 20), new NewListDataSource ());
  34. Assert.NotNull (lv.Source);
  35. Assert.Equal (-1, lv.SelectedItem);
  36. Assert.Equal (new Rect (0, 1, 10, 20), lv.Frame);
  37. }
  38. [Fact]
  39. public void ListViewSelectThenDown ()
  40. {
  41. var lv = new ListView (new List<string> () { "One", "Two", "Three" });
  42. lv.AllowsMarking = true;
  43. Assert.NotNull (lv.Source);
  44. // first item should be deselected by default
  45. Assert.Equal (-1, lv.SelectedItem);
  46. // nothing is ticked
  47. Assert.False (lv.Source.IsMarked (0));
  48. Assert.False (lv.Source.IsMarked (1));
  49. Assert.False (lv.Source.IsMarked (2));
  50. lv.AddKeyBinding (Key.Space | Key.ShiftMask, Command.ToggleChecked, Command.LineDown);
  51. var ev = new KeyEvent (Key.Space | Key.ShiftMask, new KeyModifiers () { Shift = true });
  52. // view should indicate that it has accepted and consumed the event
  53. Assert.True (lv.ProcessKey (ev));
  54. // first item should now be selected
  55. Assert.Equal (0, lv.SelectedItem);
  56. // none of the items should be ticked
  57. Assert.False (lv.Source.IsMarked (0));
  58. Assert.False (lv.Source.IsMarked (1));
  59. Assert.False (lv.Source.IsMarked (2));
  60. // Press key combo again
  61. Assert.True (lv.ProcessKey (ev));
  62. // second item should now be selected
  63. Assert.Equal (1, lv.SelectedItem);
  64. // first item only should be ticked
  65. Assert.True (lv.Source.IsMarked (0));
  66. Assert.False (lv.Source.IsMarked (1));
  67. Assert.False (lv.Source.IsMarked (2));
  68. // Press key combo again
  69. Assert.True (lv.ProcessKey (ev));
  70. Assert.Equal (2, lv.SelectedItem);
  71. Assert.True (lv.Source.IsMarked (0));
  72. Assert.True (lv.Source.IsMarked (1));
  73. Assert.False (lv.Source.IsMarked (2));
  74. // Press key combo again
  75. Assert.True (lv.ProcessKey (ev));
  76. Assert.Equal (2, lv.SelectedItem); // cannot move down any further
  77. Assert.True (lv.Source.IsMarked (0));
  78. Assert.True (lv.Source.IsMarked (1));
  79. Assert.True (lv.Source.IsMarked (2)); // but can toggle marked
  80. // Press key combo again
  81. Assert.True (lv.ProcessKey (ev));
  82. Assert.Equal (2, lv.SelectedItem); // cannot move down any further
  83. Assert.True (lv.Source.IsMarked (0));
  84. Assert.True (lv.Source.IsMarked (1));
  85. Assert.False (lv.Source.IsMarked (2)); // untoggle toggle marked
  86. }
  87. [Fact]
  88. public void SettingEmptyKeybindingThrows ()
  89. {
  90. var lv = new ListView (new List<string> () { "One", "Two", "Three" });
  91. Assert.Throws<ArgumentException> (() => lv.AddKeyBinding (Key.Space));
  92. }
  93. /// <summary>
  94. /// Tests that when none of the Commands in a chained keybinding are possible
  95. /// the <see cref="View.ProcessKey(KeyEvent)"/> returns the appropriate result
  96. /// </summary>
  97. [Fact]
  98. public void ListViewProcessKeyReturnValue_WithMultipleCommands ()
  99. {
  100. var lv = new ListView (new List<string> () { "One", "Two", "Three", "Four" });
  101. Assert.NotNull (lv.Source);
  102. // first item should be deselected by default
  103. Assert.Equal (-1, lv.SelectedItem);
  104. // bind shift down to move down twice in control
  105. lv.AddKeyBinding (Key.CursorDown | Key.ShiftMask, Command.LineDown, Command.LineDown);
  106. var ev = new KeyEvent (Key.CursorDown | Key.ShiftMask, new KeyModifiers () { Shift = true });
  107. Assert.True (lv.ProcessKey (ev), "The first time we move down 2 it should be possible");
  108. // After moving down twice from -1 we should be at 'Two'
  109. Assert.Equal (1, lv.SelectedItem);
  110. // clear the items
  111. lv.SetSource (null);
  112. // Press key combo again - return should be false this time as none of the Commands are allowable
  113. Assert.False (lv.ProcessKey (ev), "We cannot move down so will not respond to this");
  114. }
  115. private class NewListDataSource : IListDataSource {
  116. public int Count => throw new NotImplementedException ();
  117. public int Length => throw new NotImplementedException ();
  118. public bool IsMarked (int item)
  119. {
  120. throw new NotImplementedException ();
  121. }
  122. public void Render (ListView container, ConsoleDriver driver, bool selected, int item, int col, int line, int width, int start = 0)
  123. {
  124. throw new NotImplementedException ();
  125. }
  126. public void SetMark (int item, bool value)
  127. {
  128. throw new NotImplementedException ();
  129. }
  130. public IList ToList ()
  131. {
  132. return new List<string> () { "One", "Two", "Three" };
  133. }
  134. }
  135. [Fact]
  136. public void KeyBindings_Command ()
  137. {
  138. List<string> source = new List<string> () { "One", "Two", "Three" };
  139. ListView lv = new ListView (source) { Height = 2, AllowsMarking = true };
  140. Assert.Equal (-1, lv.SelectedItem);
  141. Assert.True (lv.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ())));
  142. Assert.Equal (0, lv.SelectedItem);
  143. Assert.True (lv.ProcessKey (new KeyEvent (Key.CursorUp, new KeyModifiers ())));
  144. Assert.Equal (0, lv.SelectedItem);
  145. Assert.True (lv.ProcessKey (new KeyEvent (Key.PageDown, new KeyModifiers ())));
  146. Assert.Equal (2, lv.SelectedItem);
  147. Assert.Equal (2, lv.TopItem);
  148. Assert.True (lv.ProcessKey (new KeyEvent (Key.PageUp, new KeyModifiers ())));
  149. Assert.Equal (0, lv.SelectedItem);
  150. Assert.Equal (0, lv.TopItem);
  151. Assert.False (lv.Source.IsMarked (lv.SelectedItem));
  152. Assert.True (lv.ProcessKey (new KeyEvent (Key.Space, new KeyModifiers ())));
  153. Assert.True (lv.Source.IsMarked (lv.SelectedItem));
  154. var opened = false;
  155. lv.OpenSelectedItem += (s, _) => opened = true;
  156. Assert.True (lv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
  157. Assert.True (opened);
  158. Assert.True (lv.ProcessKey (new KeyEvent (Key.End, new KeyModifiers ())));
  159. Assert.Equal (2, lv.SelectedItem);
  160. Assert.True (lv.ProcessKey (new KeyEvent (Key.Home, new KeyModifiers ())));
  161. Assert.Equal (0, lv.SelectedItem);
  162. }
  163. [Fact]
  164. [AutoInitShutdown]
  165. public void RowRender_Event ()
  166. {
  167. var rendered = false;
  168. var source = new List<string> () { "one", "two", "three" };
  169. var lv = new ListView () { Width = Dim.Fill (), Height = Dim.Fill () };
  170. lv.RowRender += (s, _) => rendered = true;
  171. Application.Top.Add (lv);
  172. Application.Begin (Application.Top);
  173. Assert.False (rendered);
  174. lv.SetSource (source);
  175. lv.Redraw (lv.Bounds);
  176. Assert.True (rendered);
  177. }
  178. [Fact]
  179. [AutoInitShutdown]
  180. public void EnsureSelectedItemVisible_Top ()
  181. {
  182. var source = new List<string> () { "First", "Second" };
  183. ListView lv = new ListView (source) { Width = Dim.Fill (), Height = 1 };
  184. lv.SelectedItem = 1;
  185. Application.Top.Add (lv);
  186. Application.Begin (Application.Top);
  187. Assert.Equal ("Second ", GetContents (0));
  188. Assert.Equal (new (' ', 7), GetContents (1));
  189. lv.MoveUp ();
  190. lv.Redraw (lv.Bounds);
  191. Assert.Equal ("First ", GetContents (0));
  192. Assert.Equal (new (' ', 7), GetContents (1));
  193. string GetContents (int line)
  194. {
  195. var item = "";
  196. for (int i = 0; i < 7; i++) {
  197. item += (char)Application.Driver.Contents [line, i, 0];
  198. }
  199. return item;
  200. }
  201. }
  202. [Fact]
  203. [AutoInitShutdown]
  204. public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp ()
  205. {
  206. var source = new List<string> ();
  207. for (int i = 0; i < 20; i++) {
  208. source.Add ($"Line{i}");
  209. }
  210. var lv = new ListView (source) { Width = Dim.Fill (), Height = Dim.Fill () };
  211. var win = new Window ();
  212. win.Add (lv);
  213. Application.Top.Add (win);
  214. Application.Begin (Application.Top);
  215. ((FakeDriver)Application.Driver).SetBufferSize (12, 12);
  216. Application.Refresh ();
  217. Assert.Equal (-1, lv.SelectedItem);
  218. TestHelpers.AssertDriverContentsWithFrameAre (@"
  219. ┌──────────┐
  220. │Line0 │
  221. │Line1 │
  222. │Line2 │
  223. │Line3 │
  224. │Line4 │
  225. │Line5 │
  226. │Line6 │
  227. │Line7 │
  228. │Line8 │
  229. │Line9 │
  230. └──────────┘", output);
  231. Assert.True (lv.ScrollDown (10));
  232. lv.Redraw (lv.Bounds);
  233. Assert.Equal (-1, lv.SelectedItem);
  234. TestHelpers.AssertDriverContentsWithFrameAre (@"
  235. ┌──────────┐
  236. │Line10 │
  237. │Line11 │
  238. │Line12 │
  239. │Line13 │
  240. │Line14 │
  241. │Line15 │
  242. │Line16 │
  243. │Line17 │
  244. │Line18 │
  245. │Line19 │
  246. └──────────┘", output);
  247. Assert.True (lv.MoveDown ());
  248. lv.Redraw (lv.Bounds);
  249. Assert.Equal (0, lv.SelectedItem);
  250. TestHelpers.AssertDriverContentsWithFrameAre (@"
  251. ┌──────────┐
  252. │Line0 │
  253. │Line1 │
  254. │Line2 │
  255. │Line3 │
  256. │Line4 │
  257. │Line5 │
  258. │Line6 │
  259. │Line7 │
  260. │Line8 │
  261. │Line9 │
  262. └──────────┘", output);
  263. Assert.True (lv.MoveEnd ());
  264. lv.Redraw (lv.Bounds);
  265. Assert.Equal (19, lv.SelectedItem);
  266. TestHelpers.AssertDriverContentsWithFrameAre (@"
  267. ┌──────────┐
  268. │Line19 │
  269. │ │
  270. │ │
  271. │ │
  272. │ │
  273. │ │
  274. │ │
  275. │ │
  276. │ │
  277. │ │
  278. └──────────┘", output);
  279. Assert.True (lv.ScrollUp (20));
  280. lv.Redraw (lv.Bounds);
  281. Assert.Equal (19, lv.SelectedItem);
  282. TestHelpers.AssertDriverContentsWithFrameAre (@"
  283. ┌──────────┐
  284. │Line0 │
  285. │Line1 │
  286. │Line2 │
  287. │Line3 │
  288. │Line4 │
  289. │Line5 │
  290. │Line6 │
  291. │Line7 │
  292. │Line8 │
  293. │Line9 │
  294. └──────────┘", output);
  295. Assert.True (lv.MoveDown ());
  296. lv.Redraw (lv.Bounds);
  297. Assert.Equal (19, lv.SelectedItem);
  298. TestHelpers.AssertDriverContentsWithFrameAre (@"
  299. ┌──────────┐
  300. │Line10 │
  301. │Line11 │
  302. │Line12 │
  303. │Line13 │
  304. │Line14 │
  305. │Line15 │
  306. │Line16 │
  307. │Line17 │
  308. │Line18 │
  309. │Line19 │
  310. └──────────┘", output);
  311. Assert.True (lv.ScrollUp (20));
  312. lv.Redraw (lv.Bounds);
  313. Assert.Equal (19, lv.SelectedItem);
  314. TestHelpers.AssertDriverContentsWithFrameAre (@"
  315. ┌──────────┐
  316. │Line0 │
  317. │Line1 │
  318. │Line2 │
  319. │Line3 │
  320. │Line4 │
  321. │Line5 │
  322. │Line6 │
  323. │Line7 │
  324. │Line8 │
  325. │Line9 │
  326. └──────────┘", output);
  327. Assert.True (lv.MoveDown ());
  328. lv.Redraw (lv.Bounds);
  329. Assert.Equal (19, lv.SelectedItem);
  330. TestHelpers.AssertDriverContentsWithFrameAre (@"
  331. ┌──────────┐
  332. │Line10 │
  333. │Line11 │
  334. │Line12 │
  335. │Line13 │
  336. │Line14 │
  337. │Line15 │
  338. │Line16 │
  339. │Line17 │
  340. │Line18 │
  341. │Line19 │
  342. └──────────┘", output);
  343. Assert.True (lv.MoveHome ());
  344. lv.Redraw (lv.Bounds);
  345. Assert.Equal (0, lv.SelectedItem);
  346. TestHelpers.AssertDriverContentsWithFrameAre (@"
  347. ┌──────────┐
  348. │Line0 │
  349. │Line1 │
  350. │Line2 │
  351. │Line3 │
  352. │Line4 │
  353. │Line5 │
  354. │Line6 │
  355. │Line7 │
  356. │Line8 │
  357. │Line9 │
  358. └──────────┘", output);
  359. Assert.True (lv.ScrollDown (20));
  360. lv.Redraw (lv.Bounds);
  361. Assert.Equal (0, lv.SelectedItem);
  362. TestHelpers.AssertDriverContentsWithFrameAre (@"
  363. ┌──────────┐
  364. │Line19 │
  365. │ │
  366. │ │
  367. │ │
  368. │ │
  369. │ │
  370. │ │
  371. │ │
  372. │ │
  373. │ │
  374. └──────────┘", output);
  375. Assert.True (lv.MoveUp ());
  376. lv.Redraw (lv.Bounds);
  377. Assert.Equal (0, lv.SelectedItem);
  378. TestHelpers.AssertDriverContentsWithFrameAre (@"
  379. ┌──────────┐
  380. │Line0 │
  381. │Line1 │
  382. │Line2 │
  383. │Line3 │
  384. │Line4 │
  385. │Line5 │
  386. │Line6 │
  387. │Line7 │
  388. │Line8 │
  389. │Line9 │
  390. └──────────┘", output);
  391. }
  392. [Fact]
  393. public void SetSource_Preserves_ListWrapper_Instance_If_Not_Null ()
  394. {
  395. var lv = new ListView (new List<string> { "One", "Two" });
  396. Assert.NotNull (lv.Source);
  397. lv.SetSource (null);
  398. Assert.NotNull (lv.Source);
  399. lv.Source = null;
  400. Assert.Null (lv.Source);
  401. lv = new ListView (new List<string> { "One", "Two" });
  402. Assert.NotNull (lv.Source);
  403. lv.SetSourceAsync (null);
  404. Assert.NotNull (lv.Source);
  405. }
  406. [Fact]
  407. public void ListWrapper_StartsWith ()
  408. {
  409. var lw = new ListWrapper (new List<string> { "One", "Two", "Three" });
  410. Assert.Equal (1, lw.StartsWith ("t"));
  411. Assert.Equal (1, lw.StartsWith ("tw"));
  412. Assert.Equal (2, lw.StartsWith ("th"));
  413. Assert.Equal (1, lw.StartsWith ("T"));
  414. Assert.Equal (1, lw.StartsWith ("TW"));
  415. Assert.Equal (2, lw.StartsWith ("TH"));
  416. lw = new ListWrapper (new List<NStack.ustring> { "One", "Two", "Three" });
  417. Assert.Equal (1, lw.StartsWith ("t"));
  418. Assert.Equal (1, lw.StartsWith ("tw"));
  419. Assert.Equal (2, lw.StartsWith ("th"));
  420. Assert.Equal (1, lw.StartsWith ("T"));
  421. Assert.Equal (1, lw.StartsWith ("TW"));
  422. Assert.Equal (2, lw.StartsWith ("TH"));
  423. }
  424. [Fact, AutoInitShutdown]
  425. public void EnsureSelectedItemVisible_SelectedItem ()
  426. {
  427. var source = new List<string> ();
  428. for (int i = 0; i < 10; i++) {
  429. source.Add ($"Item {i}");
  430. }
  431. var lv = new ListView (source) {
  432. Width = 10,
  433. Height = 5
  434. };
  435. Application.Top.Add (lv);
  436. Application.Begin (Application.Top);
  437. TestHelpers.AssertDriverContentsWithFrameAre (@"
  438. Item 0
  439. Item 1
  440. Item 2
  441. Item 3
  442. Item 4", output);
  443. // EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged
  444. lv.SelectedItem = 6;
  445. Application.Refresh ();
  446. TestHelpers.AssertDriverContentsWithFrameAre (@"
  447. Item 2
  448. Item 3
  449. Item 4
  450. Item 5
  451. Item 6", output);
  452. }
  453. [Fact]
  454. public void SelectedItem_Get_Set ()
  455. {
  456. var lv = new ListView (new List<string> { "One", "Two", "Three" });
  457. Assert.Equal (-1, lv.SelectedItem);
  458. Assert.Throws<ArgumentException> (() => lv.SelectedItem = 3);
  459. var exception = Record.Exception (() => lv.SelectedItem = -1);
  460. Assert.Null (exception);
  461. }
  462. [Fact]
  463. public void OnEnter_Does_Not_Throw_Exception ()
  464. {
  465. var lv = new ListView ();
  466. var top = new View ();
  467. top.Add (lv);
  468. var exception = Record.Exception (lv.SetFocus);
  469. Assert.Null (exception);
  470. }
  471. }
  472. }