MenuBarv2Tests.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. using System.Globalization;
  2. using System.Reflection;
  3. using TerminalGuiFluentTesting;
  4. using Xunit.Abstractions;
  5. namespace IntegrationTests.FluentTests;
  6. /// <summary>
  7. /// Tests for the MenuBarv2 class
  8. /// </summary>
  9. public class MenuBarv2Tests
  10. {
  11. private readonly TextWriter _out;
  12. public MenuBarv2Tests (ITestOutputHelper outputHelper)
  13. {
  14. CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
  15. _out = new TestOutputWriter (outputHelper);
  16. }
  17. [Theory]
  18. [ClassData (typeof (V2TestDrivers))]
  19. public void Initializes_WithNoItems (V2TestDriver d)
  20. {
  21. using GuiTestContext c = With.A<Window> (80, 25, d)
  22. .Then (
  23. () =>
  24. {
  25. // Create a menu bar with no items
  26. var menuBar = new MenuBarv2 ();
  27. Assert.Empty (menuBar.SubViews);
  28. Assert.False (menuBar.CanFocus);
  29. Assert.Equal (Orientation.Horizontal, menuBar.Orientation);
  30. Assert.Equal (Key.F9, MenuBarv2.DefaultKey);
  31. })
  32. .WriteOutLogs (_out)
  33. .Stop ();
  34. }
  35. [Theory]
  36. [ClassData (typeof (V2TestDrivers))]
  37. public void Initializes_WithItems (V2TestDriver d)
  38. {
  39. MenuBarItemv2 [] menuItems = [];
  40. using GuiTestContext c = With.A<Window> (80, 25, d)
  41. .Then (
  42. () =>
  43. {
  44. // Create items for the menu bar
  45. menuItems =
  46. [
  47. new (
  48. "_File",
  49. [
  50. new MenuItemv2 ("_Open", "Opens a file", () => { })
  51. ]),
  52. new (
  53. "_Edit",
  54. [
  55. new MenuItemv2 ("_Copy", "Copies selection", () => { })
  56. ])
  57. ];
  58. var menuBar = new MenuBarv2 (menuItems);
  59. Assert.Equal (2, menuBar.SubViews.Count);
  60. // First item should be the File menu
  61. var fileMenu = menuBar.SubViews.ElementAt (0) as MenuBarItemv2;
  62. Assert.NotNull (fileMenu);
  63. Assert.Equal ("_File", fileMenu.Title);
  64. // Second item should be the Edit menu
  65. var editMenu = menuBar.SubViews.ElementAt (1) as MenuBarItemv2;
  66. Assert.NotNull (editMenu);
  67. Assert.Equal ("_Edit", editMenu.Title);
  68. })
  69. .WriteOutLogs (_out)
  70. .Stop ();
  71. }
  72. [Theory]
  73. [ClassData (typeof (V2TestDrivers))]
  74. public void AddsItems_WithMenusProperty (V2TestDriver d)
  75. {
  76. using GuiTestContext c = With.A<Window> (80, 25, d)
  77. .Then (
  78. () =>
  79. {
  80. var menuBar = new MenuBarv2 ();
  81. // Set items through Menus property
  82. menuBar.Menus =
  83. [
  84. new ("_File"),
  85. new ("_Edit"),
  86. new ("_View")
  87. ];
  88. Assert.Equal (3, menuBar.SubViews.Count);
  89. })
  90. .WriteOutLogs (_out)
  91. .Stop ();
  92. }
  93. [Theory]
  94. [ClassData (typeof (V2TestDrivers))]
  95. public void ChangesKey_RaisesEvent (V2TestDriver d)
  96. {
  97. using GuiTestContext c = With.A<Window> (80, 25, d)
  98. .Then (
  99. () =>
  100. {
  101. var menuBar = new MenuBarv2 ();
  102. var oldKeyValue = Key.Empty;
  103. var newKeyValue = Key.Empty;
  104. var eventRaised = false;
  105. menuBar.KeyChanged += (_, args) =>
  106. {
  107. eventRaised = true;
  108. oldKeyValue = args.OldKey;
  109. newKeyValue = args.NewKey;
  110. };
  111. // Default key should be F9
  112. Assert.Equal (Key.F9, menuBar.Key);
  113. // Change key to F1
  114. menuBar.Key = Key.F1;
  115. // Verify event was raised
  116. Assert.True (eventRaised);
  117. Assert.Equal (Key.F9, oldKeyValue);
  118. Assert.Equal (Key.F1, newKeyValue);
  119. // Verify key was changed
  120. Assert.Equal (Key.F1, menuBar.Key);
  121. })
  122. .WriteOutLogs (_out)
  123. .Stop ();
  124. }
  125. [Theory]
  126. [ClassData (typeof (V2TestDrivers))]
  127. public void DefaultKey_Activates (V2TestDriver d)
  128. {
  129. MenuBarv2? menuBar = null;
  130. using GuiTestContext c = With.A<Window> (50, 20, d)
  131. .Then (
  132. () =>
  133. {
  134. menuBar = new MenuBarv2 ();
  135. Toplevel top = Application.Top!;
  136. top.Add (
  137. new View ()
  138. {
  139. CanFocus = true,
  140. Id = "focusableView",
  141. });
  142. menuBar.EnableForDesign (ref top);
  143. Application.Top!.Add (menuBar);
  144. })
  145. .WaitIteration ()
  146. .Then (() => Assert.IsNotType<MenuItemv2> (Application.Navigation!.GetFocused ()))
  147. .ScreenShot ("MenuBar initial state", _out)
  148. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  149. .WaitIteration ()
  150. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  151. .WriteOutLogs (_out)
  152. .Then (() => Assert.Equal ("_New file", Application.Navigation!.GetFocused ()!.Title))
  153. .Then (() => Assert.True (Application.Popover?.GetActivePopover () is PopoverMenu))
  154. .Then (() => Assert.True (menuBar?.IsOpen ()))
  155. .WriteOutLogs (_out)
  156. .Stop ();
  157. }
  158. [Theory]
  159. [ClassData (typeof (V2TestDrivers))]
  160. public void DefaultKey_DeActivates (V2TestDriver d)
  161. {
  162. MenuBarv2? menuBar = null;
  163. using GuiTestContext c = With.A<Window> (50, 20, d)
  164. .Then (
  165. () =>
  166. {
  167. menuBar = new MenuBarv2 ();
  168. Toplevel top = Application.Top!;
  169. top.Add (
  170. new View ()
  171. {
  172. CanFocus = true,
  173. Id = "focusableView",
  174. });
  175. menuBar.EnableForDesign (ref top);
  176. Application.Top!.Add (menuBar);
  177. })
  178. .WaitIteration ()
  179. .Then (() => Assert.IsNotType<MenuItemv2>(Application.Navigation!.GetFocused()))
  180. .ScreenShot ("MenuBar initial state", _out)
  181. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  182. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  183. .Then (() => Assert.Equal ("_New file", Application.Navigation!.GetFocused ()!.Title))
  184. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  185. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  186. .Then (() => Assert.IsNotType<MenuItemv2>(Application.Navigation!.GetFocused()))
  187. .WriteOutLogs (_out)
  188. .Stop ();
  189. }
  190. [Theory]
  191. [ClassData (typeof (V2TestDrivers))]
  192. public void ShowHidePopovers (V2TestDriver d)
  193. {
  194. using GuiTestContext c = With.A<Window> (80, 25, d)
  195. .Then (
  196. () =>
  197. {
  198. // Create a menu bar with items that have submenus
  199. var fileMenuItem = new MenuBarItemv2 (
  200. "_File",
  201. [
  202. new MenuItemv2 ("_Open", string.Empty, null),
  203. new MenuItemv2 ("_Save", string.Empty, null)
  204. ]);
  205. var menuBar = new MenuBarv2 ([fileMenuItem]);
  206. // Initially, no menu should be open
  207. Assert.False (menuBar.IsOpen ());
  208. Assert.False (menuBar.Active);
  209. // Initialize the menu bar
  210. menuBar.BeginInit ();
  211. menuBar.EndInit ();
  212. // Simulate showing a popover menu by manipulating the first menu item
  213. MethodInfo? showPopoverMethod = typeof (MenuBarv2).GetMethod (
  214. "ShowPopover",
  215. BindingFlags.NonPublic | BindingFlags.Instance);
  216. // Set menu bar to active state using reflection
  217. FieldInfo? activeField = typeof (MenuBarv2).GetField (
  218. "_active",
  219. BindingFlags.NonPublic | BindingFlags.Instance);
  220. activeField?.SetValue (menuBar, true);
  221. menuBar.CanFocus = true;
  222. // Show the popover menu
  223. showPopoverMethod?.Invoke (menuBar, new object? [] { fileMenuItem });
  224. // Should be active now
  225. Assert.True (menuBar.Active);
  226. // Test if we can hide the popover menu
  227. fileMenuItem.PopoverMenu!.Visible = true;
  228. Assert.True (menuBar.HideActiveItem ());
  229. // Menu should no longer be open or active
  230. Assert.False (menuBar.Active);
  231. Assert.False (menuBar.IsOpen ());
  232. Assert.False (menuBar.CanFocus);
  233. })
  234. .WriteOutLogs (_out)
  235. .Stop ();
  236. }
  237. [Theory]
  238. [ClassData (typeof (V2TestDrivers))]
  239. public void EnableForDesign_CreatesMenuItems (V2TestDriver d)
  240. {
  241. using GuiTestContext c = With.A<Window> (80, 25, d)
  242. .Then (
  243. () =>
  244. {
  245. var menuBar = new MenuBarv2 ();
  246. Application.Top!.Add (menuBar);
  247. // Call EnableForDesign
  248. Toplevel top = Application.Top!;
  249. bool result = menuBar.EnableForDesign (ref top);
  250. // Should return true
  251. Assert.True (result);
  252. // Should have created menu items
  253. Assert.True (menuBar.SubViews.Count > 0);
  254. // Should have File, Edit and Help menus
  255. View? fileMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItemv2)?.Title == "_File");
  256. View? editMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItemv2)?.Title == "_Edit");
  257. View? helpMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItemv2)?.Title == "_Help");
  258. Assert.NotNull (fileMenu);
  259. Assert.NotNull (editMenu);
  260. Assert.NotNull (helpMenu);
  261. })
  262. .ScreenShot ("MenuBarv2 EnableForDesign", _out)
  263. .WriteOutLogs (_out)
  264. .Stop ();
  265. }
  266. [Theory]
  267. [ClassData (typeof (V2TestDrivers))]
  268. public void Navigation_Left_Right_Wraps (V2TestDriver d)
  269. {
  270. MenuBarv2? menuBar = null;
  271. using GuiTestContext c = With.A<Window> (50, 20, d)
  272. .Then (
  273. () =>
  274. {
  275. menuBar = new MenuBarv2 ();
  276. Toplevel top = Application.Top!;
  277. menuBar.EnableForDesign (ref top);
  278. Application.Top!.Add (menuBar);
  279. })
  280. .WaitIteration ()
  281. .ScreenShot ("MenuBar initial state", _out)
  282. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  283. .Then (() => Assert.True (Application.Popover?.GetActivePopover () is PopoverMenu))
  284. .Then (() => Assert.True (menuBar?.IsOpen ()))
  285. .Then (() => Assert.Equal ("_New file", Application.Navigation?.GetFocused ()!.Title))
  286. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  287. .Right ()
  288. .Then (() => Assert.True (Application.Popover?.GetActivePopover () is PopoverMenu))
  289. .ScreenShot ("After right arrow", _out)
  290. .Then (() => Assert.Equal ("Cu_t", Application.Navigation?.GetFocused ()!.Title))
  291. .Right ()
  292. .ScreenShot ("After second right arrow", _out)
  293. .Then (() => Assert.Equal ("_Online Help...", Application.Navigation?.GetFocused ()!.Title))
  294. .ScreenShot ("After third right arrow", _out)
  295. .Right ()
  296. .ScreenShot ("After fourth right arrow", _out)
  297. .Then (() => Assert.Equal ("_New file", Application.Navigation?.GetFocused ()!.Title))
  298. .Left ()
  299. .ScreenShot ("After left arrow", _out)
  300. .Then (() => Assert.Equal ("_Online Help...", Application.Navigation?.GetFocused ()!.Title))
  301. .WriteOutLogs (_out)
  302. .Stop ();
  303. }
  304. [Theory]
  305. [ClassData (typeof (V2TestDrivers))]
  306. public void MenuBarItem_With_QuitKey_Open_QuitKey_Restores_Focus_Correctly (V2TestDriver d)
  307. {
  308. MenuBarv2? menuBar = null;
  309. using GuiTestContext c = With.A<Window> (50, 20, d)
  310. .Then (
  311. () =>
  312. {
  313. menuBar = new MenuBarv2 ();
  314. Toplevel top = Application.Top!;
  315. top.Add (
  316. new View ()
  317. {
  318. CanFocus = true,
  319. Id = "focusableView",
  320. });
  321. menuBar.EnableForDesign (ref top);
  322. Application.Top!.Add (menuBar);
  323. })
  324. .WaitIteration ()
  325. .Then (() => Assert.IsNotType<MenuItemv2>(Application.Navigation!.GetFocused()))
  326. .ScreenShot ("MenuBar initial state", _out)
  327. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  328. .Then (() => Assert.Equal ("_New file", Application.Navigation!.GetFocused ()!.Title))
  329. .Then (() => Assert.True (Application.Popover?.GetActivePopover () is PopoverMenu))
  330. .Then (() => Assert.True (menuBar?.IsOpen ()))
  331. .Then (() => Assert.Equal ("_New file", Application.Navigation?.GetFocused ()!.Title))
  332. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  333. .RaiseKeyDownEvent (Application.QuitKey)
  334. .Then (() => Assert.False (Application.Popover?.GetActivePopover () is PopoverMenu))
  335. .Then (() => Assert.IsNotType<MenuItemv2> (Application.Navigation!.GetFocused ()))
  336. .WriteOutLogs (_out)
  337. .Stop ();
  338. }
  339. [Theory]
  340. [ClassData (typeof (V2TestDrivers))]
  341. public void MenuBarItem_Without_QuitKey_Open_QuitKey_Restores_Focus_Correctly (V2TestDriver d)
  342. {
  343. MenuBarv2? menuBar = null;
  344. using GuiTestContext c = With.A<Window> (50, 20, d)
  345. .Then (
  346. () =>
  347. {
  348. menuBar = new MenuBarv2 ();
  349. Toplevel top = Application.Top!;
  350. top.Add (
  351. new View ()
  352. {
  353. CanFocus = true,
  354. Id = "focusableView",
  355. });
  356. menuBar.EnableForDesign (ref top);
  357. Application.Top!.Add (menuBar);
  358. })
  359. .WaitIteration ()
  360. .Then (() => Assert.IsNotType<MenuItemv2> (Application.Navigation!.GetFocused ()))
  361. .ScreenShot ("MenuBar initial state", _out)
  362. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  363. .RaiseKeyDownEvent (Key.CursorRight)
  364. .Then (() => Assert.Equal ("Cu_t", Application.Navigation!.GetFocused ()!.Title))
  365. .Then (() => Assert.True (Application.Popover?.GetActivePopover () is PopoverMenu))
  366. .Then (() => Assert.True (menuBar?.IsOpen ()))
  367. .Then (() => Assert.Equal ("Cu_t", Application.Navigation?.GetFocused ()!.Title))
  368. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  369. .RaiseKeyDownEvent (Application.QuitKey)
  370. .WriteOutLogs (_out)
  371. .Then (() => Assert.False (Application.Popover?.GetActivePopover () is PopoverMenu))
  372. .Then (() => Assert.IsNotType<MenuItemv2> (Application.Navigation!.GetFocused ()))
  373. .Stop ();
  374. }
  375. [Theory]
  376. [ClassData (typeof (V2TestDrivers))]
  377. public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (V2TestDriver d)
  378. {
  379. MenuBarv2? menuBar = null;
  380. using GuiTestContext c = With.A<Window> (50, 20, d)
  381. .Then (
  382. () =>
  383. {
  384. menuBar = new MenuBarv2 ();
  385. Toplevel top = Application.Top!;
  386. top.Add (
  387. new View ()
  388. {
  389. CanFocus = true,
  390. Id = "focusableView",
  391. });
  392. menuBar.EnableForDesign (ref top);
  393. Application.Top!.Add (menuBar);
  394. })
  395. .WaitIteration ()
  396. .Then (() => Assert.IsNotType<MenuItemv2> (Application.Navigation!.GetFocused ()))
  397. .ScreenShot ("MenuBar initial state", _out)
  398. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  399. .Then (() => Assert.Equal ("_New file", Application.Navigation!.GetFocused ()!.Title))
  400. .Then (() => Assert.True (Application.Top!.Running))
  401. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  402. .RaiseKeyDownEvent (Application.QuitKey)
  403. .Then (() => Assert.False (Application.Popover?.GetActivePopover () is PopoverMenu))
  404. .Then (() => Assert.True (Application.Top!.Running))
  405. .WriteOutLogs (_out)
  406. .Stop ();
  407. }
  408. [Theory]
  409. [ClassData (typeof (V2TestDrivers))]
  410. public void MenuBarItem_Without_QuitKey_Open_QuitKey_Does_Not_Quit_MenuBar_SuperView (V2TestDriver d)
  411. {
  412. MenuBarv2? menuBar = null;
  413. using GuiTestContext c = With.A<Window> (50, 20, d)
  414. .Then (
  415. () =>
  416. {
  417. menuBar = new MenuBarv2 ();
  418. Toplevel top = Application.Top!;
  419. top.Add (
  420. new View ()
  421. {
  422. CanFocus = true,
  423. Id = "focusableView",
  424. });
  425. menuBar.EnableForDesign (ref top);
  426. IEnumerable<MenuItemv2> items = menuBar.GetMenuItemsWithTitle ("_Quit");
  427. foreach (MenuItemv2 item in items)
  428. {
  429. item.Key = Key.Empty;
  430. }
  431. Application.Top!.Add (menuBar);
  432. })
  433. .WaitIteration ()
  434. .Then (() => Assert.IsNotType<MenuItemv2> (Application.Navigation!.GetFocused ()))
  435. .ScreenShot ("MenuBar initial state", _out)
  436. .RaiseKeyDownEvent (MenuBarv2.DefaultKey)
  437. .Then (() => Assert.Equal ("_New file", Application.Navigation!.GetFocused ()!.Title))
  438. .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out)
  439. .RaiseKeyDownEvent (Application.QuitKey)
  440. .Then (() => Assert.False (Application.Popover?.GetActivePopover () is PopoverMenu))
  441. .Then (() => Assert.True (Application.Top!.Running))
  442. .WriteOutLogs (_out)
  443. .Stop ();
  444. }
  445. [Theory]
  446. [ClassData (typeof (V2TestDrivers))]
  447. public void MenuBar_Not_Active_DoesNotEat_Space (V2TestDriver d)
  448. {
  449. int spaceKeyDownCount = 0;
  450. View testView = new View ()
  451. {
  452. CanFocus = true,
  453. Id = "testView",
  454. };
  455. testView.KeyDown += (sender, key) =>
  456. {
  457. if (key == Key.Space)
  458. {
  459. spaceKeyDownCount++;
  460. }
  461. };
  462. using GuiTestContext c = With.A<Window> (50, 20, d)
  463. .Then (
  464. () =>
  465. {
  466. var menuBar = new MenuBarv2 ();
  467. Toplevel top = Application.Top!;
  468. menuBar.EnableForDesign (ref top);
  469. Application.Top!.Add (menuBar);
  470. })
  471. .Add (testView)
  472. .WaitIteration ()
  473. .Focus (testView)
  474. .RaiseKeyDownEvent (Key.Space)
  475. .Then (() => Assert.Equal (1, spaceKeyDownCount))
  476. .WriteOutLogs (_out)
  477. .Stop ();
  478. }
  479. [Theory]
  480. [ClassData (typeof (V2TestDrivers))]
  481. public void MenuBar_Not_Active_DoesNotEat_Enter (V2TestDriver d)
  482. {
  483. int enterKeyDownCount = 0;
  484. View testView = new View ()
  485. {
  486. CanFocus = true,
  487. Id = "testView",
  488. };
  489. testView.KeyDown += (sender, key) =>
  490. {
  491. if (key == Key.Enter)
  492. {
  493. enterKeyDownCount++;
  494. }
  495. };
  496. using GuiTestContext c = With.A<Window> (50, 20, d)
  497. .Then (
  498. () =>
  499. {
  500. var menuBar = new MenuBarv2 ();
  501. Toplevel top = Application.Top!;
  502. menuBar.EnableForDesign (ref top);
  503. Application.Top!.Add (menuBar);
  504. })
  505. .Add (testView)
  506. .WaitIteration ()
  507. .Focus (testView)
  508. .RaiseKeyDownEvent (Key.Enter)
  509. .Then (() => Assert.Equal (1, enterKeyDownCount))
  510. .WriteOutLogs (_out)
  511. .Stop ();
  512. }
  513. }