MouseDragTests.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. namespace ViewBaseTests.Mouse;
  2. /// <summary>
  3. /// Parallelizable tests for mouse drag functionality on movable and resizable views.
  4. /// These tests validate mouse drag behavior without Application.Init or global state.
  5. /// </summary>
  6. [Trait ("Category", "Input")]
  7. public class MouseDragTests
  8. {
  9. #region Movable View Drag Tests
  10. [Fact]
  11. public void MovableView_MouseDrag_UpdatesPosition ()
  12. {
  13. // Arrange
  14. using IApplication app = Application.Create ();
  15. app.Init ("fake");
  16. View superView = new ()
  17. {
  18. Width = 50,
  19. Height = 50,
  20. App = app
  21. };
  22. View movableView = new ()
  23. {
  24. X = 10,
  25. Y = 10,
  26. Width = 10,
  27. Height = 10,
  28. Arrangement = ViewArrangement.Movable,
  29. BorderStyle = LineStyle.Single,
  30. App = app
  31. };
  32. superView.Add (movableView);
  33. // Add to a runnable so the views are part of the application
  34. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  35. runnable.Add (superView);
  36. app.Begin (runnable);
  37. // Simulate mouse press on border to start drag
  38. MouseEventArgs pressEvent = new ()
  39. {
  40. ScreenPosition = new (10, 10), // Screen position
  41. Flags = MouseFlags.Button1Pressed
  42. };
  43. // Act - Start drag
  44. app.Mouse.RaiseMouseEvent (pressEvent);
  45. // Simulate mouse drag
  46. MouseEventArgs dragEvent = new ()
  47. {
  48. ScreenPosition = new (15, 15), // New screen position
  49. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  50. };
  51. app.Mouse.RaiseMouseEvent (dragEvent);
  52. // Assert - View should have moved
  53. Assert.Equal (15, movableView.Frame.X);
  54. Assert.Equal (15, movableView.Frame.Y);
  55. Assert.Equal (10, movableView.Frame.Width);
  56. Assert.Equal (10, movableView.Frame.Height);
  57. app.End (app.SessionStack!.First ());
  58. runnable.Dispose ();
  59. superView.Dispose ();
  60. }
  61. [Fact]
  62. public void MovableView_MouseDrag_WithSuperview_UsesCorrectCoordinates ()
  63. {
  64. // Arrange
  65. using IApplication app = Application.Create ();
  66. app.Init ("fake");
  67. View superView = new ()
  68. {
  69. X = 5,
  70. Y = 5,
  71. Width = 50,
  72. Height = 50
  73. };
  74. View movableView = new ()
  75. {
  76. X = 10,
  77. Y = 10,
  78. Width = 10,
  79. Height = 10,
  80. Arrangement = ViewArrangement.Movable,
  81. BorderStyle = LineStyle.Single
  82. };
  83. superView.Add (movableView);
  84. // Add to a runnable so the views are part of the application
  85. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  86. runnable.Add (superView);
  87. app.Begin (runnable);
  88. // Simulate mouse press on border
  89. MouseEventArgs pressEvent = new ()
  90. {
  91. ScreenPosition = new (15, 15), // 5+10 offset
  92. Flags = MouseFlags.Button1Pressed
  93. };
  94. app.Mouse.RaiseMouseEvent (pressEvent);
  95. // Simulate mouse drag
  96. MouseEventArgs dragEvent = new ()
  97. {
  98. ScreenPosition = new (18, 18), // Moved 3,3
  99. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  100. };
  101. app.Mouse.RaiseMouseEvent (dragEvent);
  102. // Assert - View should have moved relative to superview
  103. Assert.Equal (13, movableView.Frame.X); // 10 + 3
  104. Assert.Equal (13, movableView.Frame.Y); // 10 + 3
  105. app.End (app.SessionStack!.First ());
  106. runnable.Dispose ();
  107. superView.Dispose ();
  108. }
  109. [Fact]
  110. public void MovableView_MouseRelease_StopsDrag ()
  111. {
  112. // Arrange
  113. using IApplication app = Application.Create ();
  114. app.Init ("fake");
  115. View superView = new ()
  116. {
  117. Width = 50,
  118. Height = 50
  119. };
  120. View movableView = new ()
  121. {
  122. X = 10,
  123. Y = 10,
  124. Width = 10,
  125. Height = 10,
  126. Arrangement = ViewArrangement.Movable,
  127. BorderStyle = LineStyle.Single
  128. };
  129. superView.Add (movableView);
  130. // Add to a runnable so the views are part of the application
  131. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  132. runnable.Add (superView);
  133. app.Begin (runnable);
  134. // Start drag
  135. MouseEventArgs pressEvent = new ()
  136. {
  137. ScreenPosition = new (10, 10),
  138. Flags = MouseFlags.Button1Pressed
  139. };
  140. app.Mouse.RaiseMouseEvent (pressEvent);
  141. // Drag
  142. MouseEventArgs dragEvent = new ()
  143. {
  144. ScreenPosition = new (15, 15),
  145. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  146. };
  147. app.Mouse.RaiseMouseEvent (dragEvent);
  148. // Release
  149. MouseEventArgs releaseEvent = new ()
  150. {
  151. ScreenPosition = new (15, 15),
  152. Flags = MouseFlags.Button1Released
  153. };
  154. app.Mouse.RaiseMouseEvent (releaseEvent);
  155. // Assert - Position should remain at dragged location
  156. Assert.Equal (15, movableView.Frame.X);
  157. Assert.Equal (15, movableView.Frame.Y);
  158. app.End (app.SessionStack!.First ());
  159. runnable.Dispose ();
  160. superView.Dispose ();
  161. }
  162. #endregion
  163. #region Resizable View Drag Tests
  164. [Fact]
  165. public void ResizableView_RightResize_Drag_IncreasesWidth ()
  166. {
  167. // Arrange
  168. using IApplication app = Application.Create ();
  169. app.Init ("fake");
  170. View superView = new ()
  171. {
  172. Width = 50,
  173. Height = 50
  174. };
  175. View resizableView = new ()
  176. {
  177. X = 10,
  178. Y = 10,
  179. Width = 10,
  180. Height = 10,
  181. Arrangement = ViewArrangement.RightResizable,
  182. BorderStyle = LineStyle.Single
  183. };
  184. superView.Add (resizableView);
  185. // Add to a runnable so the views are part of the application
  186. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  187. runnable.Add (superView);
  188. app.Begin (runnable);
  189. // Simulate mouse press on right border
  190. MouseEventArgs pressEvent = new ()
  191. {
  192. ScreenPosition = new (19, 15),
  193. Flags = MouseFlags.Button1Pressed
  194. };
  195. app.Mouse.RaiseMouseEvent (pressEvent);
  196. // Simulate mouse drag to the right
  197. MouseEventArgs dragEvent = new ()
  198. {
  199. ScreenPosition = new (24, 15),
  200. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  201. };
  202. app.Mouse.RaiseMouseEvent (dragEvent);
  203. // Assert - Width should have increased
  204. Assert.Equal (10, resizableView.Frame.X); // X unchanged
  205. Assert.Equal (10, resizableView.Frame.Y); // Y unchanged
  206. Assert.Equal (15, resizableView.Frame.Width); // Width increased by 5
  207. Assert.Equal (10, resizableView.Frame.Height); // Height unchanged
  208. app.End (app.SessionStack!.First ());
  209. runnable.Dispose ();
  210. superView.Dispose ();
  211. }
  212. [Fact]
  213. public void ResizableView_BottomResize_Drag_IncreasesHeight ()
  214. {
  215. // Arrange
  216. using IApplication app = Application.Create ();
  217. app.Init ("fake");
  218. View superView = new ()
  219. {
  220. Width = 50,
  221. Height = 50
  222. };
  223. View resizableView = new ()
  224. {
  225. X = 10,
  226. Y = 10,
  227. Width = 10,
  228. Height = 10,
  229. Arrangement = ViewArrangement.BottomResizable,
  230. BorderStyle = LineStyle.Single
  231. };
  232. superView.Add (resizableView);
  233. // Add to a runnable so the views are part of the application
  234. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  235. runnable.Add (superView);
  236. app.Begin (runnable);
  237. // Simulate mouse press on bottom border
  238. MouseEventArgs pressEvent = new ()
  239. {
  240. ScreenPosition = new (15, 19),
  241. Flags = MouseFlags.Button1Pressed
  242. };
  243. app.Mouse.RaiseMouseEvent (pressEvent);
  244. // Simulate mouse drag down
  245. MouseEventArgs dragEvent = new ()
  246. {
  247. ScreenPosition = new (15, 24),
  248. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  249. };
  250. app.Mouse.RaiseMouseEvent (dragEvent);
  251. // Assert - Height should have increased
  252. Assert.Equal (10, resizableView.Frame.X); // X unchanged
  253. Assert.Equal (10, resizableView.Frame.Y); // Y unchanged
  254. Assert.Equal (10, resizableView.Frame.Width); // Width unchanged
  255. Assert.Equal (15, resizableView.Frame.Height); // Height increased by 5
  256. app.End (app.SessionStack!.First ());
  257. runnable.Dispose ();
  258. superView.Dispose ();
  259. }
  260. [Fact]
  261. public void ResizableView_LeftResize_Drag_MovesAndResizes ()
  262. {
  263. // Arrange
  264. using IApplication app = Application.Create ();
  265. app.Init ("fake");
  266. View superView = new ()
  267. {
  268. Width = 50,
  269. Height = 50
  270. };
  271. View resizableView = new ()
  272. {
  273. X = 10,
  274. Y = 10,
  275. Width = 10,
  276. Height = 10,
  277. Arrangement = ViewArrangement.LeftResizable,
  278. BorderStyle = LineStyle.Single
  279. };
  280. superView.Add (resizableView);
  281. // Add to a runnable so the views are part of the application
  282. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  283. runnable.Add (superView);
  284. app.Begin (runnable);
  285. // Simulate mouse press on left border
  286. MouseEventArgs pressEvent = new ()
  287. {
  288. ScreenPosition = new (10, 15),
  289. Flags = MouseFlags.Button1Pressed
  290. };
  291. app.Mouse.RaiseMouseEvent (pressEvent);
  292. // Simulate mouse drag to the left
  293. MouseEventArgs dragEvent = new ()
  294. {
  295. ScreenPosition = new (7, 15),
  296. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  297. };
  298. app.Mouse.RaiseMouseEvent (dragEvent);
  299. // Assert - Should move left and resize
  300. Assert.Equal (7, resizableView.Frame.X); // X moved left by 3
  301. Assert.Equal (10, resizableView.Frame.Y); // Y unchanged
  302. Assert.Equal (13, resizableView.Frame.Width); // Width increased by 3
  303. Assert.Equal (10, resizableView.Frame.Height); // Height unchanged
  304. app.End (app.SessionStack!.First ());
  305. runnable.Dispose ();
  306. superView.Dispose ();
  307. }
  308. [Fact]
  309. public void ResizableView_TopResize_Drag_MovesAndResizes ()
  310. {
  311. // Arrange
  312. using IApplication app = Application.Create ();
  313. app.Init ("fake");
  314. View superView = new ()
  315. {
  316. Width = 50,
  317. Height = 50
  318. };
  319. View resizableView = new ()
  320. {
  321. X = 10,
  322. Y = 10,
  323. Width = 10,
  324. Height = 10,
  325. Arrangement = ViewArrangement.TopResizable,
  326. BorderStyle = LineStyle.Single
  327. };
  328. superView.Add (resizableView);
  329. // Add to a runnable so the views are part of the application
  330. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  331. runnable.Add (superView);
  332. app.Begin (runnable);
  333. // Simulate mouse press on top border
  334. MouseEventArgs pressEvent = new ()
  335. {
  336. ScreenPosition = new (15, 10),
  337. Flags = MouseFlags.Button1Pressed
  338. };
  339. app.Mouse.RaiseMouseEvent (pressEvent);
  340. // Simulate mouse drag up
  341. MouseEventArgs dragEvent = new ()
  342. {
  343. ScreenPosition = new (15, 8),
  344. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  345. };
  346. app.Mouse.RaiseMouseEvent (dragEvent);
  347. // Assert - Should move up and resize
  348. Assert.Equal (10, resizableView.Frame.X); // X unchanged
  349. Assert.Equal (8, resizableView.Frame.Y); // Y moved up by 2
  350. Assert.Equal (10, resizableView.Frame.Width); // Width unchanged
  351. Assert.Equal (12, resizableView.Frame.Height); // Height increased by 2
  352. app.End (app.SessionStack!.First ());
  353. runnable.Dispose ();
  354. superView.Dispose ();
  355. }
  356. #endregion
  357. #region Corner Resize Tests
  358. [Fact]
  359. public void ResizableView_BottomRightCornerResize_Drag_ResizesBothDimensions ()
  360. {
  361. // Arrange
  362. using IApplication app = Application.Create ();
  363. app.Init ("fake");
  364. View superView = new ()
  365. {
  366. Width = 50,
  367. Height = 50
  368. };
  369. View resizableView = new ()
  370. {
  371. X = 10,
  372. Y = 10,
  373. Width = 10,
  374. Height = 10,
  375. Arrangement = ViewArrangement.BottomResizable | ViewArrangement.RightResizable,
  376. BorderStyle = LineStyle.Single
  377. };
  378. superView.Add (resizableView);
  379. // Add to a runnable so the views are part of the application
  380. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  381. runnable.Add (superView);
  382. app.Begin (runnable);
  383. // Simulate mouse press on bottom-right corner
  384. MouseEventArgs pressEvent = new ()
  385. {
  386. ScreenPosition = new (19, 19),
  387. Flags = MouseFlags.Button1Pressed
  388. };
  389. app.Mouse.RaiseMouseEvent (pressEvent);
  390. // Simulate mouse drag diagonally
  391. MouseEventArgs dragEvent = new ()
  392. {
  393. ScreenPosition = new (24, 24),
  394. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  395. };
  396. app.Mouse.RaiseMouseEvent (dragEvent);
  397. // Assert - Both dimensions should increase
  398. Assert.Equal (10, resizableView.Frame.X); // X unchanged
  399. Assert.Equal (10, resizableView.Frame.Y); // Y unchanged
  400. Assert.Equal (15, resizableView.Frame.Width); // Width increased by 5
  401. Assert.Equal (15, resizableView.Frame.Height); // Height increased by 5
  402. app.End (app.SessionStack!.First ());
  403. runnable.Dispose ();
  404. superView.Dispose ();
  405. }
  406. [Fact]
  407. public void ResizableView_TopLeftCornerResize_Drag_MovesAndResizes ()
  408. {
  409. // Arrange
  410. using IApplication app = Application.Create ();
  411. app.Init ("fake");
  412. View superView = new ()
  413. {
  414. Width = 50,
  415. Height = 50
  416. };
  417. View resizableView = new ()
  418. {
  419. X = 10,
  420. Y = 10,
  421. Width = 10,
  422. Height = 10,
  423. Arrangement = ViewArrangement.TopResizable | ViewArrangement.LeftResizable,
  424. BorderStyle = LineStyle.Single
  425. };
  426. superView.Add (resizableView);
  427. // Add to a runnable so the views are part of the application
  428. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  429. runnable.Add (superView);
  430. app.Begin (runnable);
  431. // Simulate mouse press on top-left corner
  432. MouseEventArgs pressEvent = new ()
  433. {
  434. ScreenPosition = new (10, 10),
  435. Flags = MouseFlags.Button1Pressed
  436. };
  437. app.Mouse.RaiseMouseEvent (pressEvent);
  438. // Simulate mouse drag diagonally up and left
  439. MouseEventArgs dragEvent = new ()
  440. {
  441. ScreenPosition = new (7, 8),
  442. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  443. };
  444. app.Mouse.RaiseMouseEvent (dragEvent);
  445. // Assert - Should move and resize
  446. Assert.Equal (7, resizableView.Frame.X); // X moved left by 3
  447. Assert.Equal (8, resizableView.Frame.Y); // Y moved up by 2
  448. Assert.Equal (13, resizableView.Frame.Width); // Width increased by 3
  449. Assert.Equal (12, resizableView.Frame.Height); // Height increased by 2
  450. app.End (app.SessionStack!.First ());
  451. runnable.Dispose ();
  452. superView.Dispose ();
  453. }
  454. #endregion
  455. #region Minimum Size Constraints
  456. [Fact]
  457. public void ResizableView_Drag_RespectsMinimumWidth ()
  458. {
  459. // Arrange
  460. using IApplication app = Application.Create ();
  461. app.Init ("fake");
  462. View superView = new ()
  463. {
  464. Width = 50,
  465. Height = 50
  466. };
  467. View resizableView = new ()
  468. {
  469. X = 10,
  470. Y = 10,
  471. Width = 10,
  472. Height = 10,
  473. Arrangement = ViewArrangement.LeftResizable,
  474. BorderStyle = LineStyle.Single
  475. };
  476. superView.Add (resizableView);
  477. // Add to a runnable so the views are part of the application
  478. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  479. runnable.Add (superView);
  480. app.Begin (runnable);
  481. // Try to drag far to the right (making width very small)
  482. MouseEventArgs dragEvent = new ()
  483. {
  484. Position = new (8, 5), // Drag 8 units right, would make width 2
  485. ScreenPosition = new (18, 15),
  486. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  487. };
  488. // Act
  489. resizableView.Border!.HandleDragOperation (dragEvent);
  490. // Assert - Width should be constrained to minimum
  491. // Minimum width = border thickness + margin right
  492. int expectedMinWidth = resizableView.Border!.Thickness.Horizontal + resizableView.Margin!.Thickness.Right;
  493. Assert.True (resizableView.Frame.Width >= expectedMinWidth);
  494. app.End (app.SessionStack!.First ());
  495. runnable.Dispose ();
  496. superView.Dispose ();
  497. }
  498. [Fact]
  499. public void ResizableView_Drag_RespectsMinimumHeight ()
  500. {
  501. // Arrange
  502. using IApplication app = Application.Create ();
  503. app.Init ("fake");
  504. View superView = new ()
  505. {
  506. Width = 50,
  507. Height = 50
  508. };
  509. View resizableView = new ()
  510. {
  511. X = 10,
  512. Y = 10,
  513. Width = 10,
  514. Height = 10,
  515. Arrangement = ViewArrangement.TopResizable,
  516. BorderStyle = LineStyle.Single
  517. };
  518. superView.Add (resizableView);
  519. // Add to a runnable so the views are part of the application
  520. var runnable = new Runnable { App = app, Frame = new (0, 0, 80, 25) };
  521. runnable.Add (superView);
  522. app.Begin (runnable);
  523. // Try to drag far down (making height very small)
  524. MouseEventArgs dragEvent = new ()
  525. {
  526. Position = new (5, 8), // Drag 8 units down, would make height 2
  527. ScreenPosition = new (15, 18),
  528. Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
  529. };
  530. // Act
  531. resizableView.Border!.HandleDragOperation (dragEvent);
  532. // Assert - Height should be constrained to minimum
  533. int expectedMinHeight = resizableView.Border!.Thickness.Vertical + resizableView.Margin!.Thickness.Bottom;
  534. Assert.True (resizableView.Frame.Height >= expectedMinHeight);
  535. app.End (app.SessionStack!.First ());
  536. runnable.Dispose ();
  537. superView.Dispose ();
  538. }
  539. #endregion
  540. }