ListViewTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  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. lv.BeginInit (); lv.EndInit ();
  141. Assert.Equal (-1, lv.SelectedItem);
  142. Assert.True (lv.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ())));
  143. Assert.Equal (0, lv.SelectedItem);
  144. Assert.True (lv.ProcessKey (new KeyEvent (Key.CursorUp, new KeyModifiers ())));
  145. Assert.Equal (0, lv.SelectedItem);
  146. Assert.True (lv.ProcessKey (new KeyEvent (Key.PageDown, new KeyModifiers ())));
  147. Assert.Equal (2, lv.SelectedItem);
  148. Assert.Equal (2, lv.TopItem);
  149. Assert.True (lv.ProcessKey (new KeyEvent (Key.PageUp, new KeyModifiers ())));
  150. Assert.Equal (0, lv.SelectedItem);
  151. Assert.Equal (0, lv.TopItem);
  152. Assert.False (lv.Source.IsMarked (lv.SelectedItem));
  153. Assert.True (lv.ProcessKey (new KeyEvent (Key.Space, new KeyModifiers ())));
  154. Assert.True (lv.Source.IsMarked (lv.SelectedItem));
  155. var opened = false;
  156. lv.OpenSelectedItem += (s, _) => opened = true;
  157. Assert.True (lv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
  158. Assert.True (opened);
  159. Assert.True (lv.ProcessKey (new KeyEvent (Key.End, new KeyModifiers ())));
  160. Assert.Equal (2, lv.SelectedItem);
  161. Assert.True (lv.ProcessKey (new KeyEvent (Key.Home, new KeyModifiers ())));
  162. Assert.Equal (0, lv.SelectedItem);
  163. }
  164. [Fact]
  165. [AutoInitShutdown]
  166. public void RowRender_Event ()
  167. {
  168. var rendered = false;
  169. var source = new List<string> () { "one", "two", "three" };
  170. var lv = new ListView () { Width = Dim.Fill (), Height = Dim.Fill () };
  171. lv.RowRender += (s, _) => rendered = true;
  172. Application.Top.Add (lv);
  173. Application.Begin (Application.Top);
  174. Assert.False (rendered);
  175. lv.SetSource (source);
  176. lv.Draw ();
  177. Assert.True (rendered);
  178. }
  179. [Fact]
  180. [AutoInitShutdown]
  181. public void EnsureSelectedItemVisible_Top ()
  182. {
  183. var source = new List<string> () { "First", "Second" };
  184. ListView lv = new ListView (source) { Width = Dim.Fill (), Height = 1 };
  185. lv.SelectedItem = 1;
  186. Application.Top.Add (lv);
  187. Application.Begin (Application.Top);
  188. Assert.Equal ("Second ", GetContents (0));
  189. Assert.Equal (new (' ', 7), GetContents (1));
  190. lv.MoveUp ();
  191. lv.Draw ();
  192. Assert.Equal ("First ", GetContents (0));
  193. Assert.Equal (new (' ', 7), GetContents (1));
  194. string GetContents (int line)
  195. {
  196. var item = "";
  197. for (int i = 0; i < 7; i++) {
  198. item += Application.Driver.Contents [line, i].Runes[0];
  199. }
  200. return item;
  201. }
  202. }
  203. [Fact]
  204. [AutoInitShutdown]
  205. public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp ()
  206. {
  207. var source = new List<string> ();
  208. for (int i = 0; i < 20; i++) {
  209. source.Add ($"Line{i}");
  210. }
  211. var lv = new ListView (source) { Width = Dim.Fill (), Height = Dim.Fill () };
  212. var win = new Window ();
  213. win.Add (lv);
  214. Application.Top.Add (win);
  215. Application.Begin (Application.Top);
  216. ((FakeDriver)Application.Driver).SetBufferSize (12, 12);
  217. Application.Refresh ();
  218. Assert.Equal (-1, lv.SelectedItem);
  219. TestHelpers.AssertDriverContentsWithFrameAre (@"
  220. ┌──────────┐
  221. │Line0 │
  222. │Line1 │
  223. │Line2 │
  224. │Line3 │
  225. │Line4 │
  226. │Line5 │
  227. │Line6 │
  228. │Line7 │
  229. │Line8 │
  230. │Line9 │
  231. └──────────┘", output);
  232. Assert.True (lv.ScrollDown (10));
  233. lv.Draw ();
  234. Assert.Equal (-1, lv.SelectedItem);
  235. TestHelpers.AssertDriverContentsWithFrameAre (@"
  236. ┌──────────┐
  237. │Line10 │
  238. │Line11 │
  239. │Line12 │
  240. │Line13 │
  241. │Line14 │
  242. │Line15 │
  243. │Line16 │
  244. │Line17 │
  245. │Line18 │
  246. │Line19 │
  247. └──────────┘", output);
  248. Assert.True (lv.MoveDown ());
  249. lv.Draw ();
  250. Assert.Equal (0, lv.SelectedItem);
  251. TestHelpers.AssertDriverContentsWithFrameAre (@"
  252. ┌──────────┐
  253. │Line0 │
  254. │Line1 │
  255. │Line2 │
  256. │Line3 │
  257. │Line4 │
  258. │Line5 │
  259. │Line6 │
  260. │Line7 │
  261. │Line8 │
  262. │Line9 │
  263. └──────────┘", output);
  264. Assert.True (lv.MoveEnd ());
  265. lv.Draw ();
  266. Assert.Equal (19, lv.SelectedItem);
  267. TestHelpers.AssertDriverContentsWithFrameAre (@"
  268. ┌──────────┐
  269. │Line19 │
  270. │ │
  271. │ │
  272. │ │
  273. │ │
  274. │ │
  275. │ │
  276. │ │
  277. │ │
  278. │ │
  279. └──────────┘", output);
  280. Assert.True (lv.ScrollUp (20));
  281. lv.Draw ();
  282. Assert.Equal (19, lv.SelectedItem);
  283. TestHelpers.AssertDriverContentsWithFrameAre (@"
  284. ┌──────────┐
  285. │Line0 │
  286. │Line1 │
  287. │Line2 │
  288. │Line3 │
  289. │Line4 │
  290. │Line5 │
  291. │Line6 │
  292. │Line7 │
  293. │Line8 │
  294. │Line9 │
  295. └──────────┘", output);
  296. Assert.True (lv.MoveDown ());
  297. lv.Draw ();
  298. Assert.Equal (19, lv.SelectedItem);
  299. TestHelpers.AssertDriverContentsWithFrameAre (@"
  300. ┌──────────┐
  301. │Line10 │
  302. │Line11 │
  303. │Line12 │
  304. │Line13 │
  305. │Line14 │
  306. │Line15 │
  307. │Line16 │
  308. │Line17 │
  309. │Line18 │
  310. │Line19 │
  311. └──────────┘", output);
  312. Assert.True (lv.ScrollUp (20));
  313. lv.Draw ();
  314. Assert.Equal (19, lv.SelectedItem);
  315. TestHelpers.AssertDriverContentsWithFrameAre (@"
  316. ┌──────────┐
  317. │Line0 │
  318. │Line1 │
  319. │Line2 │
  320. │Line3 │
  321. │Line4 │
  322. │Line5 │
  323. │Line6 │
  324. │Line7 │
  325. │Line8 │
  326. │Line9 │
  327. └──────────┘", output);
  328. Assert.True (lv.MoveDown ());
  329. lv.Draw ();
  330. Assert.Equal (19, lv.SelectedItem);
  331. TestHelpers.AssertDriverContentsWithFrameAre (@"
  332. ┌──────────┐
  333. │Line10 │
  334. │Line11 │
  335. │Line12 │
  336. │Line13 │
  337. │Line14 │
  338. │Line15 │
  339. │Line16 │
  340. │Line17 │
  341. │Line18 │
  342. │Line19 │
  343. └──────────┘", output);
  344. Assert.True (lv.MoveHome ());
  345. lv.Draw ();
  346. Assert.Equal (0, lv.SelectedItem);
  347. TestHelpers.AssertDriverContentsWithFrameAre (@"
  348. ┌──────────┐
  349. │Line0 │
  350. │Line1 │
  351. │Line2 │
  352. │Line3 │
  353. │Line4 │
  354. │Line5 │
  355. │Line6 │
  356. │Line7 │
  357. │Line8 │
  358. │Line9 │
  359. └──────────┘", output);
  360. Assert.True (lv.ScrollDown (20));
  361. lv.Draw ();
  362. Assert.Equal (0, lv.SelectedItem);
  363. TestHelpers.AssertDriverContentsWithFrameAre (@"
  364. ┌──────────┐
  365. │Line19 │
  366. │ │
  367. │ │
  368. │ │
  369. │ │
  370. │ │
  371. │ │
  372. │ │
  373. │ │
  374. │ │
  375. └──────────┘", output);
  376. Assert.True (lv.MoveUp ());
  377. lv.Draw ();
  378. Assert.Equal (0, lv.SelectedItem);
  379. TestHelpers.AssertDriverContentsWithFrameAre (@"
  380. ┌──────────┐
  381. │Line0 │
  382. │Line1 │
  383. │Line2 │
  384. │Line3 │
  385. │Line4 │
  386. │Line5 │
  387. │Line6 │
  388. │Line7 │
  389. │Line8 │
  390. │Line9 │
  391. └──────────┘", output);
  392. }
  393. [Fact]
  394. public void SetSource_Preserves_ListWrapper_Instance_If_Not_Null ()
  395. {
  396. var lv = new ListView (new List<string> { "One", "Two" });
  397. Assert.NotNull (lv.Source);
  398. lv.SetSource (null);
  399. Assert.NotNull (lv.Source);
  400. lv.Source = null;
  401. Assert.Null (lv.Source);
  402. lv = new ListView (new List<string> { "One", "Two" });
  403. Assert.NotNull (lv.Source);
  404. lv.SetSourceAsync (null);
  405. Assert.NotNull (lv.Source);
  406. }
  407. [Fact]
  408. public void ListWrapper_StartsWith ()
  409. {
  410. var lw = new ListWrapper (new List<string> { "One", "Two", "Three" });
  411. Assert.Equal (1, lw.StartsWith ("t"));
  412. Assert.Equal (1, lw.StartsWith ("tw"));
  413. Assert.Equal (2, lw.StartsWith ("th"));
  414. Assert.Equal (1, lw.StartsWith ("T"));
  415. Assert.Equal (1, lw.StartsWith ("TW"));
  416. Assert.Equal (2, lw.StartsWith ("TH"));
  417. lw = new ListWrapper (new List<string> { "One", "Two", "Three" });
  418. Assert.Equal (1, lw.StartsWith ("t"));
  419. Assert.Equal (1, lw.StartsWith ("tw"));
  420. Assert.Equal (2, lw.StartsWith ("th"));
  421. Assert.Equal (1, lw.StartsWith ("T"));
  422. Assert.Equal (1, lw.StartsWith ("TW"));
  423. Assert.Equal (2, lw.StartsWith ("TH"));
  424. }
  425. [Fact, AutoInitShutdown]
  426. public void EnsureSelectedItemVisible_SelectedItem ()
  427. {
  428. var source = new List<string> ();
  429. for (int i = 0; i < 10; i++) {
  430. source.Add ($"Item {i}");
  431. }
  432. var lv = new ListView (source) {
  433. Width = 10,
  434. Height = 5
  435. };
  436. Application.Top.Add (lv);
  437. Application.Begin (Application.Top);
  438. TestHelpers.AssertDriverContentsWithFrameAre (@"
  439. Item 0
  440. Item 1
  441. Item 2
  442. Item 3
  443. Item 4", output);
  444. // EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged
  445. lv.SelectedItem = 6;
  446. Application.Refresh ();
  447. TestHelpers.AssertDriverContentsWithFrameAre (@"
  448. Item 2
  449. Item 3
  450. Item 4
  451. Item 5
  452. Item 6", output);
  453. }
  454. [Fact]
  455. public void SelectedItem_Get_Set ()
  456. {
  457. var lv = new ListView (new List<string> { "One", "Two", "Three" });
  458. Assert.Equal (-1, lv.SelectedItem);
  459. Assert.Throws<ArgumentException> (() => lv.SelectedItem = 3);
  460. var exception = Record.Exception (() => lv.SelectedItem = -1);
  461. Assert.Null (exception);
  462. }
  463. [Fact]
  464. public void OnEnter_Does_Not_Throw_Exception ()
  465. {
  466. var lv = new ListView ();
  467. var top = new View ();
  468. top.Add (lv);
  469. var exception = Record.Exception (lv.SetFocus);
  470. Assert.Null (exception);
  471. }
  472. [Fact, AutoInitShutdown]
  473. public void Clicking_On_Border_Is_Ignored ()
  474. {
  475. var selected = "";
  476. var lv = new ListView {
  477. Height = 5,
  478. Width = 7,
  479. BorderStyle = LineStyle.Single
  480. };
  481. lv.SetSource (new List<string> { "One", "Two", "Three", "Four" });
  482. lv.SelectedItemChanged += (s, e) => selected = e.Value.ToString ();
  483. Application.Top.Add (lv);
  484. Application.Begin (Application.Top);
  485. Assert.Equal (new Thickness (1), lv.Border.Thickness);
  486. Assert.Equal (-1, lv.SelectedItem);
  487. Assert.Equal ("", lv.Text);
  488. TestHelpers.AssertDriverContentsWithFrameAre (@"
  489. ┌─────┐
  490. │One │
  491. │Two │
  492. │Three│
  493. └─────┘", output);
  494. Assert.True (lv.MouseEvent (new MouseEvent {
  495. X = 0,
  496. Y = 0,
  497. Flags = MouseFlags.Button1Clicked
  498. }));
  499. Assert.Equal ("", selected);
  500. Assert.Equal (-1, lv.SelectedItem);
  501. Assert.True (lv.MouseEvent (new MouseEvent {
  502. X = 0,
  503. Y = 1,
  504. Flags = MouseFlags.Button1Clicked
  505. }));
  506. Assert.Equal ("One", selected);
  507. Assert.Equal (0, lv.SelectedItem);
  508. Assert.True (lv.MouseEvent (new MouseEvent {
  509. X = 0,
  510. Y = 2,
  511. Flags = MouseFlags.Button1Clicked
  512. }));
  513. Assert.Equal ("Two", selected);
  514. Assert.Equal (1, lv.SelectedItem);
  515. Assert.True (lv.MouseEvent (new MouseEvent {
  516. X = 0,
  517. Y = 3,
  518. Flags = MouseFlags.Button1Clicked
  519. }));
  520. Assert.Equal ("Three", selected);
  521. Assert.Equal (2, lv.SelectedItem);
  522. Assert.True (lv.MouseEvent (new MouseEvent {
  523. X = 0,
  524. Y = 4,
  525. Flags = MouseFlags.Button1Clicked
  526. }));
  527. Assert.Equal ("Three", selected);
  528. Assert.Equal (2, lv.SelectedItem);
  529. }
  530. }
  531. }