123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- 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 TimePickerTests : ScopedTestBase
- {
- [Fact]
- public void SelectedTimeChanged_Should_Fire_When_SelectedTime_Set()
- {
- using (UnitTestApplication.Start(Services))
- {
- bool handled = false;
- TimePicker timePicker = new TimePicker();
- timePicker.SelectedTimeChanged += (s, e) =>
- {
- handled = true;
- };
- TimeSpan value = TimeSpan.FromHours(10);
- timePicker.SelectedTime = value;
- Threading.Dispatcher.UIThread.RunJobs();
- Assert.True(handled);
- }
- }
- [Fact]
- public void Using_24HourClock_Should_Hide_Period()
- {
- using (UnitTestApplication.Start(Services))
- {
- TimePicker timePicker = new TimePicker()
- {
- ClockIdentifier = "12HourClock",
- Template = CreateTemplate()
- };
- timePicker.ApplyTemplate();
- var desc = timePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- var periodTextHost = container.Children[6] as Border;
- Assert.True(periodTextHost != null);
- Assert.True(periodTextHost.IsVisible);
- timePicker.ClockIdentifier = "24HourClock";
- Assert.False(periodTextHost.IsVisible);
- }
- }
-
- [Fact]
- public void UseSeconds_Equals_False_Should_Hide_Seconds()
- {
- using (UnitTestApplication.Start(Services))
- {
- TimePicker timePicker = new TimePicker()
- {
- UseSeconds = true,
- Template = CreateTemplate()
- };
- timePicker.ApplyTemplate();
- var desc = timePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- var periodTextHost = container.Children[4] as Border;
- Assert.True(periodTextHost != null);
- Assert.True(periodTextHost.IsVisible);
- timePicker.UseSeconds = false;
- Assert.False(periodTextHost.IsVisible);
- }
- }
- [Fact]
- public void UseSeconds_Equals_False_Should_Have_Zero_Seconds()
- {
- using (UnitTestApplication.Start(Services))
- {
- TimePicker timePicker = new TimePicker()
- {
- UseSeconds = false,
- Template = CreateTemplate(includePopup: true)
- };
- timePicker.ApplyTemplate();
- var desc = timePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 2);
- // find button
- Assert.True(desc.ElementAt(1) is Button);
- var btn = (Button)desc.ElementAt(1);
- Assert.True(desc.ElementAt(2) is Popup);
- var popup = (Popup)desc.ElementAt(2);
- Assert.True(popup.Child is TimePickerPresenter);
- var timePickerPresenter = (TimePickerPresenter)popup.Child;
- var panel = (Panel)timePickerPresenter.VisualChildren[0];
- var acceptBtn = (Button)panel.VisualChildren[0];
- Assert.False(popup.IsOpen);
- btn.PerformClick();
- Assert.True(popup.IsOpen);
- Assert.False(timePickerPresenter.UseSeconds);
- acceptBtn.PerformClick();
- Assert.Equal(0, timePickerPresenter.Time.Seconds);
- Assert.Equal(0, timePicker.SelectedTime?.Seconds);
- }
- }
- [Fact]
- public void TimePickerPresenter_UseSeconds_Equals_False_Should_Have_Zero_Seconds()
- {
- using (UnitTestApplication.Start(Services))
- {
- TimePickerPresenter timePickerPresenter = new TimePickerPresenter()
- {
- UseSeconds = false,
- Template = CreatePickerTemplate(),
- };
- timePickerPresenter.ApplyTemplate();
- var panel = (Panel)timePickerPresenter.VisualChildren[0];
- var acceptBtn = (Button)panel.VisualChildren[0];
- acceptBtn.PerformClick();
- Assert.Equal(0, timePickerPresenter.Time.Seconds);
- }
- }
- [Fact]
- public void SelectedTime_null_Should_Use_Placeholders()
- {
- using (UnitTestApplication.Start(Services))
- {
- TimePicker timePicker = new TimePicker()
- {
- Template = CreateTemplate()
- };
- timePicker.ApplyTemplate();
- var desc = timePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1);//Should be layoutroot grid & button
- Grid container = null;
- Assert.True(desc.ElementAt(1) is Button);
- container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- var hourTextHost = container.Children[0] as Border;
- Assert.True(hourTextHost != null);
- var hourText = hourTextHost.Child as TextBlock;
- var minuteTextHost = container.Children[2] as Border;
- Assert.True(minuteTextHost != null);
- var minuteText = minuteTextHost.Child as TextBlock;
- var secondTextHost = container.Children[4] as Border;
- Assert.True(secondTextHost != null);
- var secondText = secondTextHost.Child as TextBlock;
- TimeSpan ts = TimeSpan.FromHours(10);
- timePicker.SelectedTime = ts;
- Assert.NotNull(hourText.Text);
- Assert.NotNull(minuteText.Text);
- Assert.NotNull(secondText.Text);
- timePicker.SelectedTime = null;
- Assert.Null(hourText.Text);
- Assert.Null(minuteText.Text);
- Assert.Null(secondText.Text);
- }
- }
-
- [Fact]
- [UseEmptyDesignatorCulture]
- public void Using_12HourClock_On_Culture_With_Empty_Period_Should_Show_Period()
- {
- using (UnitTestApplication.Start(Services))
- {
- TimePicker timePicker = new TimePicker()
- {
- Template = CreateTemplate(), ClockIdentifier = "12HourClock",
- };
- timePicker.ApplyTemplate();
- var desc = timePicker.GetVisualDescendants();
- Assert.True(desc.Count() > 1); //Should be layoutroot grid & button
- Assert.True(desc.ElementAt(1) is Button);
- var container = (desc.ElementAt(1) as Button).Content as Grid;
- Assert.True(container != null);
- var periodTextHost = container.Children[6] as Border;
- Assert.NotNull(periodTextHost);
- var periodText = periodTextHost.Child as TextBlock;
- Assert.NotNull(periodTextHost);
- TimeSpan ts = TimeSpan.FromHours(10);
- timePicker.SelectedTime = ts;
- Assert.False(string.IsNullOrEmpty(periodText.Text));
- timePicker.SelectedTime = null;
- Assert.False(string.IsNullOrEmpty(periodText.Text));
- }
- }
- [Fact]
- public void SelectedTime_EnableDataValidation()
- {
- using (UnitTestApplication.Start(Services))
- {
- var handled = false;
- var timePicker = new TimePicker();
- timePicker.SelectedTimeChanged += (s, e) =>
- {
- var minTime = new TimeSpan(10, 0, 0);
- var maxTime = new TimeSpan(15, 0, 0);
- if (e.NewTime < minTime)
- throw new DataValidationException($"time is less than {maxTime}");
- if (e.NewTime > maxTime)
- throw new DataValidationException($"time is over {maxTime}");
- handled = true;
- };
- // time is less than
- Assert.Throws<DataValidationException>(() => timePicker.SelectedTime = new TimeSpan(1, 2, 3));
- // time is over
- Assert.Throws<DataValidationException>(() => timePicker.SelectedTime = new TimeSpan(21, 22, 23));
- var exception = new DataValidationException("failed validation");
- var observable =
- new BehaviorSubject<BindingNotification>(new BindingNotification(exception,
- BindingErrorType.DataValidationError));
- timePicker.Bind(TimePicker.SelectedTimeProperty, observable);
- Assert.True(DataValidationErrors.GetHasErrors(timePicker));
- Dispatcher.UIThread.RunJobs();
- timePicker.SelectedTime = new TimeSpan(11, 12, 13);
- Assert.True(handled);
- }
- }
- [Theory]
- [InlineData("PART_HourSelector")]
- [InlineData("PART_MinuteSelector")]
- [InlineData("PART_SecondSelector")]
- public void Selector_ScrollUp_Should_Work(string selectorName)
- => TestSelectorScrolling(selectorName, panel => panel.ScrollUp());
- [Theory]
- [InlineData("PART_HourSelector")]
- [InlineData("PART_MinuteSelector")]
- [InlineData("PART_SecondSelector")]
- 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 TimePickerPresenter { 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(bool includePopup = false)
- {
- 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_FlyoutButtonContentGrid"
- }.RegisterInNameScope(scope);
- var firstPickerHost = new Border
- {
- Name = "PART_FirstPickerHost",
- Child = new TextBlock
- {
- Name = "PART_HourTextBlock"
- }.RegisterInNameScope(scope)
- }.RegisterInNameScope(scope);
- Grid.SetColumn(firstPickerHost, 0);
- var secondPickerHost = new Border
- {
- Name = "PART_SecondPickerHost",
- Child = new TextBlock
- {
- Name = "PART_MinuteTextBlock"
- }.RegisterInNameScope(scope)
- }.RegisterInNameScope(scope);
- Grid.SetColumn(secondPickerHost, 2);
- var thirdPickerHost = new Border
- {
- Name = "PART_ThirdPickerHost",
- Child = new TextBlock
- {
- Name = "PART_SecondTextBlock"
- }.RegisterInNameScope(scope)
- }.RegisterInNameScope(scope);
- Grid.SetColumn(thirdPickerHost, 4);
-
- var fourthPickerHost = new Border
- {
- Name = "PART_FourthPickerHost",
- Child = new TextBlock
- {
- Name = "PART_PeriodTextBlock"
- }.RegisterInNameScope(scope)
- }.RegisterInNameScope(scope);
- Grid.SetColumn(fourthPickerHost, 6);
- var firstSpacer = new Rectangle
- {
- Name = "PART_FirstColumnDivider"
- }.RegisterInNameScope(scope);
- Grid.SetColumn(firstSpacer, 1);
- var secondSpacer = new Rectangle
- {
- Name = "PART_SecondColumnDivider"
- }.RegisterInNameScope(scope);
- Grid.SetColumn(secondSpacer, 3);
- var thirdSpacer = new Rectangle
- {
- Name = "PART_ThirdColumnDivider"
- }.RegisterInNameScope(scope);
- Grid.SetColumn(thirdSpacer, 5);
- contentGrid.Children.AddRange(new Control[] { firstPickerHost, firstSpacer, secondPickerHost, secondSpacer, thirdPickerHost, thirdSpacer, fourthPickerHost });
- flyoutButton.Content = contentGrid;
- layoutRoot.Children.Add(flyoutButton);
- if (includePopup)
- {
- var popup = new Popup
- {
- Name = "PART_Popup"
- }.RegisterInNameScope(scope);
- var pickerPresenter = new TimePickerPresenter
- {
- Name = "PART_PickerPresenter",
- Template = CreatePickerTemplate()
- }.RegisterInNameScope(scope);
- pickerPresenter.ApplyTemplate();
- popup.Child = pickerPresenter;
- layoutRoot.Children.Add(popup);
- }
- return layoutRoot;
- });
- }
- private static IControlTemplate CreatePickerTemplate()
- {
- return new FuncControlTemplate((control, scope) =>
- {
- var acceptButton = new Button
- {
- Name = "PART_AcceptButton"
- }.RegisterInNameScope(scope);
- var hourSelector = new DateTimePickerPanel
- {
- Name = "PART_HourSelector",
- PanelType = DateTimePickerPanelType.Hour,
- ShouldLoop = true
- }.RegisterInNameScope(scope);
- var minuteSelector = new DateTimePickerPanel
- {
- Name = "PART_MinuteSelector",
- PanelType = DateTimePickerPanelType.Minute,
- ShouldLoop = true
- }.RegisterInNameScope(scope);
- var secondHost = new Panel
- {
- Name = "PART_SecondHost"
- }.RegisterInNameScope(scope);
- var secondSelector = new DateTimePickerPanel
- {
- Name = "PART_SecondSelector",
- PanelType = DateTimePickerPanelType.Second,
- ShouldLoop = true
- }.RegisterInNameScope(scope);
- var periodHost = new Panel
- {
- Name = "PART_PeriodHost"
- }.RegisterInNameScope(scope);
- var periodSelector = new DateTimePickerPanel
- {
- Name = "PART_PeriodSelector",
- PanelType = DateTimePickerPanelType.TimePeriod,
- }.RegisterInNameScope(scope);
- var pickerContainer = new Grid
- {
- Name = "PART_PickerContainer"
- }.RegisterInNameScope(scope);
- var secondSpacer = new Rectangle
- {
- Name = "PART_SecondSpacer"
- }.RegisterInNameScope(scope);
- var thirdSpacer = new Rectangle
- {
- Name = "PART_ThirdSpacer"
- }.RegisterInNameScope(scope);
- var contentPanel = new Panel();
- contentPanel.Children.AddRange(new Control[] { acceptButton, hourSelector, minuteSelector, secondHost, secondSelector, periodHost, periodSelector, pickerContainer, secondSpacer, thirdSpacer });
- return contentPanel;
- });
- }
- }
- }
|