CalendarTests.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. using Xunit;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Avalonia.UnitTests;
  7. namespace Avalonia.Controls.UnitTests
  8. {
  9. public class CalendarTests : ScopedTestBase
  10. {
  11. private static bool CompareDates(DateTime first, DateTime second)
  12. {
  13. return first.Year == second.Year &&
  14. first.Month == second.Month &&
  15. first.Day == second.Day;
  16. }
  17. [Fact]
  18. public void SelectedDatesChanged_Should_Fire_When_SelectedDate_Set()
  19. {
  20. bool handled = false;
  21. Calendar calendar = new Calendar();
  22. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  23. calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(delegate
  24. {
  25. handled = true;
  26. });
  27. DateTime value = new DateTime(2000, 10, 10);
  28. calendar.SelectedDate = value;
  29. Assert.True(handled);
  30. }
  31. [Fact]
  32. public void DisplayDateChanged_Should_Fire_When_DisplayDate_Set()
  33. {
  34. bool handled = false;
  35. Calendar calendar = new Calendar();
  36. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  37. calendar.DisplayDateChanged += new EventHandler<CalendarDateChangedEventArgs>(delegate
  38. {
  39. handled = true;
  40. });
  41. DateTime value = new DateTime(2000, 10, 10);
  42. calendar.DisplayDate = value;
  43. Assert.True(handled);
  44. }
  45. [Fact]
  46. public void Setting_Selected_Date_To_Blackout_Date_Should_Throw()
  47. {
  48. Calendar calendar = new Calendar();
  49. calendar.BlackoutDates.AddDatesInPast();
  50. Assert.ThrowsAny<ArgumentOutOfRangeException>(
  51. () => calendar.SelectedDate = DateTime.Today.AddDays(-1));
  52. }
  53. [Fact]
  54. public void Setting_Selected_Date_To_Blackout_Date_Should_Throw_Range()
  55. {
  56. Calendar calendar = new Calendar();
  57. calendar.BlackoutDates.Add(new CalendarDateRange(DateTime.Today, DateTime.Today.AddDays(10)));
  58. calendar.SelectedDate = DateTime.Today.AddDays(-1);
  59. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(-1)));
  60. Assert.True(CompareDates(calendar.SelectedDate.Value, calendar.SelectedDates[0]));
  61. calendar.SelectedDate = DateTime.Today.AddDays(11);
  62. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(11)));
  63. Assert.True(CompareDates(calendar.SelectedDate.Value, calendar.SelectedDates[0]));
  64. Assert.ThrowsAny<ArgumentOutOfRangeException>(
  65. () => calendar.SelectedDate = DateTime.Today.AddDays(5));
  66. }
  67. [Fact]
  68. public void Adding_Blackout_Dates_Containing_Selected_Date_Should_Throw()
  69. {
  70. Calendar calendar = new Calendar();
  71. calendar.SelectedDate = DateTime.Today.AddDays(5);
  72. Assert.ThrowsAny<ArgumentOutOfRangeException>(
  73. () => calendar.BlackoutDates.Add(new CalendarDateRange(DateTime.Today, DateTime.Today.AddDays(10))));
  74. }
  75. [Fact]
  76. public void DisplayDateStartEnd_Should_Constrain_Display_Date()
  77. {
  78. Calendar calendar = new Calendar();
  79. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  80. calendar.DisplayDateStart = new DateTime(2005, 12, 30);
  81. DateTime value = new DateTime(2005, 12, 15);
  82. calendar.DisplayDate = value;
  83. Assert.True(CompareDates(calendar.DisplayDate, calendar.DisplayDateStart.Value));
  84. value = new DateTime(2005, 12, 30);
  85. calendar.DisplayDate = value;
  86. Assert.True(CompareDates(calendar.DisplayDate, value));
  87. value = DateTime.MaxValue;
  88. calendar.DisplayDate = value;
  89. Assert.True(CompareDates(calendar.DisplayDate, value));
  90. calendar.DisplayDateEnd = new DateTime(2010, 12, 30);
  91. Assert.True(CompareDates(calendar.DisplayDate, calendar.DisplayDateEnd.Value));
  92. }
  93. [Fact]
  94. public void Setting_DisplayDateEnd_Should_Alter_DispalyDate_And_DisplayDateStart()
  95. {
  96. Calendar calendar = new Calendar();
  97. DateTime value = new DateTime(2000, 1, 30);
  98. calendar.DisplayDate = value;
  99. calendar.DisplayDateEnd = value;
  100. calendar.DisplayDateStart = value;
  101. Assert.True(CompareDates(calendar.DisplayDateStart.Value, value));
  102. Assert.True(CompareDates(calendar.DisplayDateEnd.Value, value));
  103. value = value.AddMonths(2);
  104. calendar.DisplayDateStart = value;
  105. Assert.True(CompareDates(calendar.DisplayDateStart.Value, value));
  106. Assert.True(CompareDates(calendar.DisplayDateEnd.Value, value));
  107. Assert.True(CompareDates(calendar.DisplayDate, value));
  108. }
  109. [Fact]
  110. public void Display_Date_Range_End_Will_Contain_SelectedDate()
  111. {
  112. Calendar calendar = new Calendar();
  113. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  114. calendar.SelectedDate = DateTime.MaxValue;
  115. Assert.True(CompareDates((DateTime)calendar.SelectedDate, DateTime.MaxValue));
  116. calendar.DisplayDateEnd = DateTime.MaxValue.AddDays(-1);
  117. Assert.True(CompareDates((DateTime)calendar.DisplayDateEnd, DateTime.MaxValue));
  118. }
  119. /// <summary>
  120. /// The days added to the SelectedDates collection.
  121. /// </summary>
  122. private IList<object> _selectedDatesChangedAddedDays;
  123. /// <summary>
  124. /// The days removed from the SelectedDates collection.
  125. /// </summary>
  126. private IList<object> _selectedDateChangedRemovedDays;
  127. /// <summary>
  128. /// The number of times the SelectedDatesChanged event has been fired.
  129. /// </summary>
  130. private int _selectedDatesChangedCount;
  131. /// <summary>
  132. /// Handle the SelectedDatesChanged event.
  133. /// </summary>
  134. /// <param name="sender">The calendar.</param>
  135. /// <param name="e">Event arguments.</param>
  136. private void OnSelectedDatesChanged(object sender, SelectionChangedEventArgs e)
  137. {
  138. _selectedDatesChangedAddedDays =
  139. e.AddedItems
  140. .Cast<object>()
  141. .ToList();
  142. _selectedDateChangedRemovedDays =
  143. e.RemovedItems
  144. .Cast<object>()
  145. .ToList();
  146. _selectedDatesChangedCount++;
  147. }
  148. /// <summary>
  149. /// Clear the variables used to track the SelectedDatesChanged event.
  150. /// </summary>
  151. private void ResetSelectedDatesChanged()
  152. {
  153. if (_selectedDatesChangedAddedDays != null)
  154. {
  155. _selectedDatesChangedAddedDays.Clear();
  156. }
  157. if (_selectedDateChangedRemovedDays != null)
  158. {
  159. _selectedDateChangedRemovedDays.Clear();
  160. }
  161. _selectedDatesChangedCount = 0;
  162. }
  163. [Fact]
  164. public void SingleDate_Selection_Behavior()
  165. {
  166. ResetSelectedDatesChanged();
  167. Calendar calendar = new Calendar();
  168. calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(OnSelectedDatesChanged);
  169. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  170. calendar.SelectedDate = DateTime.Today;
  171. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
  172. Assert.True(calendar.SelectedDates.Count == 1);
  173. Assert.True(CompareDates(calendar.SelectedDates[0], DateTime.Today));
  174. Assert.True(_selectedDatesChangedCount == 1);
  175. Assert.True(_selectedDatesChangedAddedDays.Count == 1);
  176. Assert.True(_selectedDateChangedRemovedDays.Count == 0);
  177. ResetSelectedDatesChanged();
  178. calendar.SelectedDate = DateTime.Today;
  179. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
  180. Assert.True(calendar.SelectedDates.Count == 1);
  181. Assert.True(CompareDates(calendar.SelectedDates[0], DateTime.Today));
  182. Assert.True(_selectedDatesChangedCount == 0);
  183. calendar.ClearValue(Calendar.SelectedDateProperty);
  184. calendar.SelectionMode = CalendarSelectionMode.None;
  185. Assert.True(calendar.SelectedDates.Count == 0);
  186. Assert.Null(calendar.SelectedDate);
  187. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  188. calendar.SelectedDates.Add(DateTime.Today.AddDays(1));
  189. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(1)));
  190. Assert.True(calendar.SelectedDates.Count == 1);
  191. Assert.ThrowsAny<InvalidOperationException>(
  192. () => calendar.SelectedDates.Add(DateTime.Today.AddDays(2)));
  193. }
  194. [Fact]
  195. public void SingleRange_Selection_Behavior()
  196. {
  197. ResetSelectedDatesChanged();
  198. Calendar calendar = new Calendar();
  199. calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(OnSelectedDatesChanged);
  200. calendar.SelectionMode = CalendarSelectionMode.SingleRange;
  201. calendar.SelectedDate = DateTime.Today;
  202. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
  203. Assert.True(calendar.SelectedDates.Count == 1);
  204. Assert.True(CompareDates(calendar.SelectedDates[0], DateTime.Today));
  205. Assert.True(_selectedDatesChangedCount == 1);
  206. Assert.True(_selectedDatesChangedAddedDays.Count == 1);
  207. Assert.True(_selectedDateChangedRemovedDays.Count == 0);
  208. ResetSelectedDatesChanged();
  209. calendar.SelectedDates.Clear();
  210. Assert.Null(calendar.SelectedDate);
  211. ResetSelectedDatesChanged();
  212. calendar.SelectedDates.AddRange(DateTime.Today, DateTime.Today.AddDays(10));
  213. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today));
  214. Assert.True(calendar.SelectedDates.Count == 11);
  215. ResetSelectedDatesChanged();
  216. calendar.SelectedDates.AddRange(DateTime.Today, DateTime.Today.AddDays(10));
  217. Assert.True(calendar.SelectedDates.Count == 11);
  218. Assert.True(_selectedDatesChangedCount == 0);
  219. ResetSelectedDatesChanged();
  220. calendar.SelectedDates.AddRange(DateTime.Today.AddDays(-20), DateTime.Today);
  221. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(-20)));
  222. Assert.True(calendar.SelectedDates.Count == 21);
  223. Assert.True(_selectedDatesChangedCount == 1);
  224. Assert.True(_selectedDatesChangedAddedDays.Count == 21);
  225. Assert.True(_selectedDateChangedRemovedDays.Count == 11);
  226. ResetSelectedDatesChanged();
  227. calendar.SelectedDates.Add(DateTime.Today.AddDays(100));
  228. Assert.True(CompareDates(calendar.SelectedDate.Value, DateTime.Today.AddDays(100)));
  229. Assert.True(calendar.SelectedDates.Count == 1);
  230. }
  231. [Fact]
  232. public void AllowTapRangeSelection_Should_Disable_TapToSelectRange()
  233. {
  234. var calendar = new Calendar();
  235. Assert.True(calendar.AllowTapRangeSelection); // Default should be true
  236. calendar.AllowTapRangeSelection = false;
  237. Assert.False(calendar.AllowTapRangeSelection);
  238. }
  239. [Fact]
  240. public void TapRangeSelection_Should_Work_In_SingleRange_Mode()
  241. {
  242. var calendar = new Calendar();
  243. calendar.SelectionMode = CalendarSelectionMode.SingleRange;
  244. calendar.AllowTapRangeSelection = true;
  245. var startDate = new DateTime(2023, 10, 10);
  246. var endDate = new DateTime(2023, 10, 15);
  247. // First tap should select start date
  248. var firstTapResult = calendar.ProcessTapRangeSelection(startDate);
  249. Assert.True(firstTapResult);
  250. Assert.Equal(1, calendar.SelectedDates.Count);
  251. Assert.True(calendar.SelectedDates.Contains(startDate));
  252. // Second tap should complete the range
  253. var secondTapResult = calendar.ProcessTapRangeSelection(endDate);
  254. Assert.True(secondTapResult);
  255. Assert.Equal(6, calendar.SelectedDates.Count); // 5 days inclusive
  256. Assert.True(calendar.SelectedDates.Contains(startDate));
  257. Assert.True(calendar.SelectedDates.Contains(endDate));
  258. }
  259. [Fact]
  260. public void TapRangeSelection_Should_Not_Work_In_SingleDate_Mode()
  261. {
  262. var calendar = new Calendar();
  263. calendar.SelectionMode = CalendarSelectionMode.SingleDate;
  264. calendar.AllowTapRangeSelection = true;
  265. var date = new DateTime(2023, 10, 10);
  266. var result = calendar.ProcessTapRangeSelection(date);
  267. Assert.False(result); // Should not handle tap range selection
  268. }
  269. [Fact]
  270. public void TapRangeSelection_Should_Handle_Blackout_Dates()
  271. {
  272. var calendar = new Calendar();
  273. calendar.SelectionMode = CalendarSelectionMode.SingleRange;
  274. calendar.AllowTapRangeSelection = true;
  275. var startDate = new DateTime(2023, 10, 10);
  276. var blackoutDate = new DateTime(2023, 10, 12);
  277. var endDate = new DateTime(2023, 10, 15);
  278. // Add blackout date in the middle
  279. calendar.BlackoutDates.Add(new CalendarDateRange(blackoutDate, blackoutDate));
  280. // First tap
  281. calendar.ProcessTapRangeSelection(startDate);
  282. Assert.Equal(1, calendar.SelectedDates.Count);
  283. // Second tap should restart selection due to blackout date
  284. calendar.ProcessTapRangeSelection(endDate);
  285. Assert.Equal(1, calendar.SelectedDates.Count);
  286. Assert.True(calendar.SelectedDates.Contains(endDate));
  287. Assert.False(calendar.SelectedDates.Contains(startDate));
  288. }
  289. [Fact]
  290. public void TapRangeSelection_Should_Handle_Reverse_Order_Dates()
  291. {
  292. var calendar = new Calendar();
  293. calendar.SelectionMode = CalendarSelectionMode.SingleRange;
  294. calendar.AllowTapRangeSelection = true;
  295. var laterDate = new DateTime(2023, 10, 15);
  296. var earlierDate = new DateTime(2023, 10, 10);
  297. // First tap on later date
  298. calendar.ProcessTapRangeSelection(laterDate);
  299. Assert.Equal(1, calendar.SelectedDates.Count);
  300. // Second tap on earlier date should still create correct range
  301. calendar.ProcessTapRangeSelection(earlierDate);
  302. Assert.Equal(6, calendar.SelectedDates.Count);
  303. Assert.True(calendar.SelectedDates.Contains(earlierDate));
  304. Assert.True(calendar.SelectedDates.Contains(laterDate));
  305. }
  306. }
  307. }