123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- using System;
- using System.Linq;
- using System.Reactive.Subjects;
- using Avalonia.Controls.Primitives;
- using Avalonia.Controls.Shapes;
- using Avalonia.Controls.Templates;
- using Avalonia.Data;
- using Avalonia.Headless;
- using Avalonia.Platform;
- using Avalonia.Threading;
- using Avalonia.UnitTests;
- using Avalonia.VisualTree;
- using Moq;
- using Xunit;
- namespace Avalonia.Controls.UnitTests
- {
- public class DatePickerTests : ScopedTestBase
- {
- [Fact]
- public void SelectedDateChanged_Should_Fire_When_SelectedDate_Set()
- {
- using (UnitTestApplication.Start(Services))
- {
- bool handled = false;
- DatePicker datePicker = new DatePicker();
- datePicker.SelectedDateChanged += (s, e) =>
- {
- handled = true;
- };
- DateTimeOffset value = new DateTimeOffset(2000, 10, 10, 0, 0, 0, TimeSpan.Zero);
- datePicker.SelectedDate = value;
- Threading.Dispatcher.UIThread.RunJobs();
- Assert.True(handled);
- }
- }
- [Fact]
- public void DayVisible_False_Should_Hide_Day()
- {
- using (UnitTestApplication.Start(Services))
- {
- DatePicker datePicker = new DatePicker
- {
- Template = CreateTemplate(),
- DayVisible = false
- };
- datePicker.ApplyTemplate();
- Threading.Dispatcher.UIThread.RunJobs();
- var desc = datePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- TextBlock dayText = null;
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- for(int i = 0; i < container.Children.Count; i++)
- {
- if(container.Children[i] is TextBlock tb && tb.Name == "PART_DayTextBlock")
- {
- dayText = tb;
- break;
- }
- }
- Assert.True(dayText != null);
- Assert.True(!dayText.IsVisible);
- Assert.True(container.ColumnDefinitions.Count == 3);
- }
- }
- [Fact]
- public void MonthVisible_False_Should_Hide_Month()
- {
- using (UnitTestApplication.Start(Services))
- {
- DatePicker datePicker = new DatePicker
- {
- Template = CreateTemplate(),
- MonthVisible = false
- };
- datePicker.ApplyTemplate();
- Threading.Dispatcher.UIThread.RunJobs();
- var desc = datePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- TextBlock monthText = null;
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- for (int i = 0; i < container.Children.Count; i++)
- {
- if (container.Children[i] is TextBlock tb && tb.Name == "PART_MonthTextBlock")
- {
- monthText = tb;
- break;
- }
- }
- Assert.True(monthText != null);
- Assert.True(!monthText.IsVisible);
- Assert.True(container.ColumnDefinitions.Count == 3);
- }
- }
- [Fact]
- public void YearVisible_False_Should_Hide_Year()
- {
- using (UnitTestApplication.Start(Services))
- {
- DatePicker datePicker = new DatePicker
- {
- Template = CreateTemplate(),
- YearVisible = false
- };
- datePicker.ApplyTemplate();
- Threading.Dispatcher.UIThread.RunJobs();
- var desc = datePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- TextBlock yearText = null;
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- for (int i = 0; i < container.Children.Count; i++)
- {
- if (container.Children[i] is TextBlock tb && tb.Name == "PART_YearTextBlock")
- {
- yearText = tb;
- break;
- }
- }
- Assert.True(yearText != null);
- Assert.True(!yearText.IsVisible);
- Assert.True(container.ColumnDefinitions.Count == 3);
- }
- }
- [Fact]
- public void SelectedDate_null_Should_Use_Placeholders()
- {
- using (UnitTestApplication.Start(Services))
- {
- DatePicker datePicker = new DatePicker
- {
- Template = CreateTemplate(),
- YearVisible = false
- };
- datePicker.ApplyTemplate();
- Threading.Dispatcher.UIThread.RunJobs();
- var desc = datePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- TextBlock yearText = null;
- TextBlock monthText = null;
- TextBlock dayText = null;
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- for (int i = 0; i < container.Children.Count; i++)
- {
- if (container.Children[i] is TextBlock tb && tb.Name == "PART_YearTextBlock")
- {
- yearText = tb;
- }
- else if (container.Children[i] is TextBlock tb1 && tb1.Name == "PART_MonthTextBlock")
- {
- monthText = tb1;
- }
- else if (container.Children[i] is TextBlock tb2 && tb2.Name == "PART_DayTextBlock")
- {
- dayText = tb2;
- }
- }
- DateTimeOffset value = new DateTimeOffset(2000, 10, 10, 0, 0, 0, TimeSpan.Zero);
- datePicker.SelectedDate = value;
- Assert.NotNull(dayText.Text);
- Assert.NotNull(monthText.Text);
- Assert.NotNull(yearText.Text);
- Assert.False(datePicker.Classes.Contains(":hasnodate"));
- datePicker.SelectedDate = null;
- Assert.Null(dayText.Text);
- Assert.Null(monthText.Text);
- Assert.Null(yearText.Text);
- Assert.True(datePicker.Classes.Contains(":hasnodate"));
- }
- }
- [Fact]
- public void SelectedDate_EnableDataValidation()
- {
- var handled = false;
- var datePicker = new DatePicker();
- datePicker.SelectedDateChanged += (s, e) =>
- {
- var minDateTime = new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero);
- var maxDateTime = new DateTimeOffset(2010, 1, 1, 0, 0, 0, TimeSpan.Zero);
- if (e.NewDate < minDateTime)
- throw new DataValidationException($"dateTime is less than {minDateTime}");
- if (e.NewDate > maxDateTime)
- throw new DataValidationException($"dateTime is over {maxDateTime}");
- handled = true;
- };
- // dateTime is less than
- Assert.Throws<DataValidationException>(() => datePicker.SelectedDate = new DateTimeOffset(1999, 1, 1, 0, 0, 0, TimeSpan.Zero));
- // dateTime is over
- Assert.Throws<DataValidationException>(() => datePicker.SelectedDate = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero));
- var exception = new DataValidationException("failed validation");
- var observable =
- new BehaviorSubject<BindingNotification>(new BindingNotification(exception,
- BindingErrorType.DataValidationError));
- datePicker.Bind(DatePicker.SelectedDateProperty, observable);
- Assert.True(DataValidationErrors.GetHasErrors(datePicker));
- Dispatcher.UIThread.RunJobs();
- datePicker.SelectedDate = new DateTimeOffset(2005, 5, 10, 11, 12, 13, TimeSpan.Zero);
- Assert.True(handled);
- }
- [Theory]
- [InlineData("PART_DaySelector")]
- [InlineData("PART_MonthSelector")]
- [InlineData("PART_YearSelector")]
- public void Selector_ScrollUp_Should_Work(string selectorName)
- => TestSelectorScrolling(selectorName, panel => panel.ScrollUp());
- [Theory]
- [InlineData("PART_DaySelector")]
- [InlineData("PART_MonthSelector")]
- [InlineData("PART_YearSelector")]
- public void Selector_ScrollDown_Should_Work(string selectorName)
- => TestSelectorScrolling(selectorName, panel => panel.ScrollDown());
- private static void TestSelectorScrolling(string selectorName, Action<DateTimePickerPanel> scroll)
- {
- using var app = UnitTestApplication.Start(Services);
- var presenter = new DatePickerPresenter { Template = CreatePickerTemplate() };
- presenter.ApplyTemplate();
- presenter.Measure(new Size(1000, 1000));
- var panel = presenter
- .GetVisualDescendants()
- .OfType<DateTimePickerPanel>()
- .FirstOrDefault(panel => panel.Name == selectorName);
- Assert.NotNull(panel);
- var previousOffset = panel.Offset;
- scroll(panel);
- Assert.NotEqual(previousOffset, panel.Offset);
- }
- private static TestServices Services => TestServices.MockThreadingInterface.With(
- fontManagerImpl: new HeadlessFontManagerStub(),
- standardCursorFactory: Mock.Of<ICursorFactory>(),
- textShaperImpl: new HeadlessTextShaperStub(),
- renderInterface: new HeadlessPlatformRenderInterface());
- private static IControlTemplate CreateTemplate()
- {
- return new FuncControlTemplate((control, scope) =>
- {
- var layoutRoot = new Grid
- {
- Name = "LayoutRoot"
- }.RegisterInNameScope(scope);
- //Skip contentpresenter
- var flyoutButton = new Button
- {
- Name = "PART_FlyoutButton"
- }.RegisterInNameScope(scope);
- var contentGrid = new Grid
- {
- Name = "PART_ButtonContentGrid"
- }.RegisterInNameScope(scope);
- var dayText = new TextBlock
- {
- Name = "PART_DayTextBlock"
- }.RegisterInNameScope(scope);
- var monthText = new TextBlock
- {
- Name = "PART_MonthTextBlock"
- }.RegisterInNameScope(scope);
- var yearText = new TextBlock
- {
- Name = "PART_YearTextBlock"
- }.RegisterInNameScope(scope);
- var firstSpacer = new Rectangle
- {
- Name = "PART_FirstSpacer"
- }.RegisterInNameScope(scope);
- var secondSpacer = new Rectangle
- {
- Name = "PART_SecondSpacer"
- }.RegisterInNameScope(scope);
- var thirdSpacer = new Rectangle
- {
- Name = "PART_ThirdSpacer"
- }.RegisterInNameScope(scope);
-
- contentGrid.Children.AddRange(new Control[] { dayText, monthText, yearText, firstSpacer, secondSpacer, thirdSpacer });
- flyoutButton.Content = contentGrid;
- layoutRoot.Children.Add(flyoutButton);
- return layoutRoot;
- });
- }
- private static IControlTemplate CreatePickerTemplate()
- {
- return new FuncControlTemplate((_, scope) =>
- {
- var dayHost = new Panel
- {
- Name = "PART_DayHost"
- }.RegisterInNameScope(scope);
- var daySelector = new DateTimePickerPanel
- {
- Name = "PART_DaySelector",
- PanelType = DateTimePickerPanelType.Day,
- ShouldLoop = true
- }.RegisterInNameScope(scope);
- var monthHost = new Panel
- {
- Name = "PART_MonthHost"
- }.RegisterInNameScope(scope);
- var monthSelector = new DateTimePickerPanel
- {
- Name = "PART_MonthSelector",
- PanelType = DateTimePickerPanelType.Month,
- ShouldLoop = true
- }.RegisterInNameScope(scope);
- var yearHost = new Panel
- {
- Name = "PART_YearHost"
- }.RegisterInNameScope(scope);
- var yearSelector = new DateTimePickerPanel
- {
- Name = "PART_YearSelector",
- PanelType = DateTimePickerPanelType.Year,
- ShouldLoop = true
- }.RegisterInNameScope(scope);
- var acceptButton = new Button
- {
- Name = "PART_AcceptButton"
- }.RegisterInNameScope(scope);
- var pickerContainer = new Grid
- {
- Name = "PART_PickerContainer"
- }.RegisterInNameScope(scope);
- var firstSpacer = new Rectangle
- {
- Name = "PART_FirstSpacer"
- }.RegisterInNameScope(scope);
- var secondSpacer = new Rectangle
- {
- Name = "PART_SecondSpacer"
- }.RegisterInNameScope(scope);
- var contentPanel = new Panel();
- contentPanel.Children.AddRange([
- dayHost, daySelector, monthHost, monthSelector, yearHost, yearSelector,
- acceptButton, pickerContainer, firstSpacer, secondSpacer
- ]);
- return contentPanel;
- });
- }
- }
- }
|