MenuBarvTests.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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 MenuBar class
  9. /// </summary>
  10. public class MenuBarTests
  11. {
  12. private readonly TextWriter _out;
  13. public MenuBarTests (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 MenuBar ();
  27. Assert.Empty (menuBar.SubViews);
  28. Assert.False (menuBar.CanFocus);
  29. Assert.Equal (Orientation.Horizontal, menuBar.Orientation);
  30. Assert.Equal (Key.F9, MenuBar.DefaultKey);
  31. });
  32. }
  33. [Theory]
  34. [ClassData (typeof (TestDrivers))]
  35. public void Initializes_WithItems (TestDriver d)
  36. {
  37. MenuBarItem [] 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 MenuItem ("_Open", "Opens a file", () => { })
  48. ]),
  49. new (
  50. "_Edit",
  51. [
  52. new MenuItem ("_Copy", "Copies selection", () => { })
  53. ])
  54. ];
  55. var menuBar = new MenuBar (menuItems);
  56. Assert.Equal (2, menuBar.SubViews.Count);
  57. // First item should be the File menu
  58. var fileMenu = menuBar.SubViews.ElementAt (0) as MenuBarItem;
  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 MenuBarItem;
  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 MenuBar ();
  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 MenuBar ();
  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. MenuBar? menuBar = null;
  119. Toplevel? top = null;
  120. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  121. .Then ((app) =>
  122. {
  123. menuBar = new MenuBar ();
  124. top = app.TopRunnable!;
  125. top.Add (
  126. new View ()
  127. {
  128. CanFocus = true,
  129. Id = "focusableView",
  130. });
  131. menuBar.EnableForDesign (ref top);
  132. app.TopRunnable!.Add (menuBar);
  133. })
  134. .WaitIteration ()
  135. .AssertIsNotType<MenuItem> (top?.App?.Navigation!.GetFocused ())
  136. .ScreenShot ("MenuBar initial state", _out)
  137. .EnqueueKeyEvent (MenuBar.DefaultKey)
  138. .WaitIteration ()
  139. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  140. .AssertEqual ("_New file", top?.App?.Navigation!.GetFocused ()!.Title)
  141. .AssertTrue (top?.App?.Popover?.GetActivePopover () is PopoverMenu)
  142. .AssertTrue (menuBar?.IsOpen ());
  143. }
  144. [Theory]
  145. [ClassData (typeof (TestDrivers))]
  146. public void DefaultKey_DeActivates (TestDriver d)
  147. {
  148. MenuBar? menuBar = null;
  149. IApplication? app = null;
  150. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  151. .Then ((a) =>
  152. {
  153. app = a;
  154. menuBar = new MenuBar ();
  155. Toplevel top = app.TopRunnable!;
  156. top.Add (
  157. new View ()
  158. {
  159. CanFocus = true,
  160. Id = "focusableView",
  161. });
  162. menuBar.EnableForDesign (ref top);
  163. app.TopRunnable!.Add (menuBar);
  164. })
  165. .WaitIteration ()
  166. .AssertIsNotType<MenuItem> (app?.Navigation!.GetFocused ())
  167. .ScreenShot ("MenuBar initial state", _out)
  168. .EnqueueKeyEvent (MenuBar.DefaultKey)
  169. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  170. .AssertEqual ("_New file", app?.Navigation!.GetFocused ()!.Title)
  171. .EnqueueKeyEvent (MenuBar.DefaultKey)
  172. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  173. .AssertIsNotType<MenuItem> (app?.Navigation!.GetFocused ());
  174. }
  175. [Theory]
  176. [ClassData (typeof (TestDrivers))]
  177. public void ShowHidePopovers (TestDriver d)
  178. {
  179. IApplication? app = null;
  180. using GuiTestContext c = With.A<Window> (80, 25, d, _out)
  181. .Then ((a) =>
  182. {
  183. app = a;
  184. // Create a menu bar with items that have submenus
  185. var fileMenuItem = new MenuBarItem (
  186. "_File",
  187. [
  188. new MenuItem ("_Open", string.Empty, null),
  189. new MenuItem ("_Save", string.Empty, null)
  190. ]);
  191. var menuBar = new MenuBar ([fileMenuItem]) { App = app };
  192. // Initially, no menu should be open
  193. Assert.False (menuBar.IsOpen ());
  194. Assert.False (menuBar.Active);
  195. // Initialize the menu bar
  196. menuBar.BeginInit ();
  197. menuBar.EndInit ();
  198. // Simulate showing a popover menu by manipulating the first menu item
  199. MethodInfo? showPopoverMethod = typeof (MenuBar).GetMethod (
  200. "ShowPopover",
  201. BindingFlags.NonPublic | BindingFlags.Instance);
  202. // Set menu bar to active state using reflection
  203. FieldInfo? activeField = typeof (MenuBar).GetField (
  204. "_active",
  205. BindingFlags.NonPublic | BindingFlags.Instance);
  206. activeField?.SetValue (menuBar, true);
  207. menuBar.CanFocus = true;
  208. // Show the popover menu
  209. showPopoverMethod?.Invoke (menuBar, new object? [] { fileMenuItem });
  210. // Should be active now
  211. Assert.True (menuBar.Active);
  212. // Test if we can hide the popover menu
  213. fileMenuItem.PopoverMenu!.Visible = true;
  214. Assert.True (menuBar.HideActiveItem ());
  215. // Menu should no longer be open or active
  216. Assert.False (menuBar.Active);
  217. Assert.False (menuBar.IsOpen ());
  218. Assert.False (menuBar.CanFocus);
  219. });
  220. }
  221. [Theory]
  222. [ClassData (typeof (TestDrivers))]
  223. public void EnableForDesign_CreatesMenuItems (TestDriver d)
  224. {
  225. using GuiTestContext c = With.A<Window> (80, 25, d, _out)
  226. .Then ((app) =>
  227. {
  228. var menuBar = new MenuBar ();
  229. app.TopRunnable!.Add (menuBar);
  230. // Call EnableForDesign
  231. Toplevel top = app.TopRunnable!;
  232. bool result = menuBar.EnableForDesign (ref top);
  233. // Should return true
  234. Assert.True (result);
  235. // Should have created menu items
  236. Assert.True (menuBar.SubViews.Count > 0);
  237. // Should have File, Edit and Help menus
  238. View? fileMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItem)?.Title == "_File");
  239. View? editMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItem)?.Title == "_Edit");
  240. View? helpMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItem)?.Title == "_Help");
  241. Assert.NotNull (fileMenu);
  242. Assert.NotNull (editMenu);
  243. Assert.NotNull (helpMenu);
  244. })
  245. .ScreenShot ("MenuBar EnableForDesign", _out);
  246. }
  247. [Theory]
  248. [ClassData (typeof (TestDrivers))]
  249. public void Navigation_Left_Right_Wraps (TestDriver d)
  250. {
  251. MenuBar? menuBar = null;
  252. IApplication? app = null;
  253. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  254. .Then ((a) =>
  255. {
  256. app = a;
  257. menuBar = new MenuBar ();
  258. Toplevel top = app.TopRunnable!;
  259. menuBar.EnableForDesign (ref top);
  260. app.TopRunnable!.Add (menuBar);
  261. })
  262. .WaitIteration ()
  263. .ScreenShot ("MenuBar initial state", _out)
  264. .EnqueueKeyEvent (MenuBar.DefaultKey)
  265. .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu)
  266. .AssertTrue (menuBar?.IsOpen ())
  267. .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title)
  268. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  269. .EnqueueKeyEvent (Key.CursorRight)
  270. .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu)
  271. .ScreenShot ("After right arrow", _out)
  272. .AssertEqual ("Cu_t", app?.Navigation?.GetFocused ()!.Title)
  273. .EnqueueKeyEvent (Key.CursorRight)
  274. .ScreenShot ("After second right arrow", _out)
  275. .AssertEqual ("_Online Help...", app?.Navigation?.GetFocused ()!.Title)
  276. .ScreenShot ("After third right arrow", _out)
  277. .EnqueueKeyEvent (Key.CursorRight)
  278. .ScreenShot ("After fourth right arrow", _out)
  279. .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title)
  280. .EnqueueKeyEvent (Key.CursorLeft)
  281. .ScreenShot ("After left arrow", _out)
  282. .AssertEqual ("_Online Help...", app?.Navigation?.GetFocused ()!.Title);
  283. }
  284. [Theory]
  285. [ClassData (typeof (TestDrivers))]
  286. public void MenuBarItem_With_QuitKey_Open_QuitKey_Restores_Focus_Correctly (TestDriver d)
  287. {
  288. MenuBar? menuBar = null;
  289. IApplication? app = null;
  290. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  291. .Then ((a) =>
  292. {
  293. app = a;
  294. menuBar = new MenuBar ();
  295. Toplevel top = app.TopRunnable!;
  296. top.Add (
  297. new View ()
  298. {
  299. CanFocus = true,
  300. Id = "focusableView",
  301. });
  302. menuBar.EnableForDesign (ref top);
  303. app.TopRunnable!.Add (menuBar);
  304. })
  305. .AssertIsNotType<MenuItem> (app!.Navigation!.GetFocused ())
  306. .ScreenShot ("MenuBar initial state", _out)
  307. .EnqueueKeyEvent (MenuBar.DefaultKey)
  308. .AssertEqual ("_New file", app.Navigation!.GetFocused ()!.Title)
  309. .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu)
  310. .AssertTrue (menuBar?.IsOpen ())
  311. .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title)
  312. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  313. .EnqueueKeyEvent (Application.QuitKey)
  314. .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu)
  315. .AssertIsNotType<MenuItem> (app!.Navigation!.GetFocused ());
  316. }
  317. [Theory]
  318. [ClassData (typeof (TestDrivers))]
  319. public void MenuBarItem_Without_QuitKey_Open_QuitKey_Restores_Focus_Correctly (TestDriver d)
  320. {
  321. MenuBar? menuBar = null;
  322. IApplication? app = null;
  323. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  324. .Add (
  325. new View ()
  326. {
  327. CanFocus = true,
  328. Id = "focusableView",
  329. })
  330. .Then ((a) =>
  331. {
  332. app = a;
  333. menuBar = new MenuBar ();
  334. Toplevel? toplevel = app.TopRunnable;
  335. menuBar.EnableForDesign (ref toplevel!);
  336. app.TopRunnable!.Add (menuBar);
  337. })
  338. .WaitIteration ()
  339. .AssertIsNotType<MenuItem> (app?.Navigation!.GetFocused ())
  340. .ScreenShot ("MenuBar initial state", _out)
  341. .EnqueueKeyEvent (MenuBar.DefaultKey)
  342. .EnqueueKeyEvent (Key.CursorRight)
  343. .AssertEqual ("Cu_t", app?.Navigation!.GetFocused ()!.Title)
  344. .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu)
  345. .AssertTrue (menuBar?.IsOpen ())
  346. .AssertEqual ("Cu_t", app?.Navigation?.GetFocused ()!.Title)
  347. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  348. .EnqueueKeyEvent (Application.QuitKey)
  349. .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu)
  350. .AssertIsNotType<MenuItem> (app?.Navigation?.GetFocused ());
  351. }
  352. [Theory]
  353. [ClassData (typeof (TestDrivers))]
  354. public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver d)
  355. {
  356. MenuBar? menuBar = null;
  357. IApplication? app = null;
  358. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  359. .Then ((a) =>
  360. {
  361. app = a;
  362. menuBar = new MenuBar ();
  363. Toplevel top = app.TopRunnable!;
  364. top.Add (
  365. new View ()
  366. {
  367. CanFocus = true,
  368. Id = "focusableView",
  369. });
  370. menuBar.EnableForDesign (ref top);
  371. app.TopRunnable!.Add (menuBar);
  372. })
  373. .WaitIteration ()
  374. .AssertIsNotType<MenuItem> (app!.Navigation!.GetFocused ())
  375. .ScreenShot ("MenuBar initial state", _out)
  376. .EnqueueKeyEvent (MenuBar.DefaultKey)
  377. .AssertEqual ("_New file", app.Navigation!.GetFocused ()!.Title)
  378. .AssertTrue (app?.TopRunnable!.Running)
  379. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  380. .EnqueueKeyEvent (Application.QuitKey)
  381. .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu)
  382. .AssertTrue (app!.TopRunnable!.Running);
  383. }
  384. [Theory]
  385. [ClassData (typeof (TestDrivers))]
  386. public void MenuBarItem_Without_QuitKey_Open_QuitKey_Does_Not_Quit_MenuBar_SuperView (TestDriver d)
  387. {
  388. MenuBar? menuBar = null;
  389. IApplication? app = null;
  390. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  391. .Then ((a) =>
  392. {
  393. app = a;
  394. menuBar = new MenuBar ();
  395. Toplevel top = app.TopRunnable!;
  396. top.Add (
  397. new View ()
  398. {
  399. CanFocus = true,
  400. Id = "focusableView",
  401. });
  402. menuBar.EnableForDesign (ref top);
  403. IEnumerable<MenuItem> items = menuBar.GetMenuItemsWithTitle ("_Quit");
  404. foreach (MenuItem item in items)
  405. {
  406. item.Key = Key.Empty;
  407. }
  408. app.TopRunnable!.Add (menuBar);
  409. })
  410. .WaitIteration ()
  411. .AssertIsNotType<MenuItem> (app?.Navigation!.GetFocused ())
  412. .ScreenShot ("MenuBar initial state", _out)
  413. .EnqueueKeyEvent (MenuBar.DefaultKey)
  414. .AssertEqual ("_New file", app?.Navigation!.GetFocused ()!.Title)
  415. .ScreenShot ($"After {MenuBar.DefaultKey}", _out)
  416. .EnqueueKeyEvent (Application.QuitKey)
  417. .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu)
  418. .AssertTrue (app?.TopRunnable!.Running);
  419. }
  420. [Theory]
  421. [ClassData (typeof (TestDrivers))]
  422. public void MenuBar_Not_Active_DoesNotEat_Space (TestDriver d)
  423. {
  424. int spaceKeyDownCount = 0;
  425. View testView = new View ()
  426. {
  427. CanFocus = true,
  428. Id = "testView",
  429. };
  430. testView.KeyDown += (sender, key) =>
  431. {
  432. if (key == Key.Space)
  433. {
  434. spaceKeyDownCount++;
  435. }
  436. };
  437. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  438. .Then ((a) =>
  439. {
  440. var menuBar = new MenuBar ();
  441. Toplevel top = a.TopRunnable!;
  442. menuBar.EnableForDesign (ref top);
  443. a.TopRunnable!.Add (menuBar);
  444. })
  445. .Add (testView)
  446. .WaitIteration ()
  447. .Focus (testView)
  448. .EnqueueKeyEvent (Key.Space)
  449. .AssertEqual (1, spaceKeyDownCount);
  450. }
  451. [Theory]
  452. [ClassData (typeof (TestDrivers))]
  453. public void MenuBar_Not_Active_DoesNotEat_Enter (TestDriver d)
  454. {
  455. int enterKeyDownCount = 0;
  456. View testView = new View ()
  457. {
  458. CanFocus = true,
  459. Id = "testView",
  460. };
  461. testView.KeyDown += (sender, key) =>
  462. {
  463. if (key == Key.Enter)
  464. {
  465. enterKeyDownCount++;
  466. }
  467. };
  468. using GuiTestContext c = With.A<Window> (50, 20, d, _out)
  469. .Then ((a) =>
  470. {
  471. var menuBar = new MenuBar ();
  472. Toplevel top = a.TopRunnable!;
  473. menuBar.EnableForDesign (ref top);
  474. a.TopRunnable!.Add (menuBar);
  475. })
  476. .Add (testView)
  477. .WaitIteration ()
  478. .Focus (testView)
  479. .EnqueueKeyEvent (Key.Enter)
  480. .AssertEqual (1, enterKeyDownCount);
  481. }
  482. }