SubviewTests.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. namespace ViewBaseTests.Hierarchy;
  2. [Collection ("Global Test Setup")]
  3. public class SubViewTests
  4. {
  5. [Fact]
  6. public void SuperViewChanged_Raised_On_Add ()
  7. {
  8. var super = new View { };
  9. var sub = new View ();
  10. int superRaisedCount = 0;
  11. int subRaisedCount = 0;
  12. super.SuperViewChanged += (s, e) =>
  13. {
  14. superRaisedCount++;
  15. };
  16. sub.SuperViewChanged += (s, e) =>
  17. {
  18. if (sub.SuperView is { })
  19. {
  20. subRaisedCount++;
  21. }
  22. };
  23. super.Add (sub);
  24. Assert.True (super.SubViews.Count == 1);
  25. Assert.Equal (super, sub.SuperView);
  26. Assert.Equal (0, superRaisedCount);
  27. Assert.Equal (1, subRaisedCount);
  28. }
  29. [Fact]
  30. public void SuperViewChanged_Raised_On_Remove ()
  31. {
  32. var super = new View { };
  33. var sub = new View ();
  34. int superRaisedCount = 0;
  35. int subRaisedCount = 0;
  36. super.SuperViewChanged += (s, e) =>
  37. {
  38. superRaisedCount++;
  39. };
  40. sub.SuperViewChanged += (s, e) =>
  41. {
  42. if (sub.SuperView is null)
  43. {
  44. subRaisedCount++;
  45. }
  46. };
  47. super.Add (sub);
  48. Assert.True (super.SubViews.Count == 1);
  49. Assert.Equal (super, sub.SuperView);
  50. Assert.Equal (0, superRaisedCount);
  51. Assert.Equal (0, subRaisedCount);
  52. super.Remove (sub);
  53. Assert.Empty (super.SubViews);
  54. Assert.NotEqual (super, sub.SuperView);
  55. Assert.Equal (0, superRaisedCount);
  56. Assert.Equal (1, subRaisedCount);
  57. }
  58. [Fact]
  59. public void SuperView_Set_On_Add_Remove ()
  60. {
  61. var superView = new View ();
  62. var view = new View ();
  63. Assert.Null (view.SuperView);
  64. superView.Add (view);
  65. Assert.Equal (superView, view.SuperView);
  66. superView.Remove (view);
  67. Assert.Null (view.SuperView);
  68. }
  69. // TODO: Consider a feature that will change the ContentSize to fit the subviews.
  70. [Fact]
  71. public void Add_Does_Not_Impact_ContentSize ()
  72. {
  73. var view = new View ();
  74. view.SetContentSize (new Size (1, 1));
  75. var subview = new View
  76. {
  77. X = 10,
  78. Y = 10
  79. };
  80. Assert.Equal (new (1, 1), view.GetContentSize ());
  81. view.Add (subview);
  82. Assert.Equal (new (1, 1), view.GetContentSize ());
  83. }
  84. [Fact]
  85. public void Remove_Does_Not_Impact_ContentSize ()
  86. {
  87. var view = new View ();
  88. view.SetContentSize (new Size (1, 1));
  89. var subview = new View
  90. {
  91. X = 10,
  92. Y = 10
  93. };
  94. Assert.Equal (new (1, 1), view.GetContentSize ());
  95. view.Add (subview);
  96. Assert.Equal (new (1, 1), view.GetContentSize ());
  97. view.SetContentSize (new Size (5, 5));
  98. Assert.Equal (new (5, 5), view.GetContentSize ());
  99. view.Remove (subview);
  100. Assert.Equal (new (5, 5), view.GetContentSize ());
  101. }
  102. [Theory]
  103. [InlineData (ViewArrangement.Fixed)]
  104. [InlineData (ViewArrangement.Overlapped)]
  105. public void MoveSubViewToEnd_ViewArrangement (ViewArrangement arrangement)
  106. {
  107. View superView = new () { Arrangement = arrangement };
  108. var subview1 = new View
  109. {
  110. Id = "subview1"
  111. };
  112. var subview2 = new View
  113. {
  114. Id = "subview2"
  115. };
  116. var subview3 = new View
  117. {
  118. Id = "subview3"
  119. };
  120. superView.Add (subview1, subview2, subview3);
  121. superView.MoveSubViewToEnd (subview1);
  122. Assert.Equal ([subview2, subview3, subview1], superView.SubViews.ToArray ());
  123. superView.MoveSubViewToEnd (subview2);
  124. Assert.Equal ([subview3, subview1, subview2], superView.SubViews.ToArray ());
  125. superView.MoveSubViewToEnd (subview3);
  126. Assert.Equal ([subview1, subview2, subview3], superView.SubViews.ToArray ());
  127. }
  128. [Fact]
  129. public void MoveSubViewToStart ()
  130. {
  131. View superView = new ();
  132. var subview1 = new View
  133. {
  134. Id = "subview1"
  135. };
  136. var subview2 = new View
  137. {
  138. Id = "subview2"
  139. };
  140. var subview3 = new View
  141. {
  142. Id = "subview3"
  143. };
  144. superView.Add (subview1, subview2, subview3);
  145. superView.MoveSubViewToStart (subview2);
  146. Assert.Equal (subview2, superView.SubViews.ElementAt (0));
  147. superView.MoveSubViewToStart (subview3);
  148. Assert.Equal (subview3, superView.SubViews.ElementAt (0));
  149. }
  150. [Fact]
  151. public void MoveSubViewTowardsFront ()
  152. {
  153. View superView = new ();
  154. var subview1 = new View
  155. {
  156. Id = "subview1"
  157. };
  158. var subview2 = new View
  159. {
  160. Id = "subview2"
  161. };
  162. var subview3 = new View
  163. {
  164. Id = "subview3"
  165. };
  166. superView.Add (subview1, subview2, subview3);
  167. superView.MoveSubViewTowardsStart (subview2);
  168. Assert.Equal (subview2, superView.SubViews.ElementAt (0));
  169. superView.MoveSubViewTowardsStart (subview3);
  170. Assert.Equal (subview3, superView.SubViews.ElementAt (1));
  171. // Already at front, what happens?
  172. superView.MoveSubViewTowardsStart (subview2);
  173. Assert.Equal (subview2, superView.SubViews.ElementAt (0));
  174. }
  175. [Fact]
  176. public void MoveSubViewToEnd ()
  177. {
  178. View superView = new ();
  179. var subview1 = new View
  180. {
  181. Id = "subview1"
  182. };
  183. var subview2 = new View
  184. {
  185. Id = "subview2"
  186. };
  187. var subview3 = new View
  188. {
  189. Id = "subview3"
  190. };
  191. superView.Add (subview1, subview2, subview3);
  192. superView.MoveSubViewToEnd (subview1);
  193. Assert.Equal (subview1, superView.SubViews.ToArray () [^1]);
  194. superView.MoveSubViewToEnd (subview2);
  195. Assert.Equal (subview2, superView.SubViews.ToArray () [^1]);
  196. }
  197. [Fact]
  198. public void MoveSubViewTowardsEnd ()
  199. {
  200. View superView = new ();
  201. var subview1 = new View
  202. {
  203. Id = "subview1"
  204. };
  205. var subview2 = new View
  206. {
  207. Id = "subview2"
  208. };
  209. var subview3 = new View
  210. {
  211. Id = "subview3"
  212. };
  213. superView.Add (subview1, subview2, subview3);
  214. superView.MoveSubViewTowardsEnd (subview2);
  215. Assert.Equal (subview2, superView.SubViews.ToArray () [^1]);
  216. superView.MoveSubViewTowardsEnd (subview1);
  217. Assert.Equal (subview1, superView.SubViews.ToArray () [1]);
  218. // Already at end, what happens?
  219. superView.MoveSubViewTowardsEnd (subview2);
  220. Assert.Equal (subview2, superView.SubViews.ToArray () [^1]);
  221. }
  222. [Fact]
  223. public void IsInHierarchy_ViewIsNull_ReturnsFalse ()
  224. {
  225. // Arrange
  226. var start = new View ();
  227. // Act
  228. bool result = View.IsInHierarchy (start, null);
  229. // Assert
  230. Assert.False (result);
  231. }
  232. [Fact]
  233. public void IsInHierarchy_StartIsNull_ReturnsFalse ()
  234. {
  235. // Arrange
  236. var view = new View ();
  237. // Act
  238. bool result = View.IsInHierarchy (null, view);
  239. // Assert
  240. Assert.False (result);
  241. }
  242. [Fact]
  243. public void IsInHierarchy_ViewIsStart_ReturnsTrue ()
  244. {
  245. // Arrange
  246. var start = new View ();
  247. // Act
  248. bool result = View.IsInHierarchy (start, start);
  249. // Assert
  250. Assert.True (result);
  251. }
  252. [Fact]
  253. public void IsInHierarchy_ViewIsDirectSubView_ReturnsTrue ()
  254. {
  255. // Arrange
  256. var start = new View ();
  257. var subview = new View ();
  258. start.Add (subview);
  259. // Act
  260. bool result = View.IsInHierarchy (start, subview);
  261. // Assert
  262. Assert.True (result);
  263. }
  264. [Fact]
  265. public void IsInHierarchy_ViewIsNestedSubView_ReturnsTrue ()
  266. {
  267. // Arrange
  268. var start = new View ();
  269. var subview = new View ();
  270. var nestedSubView = new View ();
  271. start.Add (subview);
  272. subview.Add (nestedSubView);
  273. // Act
  274. bool result = View.IsInHierarchy (start, nestedSubView);
  275. // Assert
  276. Assert.True (result);
  277. }
  278. [Fact]
  279. public void IsInHierarchy_ViewIsNotInHierarchy_ReturnsFalse ()
  280. {
  281. // Arrange
  282. var start = new View ();
  283. var subview = new View ();
  284. // Act
  285. bool result = View.IsInHierarchy (start, subview);
  286. // Assert
  287. Assert.False (result);
  288. }
  289. [Theory]
  290. [CombinatorialData]
  291. public void IsInHierarchy_ViewIsInAdornments_ReturnsTrue (bool includeAdornments)
  292. {
  293. // Arrange
  294. var start = new View
  295. {
  296. Id = "start"
  297. };
  298. var inPadding = new View
  299. {
  300. Id = "inPadding"
  301. };
  302. start.Padding!.Add (inPadding);
  303. // Act
  304. bool result = View.IsInHierarchy (start, inPadding, includeAdornments);
  305. // Assert
  306. Assert.Equal (includeAdornments, result);
  307. }
  308. [Fact]
  309. public void SuperView_Set_Raises_SuperViewChangedEvents ()
  310. {
  311. // Arrange
  312. var view = new View ();
  313. var superView = new View ();
  314. int superViewChangedCount = 0;
  315. int superViewChangingCount = 0;
  316. view.SuperViewChanged += (s, e) =>
  317. {
  318. superViewChangedCount++;
  319. };
  320. view.SuperViewChanging += (s, e) =>
  321. {
  322. superViewChangingCount++;
  323. };
  324. // Act
  325. superView.Add (view);
  326. // Assert
  327. Assert.Equal (1, superViewChangingCount);
  328. Assert.Equal (1, superViewChangedCount);
  329. }
  330. [Fact]
  331. public void GetTopSuperView_Test ()
  332. {
  333. var v1 = new View ();
  334. var fv1 = new FrameView ();
  335. fv1.Add (v1);
  336. var tf1 = new TextField ();
  337. var w1 = new Window ();
  338. w1.Add (fv1, tf1);
  339. var top1 = new Runnable ();
  340. top1.Add (w1);
  341. var v2 = new View ();
  342. var fv2 = new FrameView ();
  343. fv2.Add (v2);
  344. var tf2 = new TextField ();
  345. var w2 = new Window ();
  346. w2.Add (fv2, tf2);
  347. var top2 = new Runnable ();
  348. top2.Add (w2);
  349. Assert.Equal (top1, v1.GetTopSuperView ());
  350. Assert.Equal (top2, v2.GetTopSuperView ());
  351. v1.Dispose ();
  352. fv1.Dispose ();
  353. tf1.Dispose ();
  354. w1.Dispose ();
  355. top1.Dispose ();
  356. v2.Dispose ();
  357. fv2.Dispose ();
  358. tf2.Dispose ();
  359. w2.Dispose ();
  360. top2.Dispose ();
  361. }
  362. [Fact]
  363. public void Initialized_Event_Comparing_With_Added_Event ()
  364. {
  365. var top = new Runnable { Id = "0" }; // Frame: 0, 0, 80, 25; Viewport: 0, 0, 80, 25
  366. var winAddedToTop = new Window
  367. {
  368. Id = "t", Width = Dim.Fill (), Height = Dim.Fill ()
  369. }; // Frame: 0, 0, 80, 25; Viewport: 0, 0, 78, 23
  370. var v1AddedToWin = new View
  371. {
  372. Id = "v1", Width = Dim.Fill (), Height = Dim.Fill ()
  373. }; // Frame: 1, 1, 78, 23 (because Windows has a border)
  374. var v2AddedToWin = new View
  375. {
  376. Id = "v2", Width = Dim.Fill (), Height = Dim.Fill ()
  377. }; // Frame: 1, 1, 78, 23 (because Windows has a border)
  378. var svAddedTov1 = new View
  379. {
  380. Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill ()
  381. }; // Frame: 1, 1, 78, 23 (same as it's superview v1AddedToWin)
  382. int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0;
  383. winAddedToTop.SubViewAdded += (s, e) =>
  384. {
  385. Assert.Equal (e.SuperView!.Frame.Width, winAddedToTop.Frame.Width);
  386. Assert.Equal (e.SuperView.Frame.Height, winAddedToTop.Frame.Height);
  387. };
  388. v1AddedToWin.SubViewAdded += (s, e) =>
  389. {
  390. Assert.Equal (e.SuperView!.Frame.Width, v1AddedToWin.Frame.Width);
  391. Assert.Equal (e.SuperView.Frame.Height, v1AddedToWin.Frame.Height);
  392. };
  393. v2AddedToWin.SubViewAdded += (s, e) =>
  394. {
  395. Assert.Equal (e.SuperView!.Frame.Width, v2AddedToWin.Frame.Width);
  396. Assert.Equal (e.SuperView.Frame.Height, v2AddedToWin.Frame.Height);
  397. };
  398. svAddedTov1.SubViewAdded += (s, e) =>
  399. {
  400. Assert.Equal (e.SuperView!.Frame.Width, svAddedTov1.Frame.Width);
  401. Assert.Equal (e.SuperView.Frame.Height, svAddedTov1.Frame.Height);
  402. };
  403. top.Initialized += (s, e) =>
  404. {
  405. tc++;
  406. Assert.Equal (1, tc);
  407. Assert.Equal (1, wc);
  408. Assert.Equal (1, v1c);
  409. Assert.Equal (1, v2c);
  410. Assert.Equal (1, sv1c);
  411. Assert.True (top.CanFocus);
  412. Assert.True (winAddedToTop.CanFocus);
  413. Assert.False (v1AddedToWin.CanFocus);
  414. Assert.False (v2AddedToWin.CanFocus);
  415. Assert.False (svAddedTov1.CanFocus);
  416. top.Layout ();
  417. };
  418. winAddedToTop.Initialized += (s, e) =>
  419. {
  420. wc++;
  421. Assert.Equal (top.Viewport.Width, winAddedToTop.Frame.Width);
  422. Assert.Equal (top.Viewport.Height, winAddedToTop.Frame.Height);
  423. };
  424. v1AddedToWin.Initialized += (s, e) =>
  425. {
  426. v1c++;
  427. // Top.Frame: 0, 0, 80, 25; Top.Viewport: 0, 0, 80, 25
  428. // BUGBUG: This is wrong, it should be 78, 23. This test has always been broken.
  429. // in no way should the v1AddedToWin.Frame be the same as the Top.Frame/Viewport
  430. // as it is a subview of winAddedToTop, which has a border!
  431. //Assert.Equal (top.Viewport.Width, v1AddedToWin.Frame.Width);
  432. //Assert.Equal (top.Viewport.Height, v1AddedToWin.Frame.Height);
  433. };
  434. v2AddedToWin.Initialized += (s, e) =>
  435. {
  436. v2c++;
  437. // Top.Frame: 0, 0, 80, 25; Top.Viewport: 0, 0, 80, 25
  438. // BUGBUG: This is wrong, it should be 78, 23. This test has always been broken.
  439. // in no way should the v2AddedToWin.Frame be the same as the Top.Frame/Viewport
  440. // as it is a subview of winAddedToTop, which has a border!
  441. //Assert.Equal (top.Viewport.Width, v2AddedToWin.Frame.Width);
  442. //Assert.Equal (top.Viewport.Height, v2AddedToWin.Frame.Height);
  443. };
  444. svAddedTov1.Initialized += (s, e) =>
  445. {
  446. sv1c++;
  447. // Top.Frame: 0, 0, 80, 25; Top.Viewport: 0, 0, 80, 25
  448. // BUGBUG: This is wrong, it should be 78, 23. This test has always been broken.
  449. // in no way should the svAddedTov1.Frame be the same as the Top.Frame/Viewport
  450. // because sv1AddedTov1 is a subview of v1AddedToWin, which is a subview of
  451. // winAddedToTop, which has a border!
  452. //Assert.Equal (top.Viewport.Width, svAddedTov1.Frame.Width);
  453. //Assert.Equal (top.Viewport.Height, svAddedTov1.Frame.Height);
  454. Assert.False (svAddedTov1.CanFocus);
  455. //Assert.Throws<InvalidOperationException> (() => svAddedTov1.CanFocus = true);
  456. Assert.False (svAddedTov1.CanFocus);
  457. };
  458. v1AddedToWin.Add (svAddedTov1);
  459. winAddedToTop.Add (v1AddedToWin, v2AddedToWin);
  460. top.Add (winAddedToTop);
  461. top.BeginInit ();
  462. top.EndInit ();
  463. Assert.Equal (1, tc);
  464. Assert.Equal (1, wc);
  465. Assert.Equal (1, v1c);
  466. Assert.Equal (1, v2c);
  467. Assert.Equal (1, sv1c);
  468. Assert.True (top.CanFocus);
  469. Assert.True (winAddedToTop.CanFocus);
  470. Assert.False (v1AddedToWin.CanFocus);
  471. Assert.False (v2AddedToWin.CanFocus);
  472. Assert.False (svAddedTov1.CanFocus);
  473. v1AddedToWin.CanFocus = true;
  474. Assert.False (svAddedTov1.CanFocus); // False because sv1 was disposed and it isn't a subview of v1.
  475. }
  476. [Fact]
  477. public void SuperViewChanged_Raised_On_SubViewAdded_SubViewRemoved ()
  478. {
  479. var isAdded = false;
  480. View superView = new () { Id = "superView" };
  481. View subView = new () { Id = "subView" };
  482. superView.SubViewAdded += (s, e) =>
  483. {
  484. Assert.True (isAdded);
  485. Assert.Equal (superView, subView.SuperView);
  486. Assert.Equal (subView, e.SubView);
  487. Assert.Equal (superView, e.SuperView);
  488. };
  489. superView.SubViewRemoved += (s, e) =>
  490. {
  491. Assert.False (isAdded);
  492. Assert.NotEqual (superView, subView.SuperView);
  493. Assert.Equal (subView, e.SubView);
  494. Assert.Equal (superView, e.SuperView);
  495. };
  496. subView.SuperViewChanged += (s, e) => { isAdded = subView.SuperView == superView; };
  497. superView.Add (subView);
  498. Assert.True (isAdded);
  499. Assert.Equal (superView, subView.SuperView);
  500. Assert.Single (superView.SubViews);
  501. superView.Remove (subView);
  502. Assert.False (isAdded);
  503. Assert.NotEqual (superView, subView.SuperView);
  504. Assert.Empty (superView.SubViews);
  505. }
  506. [Fact]
  507. public void RemoveAll_Removes_All_SubViews ()
  508. {
  509. // Arrange
  510. var superView = new View ();
  511. var subView1 = new View ();
  512. var subView2 = new View ();
  513. var subView3 = new View ();
  514. superView.Add (subView1, subView2, subView3);
  515. // Act
  516. var removedViews = superView.RemoveAll ();
  517. // Assert
  518. Assert.Empty (superView.SubViews);
  519. Assert.Equal (3, removedViews.Count);
  520. Assert.Contains (subView1, removedViews);
  521. Assert.Contains (subView2, removedViews);
  522. Assert.Contains (subView3, removedViews);
  523. }
  524. [Fact]
  525. public void RemoveAllTView_Removes_All_SubViews_Of_Specific_Type ()
  526. {
  527. // Arrange
  528. var superView = new View ();
  529. var subView1 = new View ();
  530. var subView2 = new View ();
  531. var subView3 = new View ();
  532. var subView4 = new Button ();
  533. superView.Add (subView1, subView2, subView3, subView4);
  534. // Act
  535. var removedViews = superView.RemoveAll<Button> ();
  536. // Assert
  537. Assert.Equal (3, superView.SubViews.Count);
  538. Assert.DoesNotContain (subView4, superView.SubViews);
  539. Assert.Single (removedViews);
  540. Assert.Contains (subView4, removedViews);
  541. }
  542. [Fact]
  543. public void RemoveAllTView_Does_Not_Remove_Other_Types ()
  544. {
  545. // Arrange
  546. var superView = new View ();
  547. var subView1 = new View ();
  548. var subView2 = new Button ();
  549. var subView3 = new Label ();
  550. superView.Add (subView1, subView2, subView3);
  551. // Act
  552. var removedViews = superView.RemoveAll<Button> ();
  553. // Assert
  554. Assert.Equal (2, superView.SubViews.Count);
  555. Assert.Contains (subView1, superView.SubViews);
  556. Assert.Contains (subView3, superView.SubViews);
  557. Assert.Single (removedViews);
  558. Assert.Contains (subView2, removedViews);
  559. }
  560. [Fact]
  561. public void SuperViewChanging_Raised_Before_SuperViewChanged ()
  562. {
  563. // Arrange
  564. var superView = new View ();
  565. var subView = new View ();
  566. var events = new List<string> ();
  567. subView.SuperViewChanging += (s, e) => { events.Add ("SuperViewChanging"); };
  568. subView.SuperViewChanged += (s, e) => { events.Add ("SuperViewChanged"); };
  569. // Act
  570. superView.Add (subView);
  571. // Assert
  572. Assert.Equal (2, events.Count);
  573. Assert.Equal ("SuperViewChanging", events [0]);
  574. Assert.Equal ("SuperViewChanged", events [1]);
  575. }
  576. [Fact]
  577. public void SuperViewChanging_Provides_OldSuperView_On_Add ()
  578. {
  579. // Arrange
  580. var superView = new View ();
  581. var subView = new View ();
  582. View? currentValueInEvent = new View (); // Set to non-null to ensure it gets updated
  583. View? newValueInEvent = null;
  584. subView.SuperViewChanging += (s, e) =>
  585. {
  586. currentValueInEvent = e.CurrentValue;
  587. newValueInEvent = e.NewValue;
  588. };
  589. // Act
  590. superView.Add (subView);
  591. // Assert
  592. Assert.Null (currentValueInEvent); // Was null before add
  593. Assert.Equal (superView, newValueInEvent); // Will be superView after add
  594. }
  595. [Fact]
  596. public void SuperViewChanging_Provides_OldSuperView_On_Remove ()
  597. {
  598. // Arrange
  599. var superView = new View ();
  600. var subView = new View ();
  601. superView.Add (subView);
  602. View? currentValueInEvent = null;
  603. View? newValueInEvent = new View (); // Set to non-null to ensure it gets updated
  604. subView.SuperViewChanging += (s, e) =>
  605. {
  606. currentValueInEvent = e.CurrentValue;
  607. newValueInEvent = e.NewValue;
  608. };
  609. // Act
  610. superView.Remove (subView);
  611. // Assert
  612. Assert.Equal (superView, currentValueInEvent); // Was superView before remove
  613. Assert.Null (newValueInEvent); // Will be null after remove
  614. }
  615. [Fact]
  616. public void SuperViewChanging_Allows_Access_To_App_Before_Remove ()
  617. {
  618. // Arrange
  619. using IApplication app = Application.Create ();
  620. var runnable = new Runnable<bool> ();
  621. var subView = new View ();
  622. runnable.Add (subView);
  623. SessionToken? token = app.Begin (runnable);
  624. IApplication? appInEvent = null;
  625. subView.SuperViewChanging += (s, e) =>
  626. {
  627. Assert.NotNull (s);
  628. // At this point, SuperView is still set, so App should be accessible
  629. appInEvent = (s as View)?.App;
  630. };
  631. Assert.NotNull (runnable.App);
  632. // Act
  633. runnable.Remove (subView);
  634. // Assert
  635. Assert.NotNull (appInEvent);
  636. Assert.Equal (app, appInEvent);
  637. app.End (token!);
  638. runnable.Dispose ();
  639. }
  640. [Fact]
  641. public void OnSuperViewChanging_Called_Before_OnSuperViewChanged ()
  642. {
  643. // Arrange
  644. var superView = new View ();
  645. var events = new List<string> ();
  646. var subView = new TestViewWithSuperViewEvents (events);
  647. // Act
  648. superView.Add (subView);
  649. // Assert
  650. Assert.Equal (2, events.Count);
  651. Assert.Equal ("OnSuperViewChanging", events [0]);
  652. Assert.Equal ("OnSuperViewChanged", events [1]);
  653. }
  654. [Fact]
  655. public void SuperViewChanging_Raised_When_Changing_Between_SuperViews ()
  656. {
  657. // Arrange
  658. var superView1 = new View ();
  659. var superView2 = new View ();
  660. var subView = new View ();
  661. superView1.Add (subView);
  662. View? currentValueInEvent = null;
  663. View? newValueInEvent = null;
  664. subView.SuperViewChanging += (s, e) =>
  665. {
  666. currentValueInEvent = e.CurrentValue;
  667. newValueInEvent = e.NewValue;
  668. };
  669. // Act
  670. superView2.Add (subView);
  671. // Assert
  672. Assert.Equal (superView1, currentValueInEvent);
  673. Assert.Equal (superView2, newValueInEvent);
  674. }
  675. // Helper class for testing virtual method calls
  676. private class TestViewWithSuperViewEvents : View
  677. {
  678. private readonly List<string> _events;
  679. public TestViewWithSuperViewEvents (List<string> events) { _events = events; }
  680. protected override bool OnSuperViewChanging (ValueChangingEventArgs<View?> args)
  681. {
  682. _events.Add ("OnSuperViewChanging");
  683. return base.OnSuperViewChanging (args);
  684. }
  685. protected override void OnSuperViewChanged (ValueChangedEventArgs<View?> args)
  686. {
  687. _events.Add ("OnSuperViewChanged");
  688. base.OnSuperViewChanged (args);
  689. }
  690. }
  691. [Fact]
  692. public void SuperViewChanging_Can_Be_Cancelled_Via_Event ()
  693. {
  694. // Arrange
  695. var superView = new View ();
  696. var subView = new View ();
  697. subView.SuperViewChanging += (s, e) =>
  698. {
  699. e.Handled = true; // Cancel the change
  700. };
  701. // Act
  702. superView.Add (subView);
  703. // Assert - SuperView should not be set because the change was cancelled
  704. Assert.Null (subView.SuperView);
  705. Assert.Empty (superView.SubViews);
  706. }
  707. [Fact]
  708. public void SuperViewChanging_Can_Be_Cancelled_Via_Virtual_Method ()
  709. {
  710. // Arrange
  711. var superView = new View ();
  712. var subView = new TestViewThatCancelsChange ();
  713. // Act
  714. superView.Add (subView);
  715. // Assert - SuperView should not be set because the change was cancelled
  716. Assert.Null (subView.SuperView);
  717. Assert.Empty (superView.SubViews);
  718. }
  719. [Fact]
  720. public void SuperViewChanging_Virtual_Method_Cancellation_Prevents_Event ()
  721. {
  722. // Arrange
  723. var superView = new View ();
  724. var subView = new TestViewThatCancelsChange ();
  725. var eventRaised = false;
  726. subView.SuperViewChanging += (s, e) =>
  727. {
  728. eventRaised = true;
  729. };
  730. // Act
  731. superView.Add (subView);
  732. // Assert - Event should not be raised because virtual method cancelled first
  733. Assert.False (eventRaised);
  734. Assert.Null (subView.SuperView);
  735. }
  736. [Fact]
  737. public void SuperViewChanging_Cancellation_On_Remove ()
  738. {
  739. // Arrange
  740. var superView = new View ();
  741. var subView = new View ();
  742. superView.Add (subView);
  743. Assert.Equal (superView, subView.SuperView);
  744. subView.SuperViewChanging += (s, e) =>
  745. {
  746. // Cancel removal if trying to set to null
  747. if (e.NewValue is null)
  748. {
  749. e.Handled = true;
  750. }
  751. };
  752. // Act
  753. superView.Remove (subView);
  754. // Assert - SuperView should still be set because removal was cancelled
  755. Assert.Equal (superView, subView.SuperView);
  756. Assert.Single (superView.SubViews);
  757. }
  758. [Fact]
  759. public void SuperViewChanging_Cancellation_When_Changing_Between_SuperViews ()
  760. {
  761. // Arrange
  762. var superView1 = new View ();
  763. var superView2 = new View ();
  764. var subView = new View ();
  765. superView1.Add (subView);
  766. subView.SuperViewChanging += (s, e) =>
  767. {
  768. // Cancel if trying to move to superView2
  769. if (e.NewValue == superView2)
  770. {
  771. e.Handled = true;
  772. }
  773. };
  774. // Act
  775. superView2.Add (subView);
  776. // Assert - Should still be in superView1 because change was cancelled
  777. Assert.Equal (superView1, subView.SuperView);
  778. Assert.Single (superView1.SubViews);
  779. Assert.Empty (superView2.SubViews);
  780. }
  781. // Helper class for testing cancellation
  782. private class TestViewThatCancelsChange : View
  783. {
  784. protected override bool OnSuperViewChanging (ValueChangingEventArgs<View?> args)
  785. {
  786. return true; // Always cancel the change
  787. }
  788. }
  789. }