MenuBarv2Tests.cs 29 KB

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