diff --git a/.codacy.yml b/.codacy.yml
index 429f6288b5be..96a31e2510bc 100644
--- a/.codacy.yml
+++ b/.codacy.yml
@@ -4,4 +4,6 @@ exclude_paths:
- 'src/SamplesApp/**'
- '**/tsBindings/*.ts'
- 'doc/**'
- - 'src/Uno.UI/Microsoft/UI/Xaml/Controls/Repeater/**'
\ No newline at end of file
+ - 'src/Uno.UI/Microsoft/UI/Xaml/Controls/Repeater/**'
+ - 'src/Uno.UI/UI/Xaml/Controls/CalendarView/**'
+ - 'src/Uno.UI/DirectUI/**'
diff --git a/build/PackageDiffIgnore.xml b/build/PackageDiffIgnore.xml
index bc9b37f494f1..ff09c09a2fc5 100644
--- a/build/PackageDiffIgnore.xml
+++ b/build/PackageDiffIgnore.xml
@@ -3681,15 +3681,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+ ChineseLunarCalendar
+ GregorianCalendar
+ HebrewCalendar
+ HijriCalendar
+ JapaneseCalendar
+ JapaneseLunarCalendar
+ JulianCalendar
+ KoreanCalendar
+ KoreanLunarCalendar
+ PersianCalendar
+ TaiwanCalendar
+ TaiwanLunarCalendar
+ ThaiCalendar
+ UmAlQuraCalendar
+ VietnameseLunarCalendar
+
+ IsTodayHighlighted
+
+
+
+
+ IsEnabled
+
+
+ IsCalendarOpen
+
+
+ Date:
+
+
+
+
+ IsEnabled
+
+
+ IsCalendarOpen
+
+
+
+ Date:
+
+
+
+
+ IsEnabled
+
+
+ IsCalendarOpen
+
+
+ Date:
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarDatePicker_Features.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarDatePicker_Features.xaml.cs
new file mode 100644
index 000000000000..57e2c1eb9c3b
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarDatePicker_Features.xaml.cs
@@ -0,0 +1,14 @@
+using Windows.UI.Xaml.Controls;
+using Uno.UI.Samples.Controls;
+
+namespace UITests.Windows_UI_Xaml_Controls.CalendarView
+{
+ [Sample("Date Picking")]
+ public sealed partial class CalendarDatePicker_Features : Page
+ {
+ public CalendarDatePicker_Features()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Basics.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Basics.xaml
new file mode 100644
index 000000000000..f7cd276261f8
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Basics.xaml
@@ -0,0 +1,15 @@
+
+
+
+ This is a vanilla CalendarView...
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Basics.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Basics.xaml.cs
new file mode 100644
index 000000000000..7d5e3f010ef7
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Basics.xaml.cs
@@ -0,0 +1,16 @@
+using System.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Uno.UI.Samples.Controls;
+
+namespace UITests.Windows_UI_Xaml_Controls.CalendarViewTests
+{
+ [Sample("Date Picking")]
+ public sealed partial class CalendarView_Basics : Page
+ {
+ public CalendarView_Basics()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Features.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Features.xaml
new file mode 100644
index 000000000000..f9eb80c81468
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Features.xaml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Month
+ Decade
+ Year
+
+
+ Single
+ Multiple
+ None
+
+
+ Sunday
+ Monday
+ Tuesday
+ Wednesday
+ Thursday
+ Friday
+ Saturday
+
+ IsTodayHighlighted
+
+
+
+
+
+
+
+
+
+
+
+ Selected Date(s):
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Features.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Features.xaml.cs
new file mode 100644
index 000000000000..59ac6455ad30
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_Features.xaml.cs
@@ -0,0 +1,26 @@
+using System.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Uno.UI.Samples.Controls;
+
+namespace UITests.Windows_UI_Xaml_Controls.CalendarView
+{
+ [Sample("Date Picking")]
+ public sealed partial class CalendarView_Features : Page
+ {
+ public CalendarView_Features()
+ {
+ this.InitializeComponent();
+
+ sut.SelectedDatesChanged += (snd, evt) =>
+ {
+ selected.ItemsSource = sut.SelectedDates.ToArray();
+ };
+ }
+
+ private void SetDisplayDate(object sender, RoutedEventArgs args)
+ {
+ sut.SetDisplayDate(setDisplayDate.Date);
+ }
+ }
+}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_ScrollingRegion.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_ScrollingRegion.xaml
new file mode 100644
index 000000000000..29a35f7bf74b
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_ScrollingRegion.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ The following CalendarView is inside a ScrollViewer
+ with enough content to force scrolling.
+
+
+
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_ScrollingRegion.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_ScrollingRegion.xaml.cs
new file mode 100644
index 000000000000..70f3f21a2fb5
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_ScrollingRegion.xaml.cs
@@ -0,0 +1,14 @@
+using Windows.UI.Xaml.Controls;
+using Uno.UI.Samples.Controls;
+
+namespace UITests.Windows_UI_Xaml_Controls.CalendarView
+{
+ [Sample("Date Picking")]
+ public sealed partial class CalendarView_ScrollingRegion : Page
+ {
+ public CalendarView_ScrollingRegion()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_SmallRange.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_SmallRange.xaml
new file mode 100644
index 000000000000..a6fb1f735d97
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_SmallRange.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+ Following CalendarView should present only tuesday to friday of this week.
+
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_SmallRange.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_SmallRange.xaml.cs
new file mode 100644
index 000000000000..984a38ddfc42
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/CalendarView/CalendarView_SmallRange.xaml.cs
@@ -0,0 +1,23 @@
+using System;
+using Windows.UI.Xaml.Controls;
+using Uno.UI.Samples.Controls;
+
+namespace UITests.Windows_UI_Xaml_Controls.CalendarView
+{
+ [Sample("Date Picking")]
+ public sealed partial class CalendarView_SmallRange : Page
+ {
+ public CalendarView_SmallRange()
+ {
+ this.InitializeComponent();
+
+ var today = DateTime.Now.Date;
+ var sundayThisWeek = today.AddDays(-(int)today.DayOfWeek);
+ var tuesday = sundayThisWeek.AddDays((int)DayOfWeek.Tuesday);
+ var friday = sundayThisWeek.AddDays((int)DayOfWeek.Friday);
+
+ sut.MinDate = tuesday;
+ sut.MaxDate = friday;
+ }
+ }
+}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Automated.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Automated.xaml.cs
index 21bb21c65623..caf217319a40 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Automated.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Automated.xaml.cs
@@ -4,7 +4,7 @@
namespace UITests.Shared.Windows_UI_Xaml_Controls.DatePicker
{
- [SampleControlInfo]
+ [Sample("Date Picking")]
public sealed partial class DatePickerFlyout_Automated : UserControl
{
public DatePickerFlyout_Automated()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Date_Binding.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Date_Binding.xaml.cs
index 040864971936..90f1debf3121 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Date_Binding.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Date_Binding.xaml.cs
@@ -1,13 +1,11 @@
using System;
-using System.Threading.Tasks;
using Windows.UI.Xaml;
using Uno.UI.Samples.Controls;
using Windows.UI.Xaml.Controls;
-using Uno.Extensions;
namespace UITests.Shared.Windows_UI_Xaml_Controls.DatePicker
{
- [SampleControlInfo]
+ [Sample("Date Picking")]
public sealed partial class DatePickerFlyout_Date_Binding : UserControl
{
public DatePickerFlyout_Date_Binding()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_MinYear.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_MinYear.xaml.cs
index 4b270f1340a2..0ef8c8488025 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_MinYear.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_MinYear.xaml.cs
@@ -4,7 +4,7 @@
namespace UITests.Shared.Windows_UI_Xaml_Controls.DatePicker
{
- [SampleControlInfo(viewModelType: typeof(DatePickerViewModel))]
+ [Sample("Date Picking", ViewModelType = typeof(DatePickerViewModel))]
public sealed partial class DatePickerFlyout_MinYear : UserControl
{
public DatePickerFlyout_MinYear()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Unloaded.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Unloaded.xaml.cs
index b76ace8ea59b..64296324e1b5 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Unloaded.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerFlyout_Unloaded.xaml.cs
@@ -7,7 +7,7 @@
namespace UITests.Shared.Windows_UI_Xaml_Controls.DatePicker
{
- [SampleControlInfo]
+ [Sample("Date Picking")]
public sealed partial class DatePickerFlyout_Unloaded : UserControl
{
public DatePickerFlyout_Unloaded()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerSample.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerSample.xaml.cs
index d6217d5747ff..680501b850f0 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerSample.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePickerSample.xaml.cs
@@ -4,7 +4,7 @@
namespace UITests.Shared.Windows_UI_Xaml_Controls.DatePicker
{
- [SampleControlInfo(viewModelType: typeof(DatePickerViewModel))]
+ [Sample("Date Picking", ViewModelType = typeof(DatePickerViewModel))]
public sealed partial class DatePickerSample : UserControl
{
public DatePickerSample()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_DatePartVisibility.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_DatePartVisibility.xaml.cs
index 114e7658302e..a5f634c1a204 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_DatePartVisibility.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_DatePartVisibility.xaml.cs
@@ -3,7 +3,7 @@
namespace UITests.Windows_UI_Xaml_Controls.DatePicker
{
- [Sample("DatePicker")]
+ [Sample("Date Picking")]
public sealed partial class DatePicker_DatePartVisibility : Page
{
public DatePicker_DatePartVisibility()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Features.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Features.xaml.cs
index 9fa40cffaa06..dda6f93630e9 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Features.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Features.xaml.cs
@@ -7,7 +7,7 @@
namespace UITests.Windows_UI_Xaml_Controls.DatePicker
{
- [Sample]
+ [Sample("Date Picking")]
public sealed partial class DatePicker_Features : Page
{
public static readonly DependencyProperty PickedDateProperty =
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Header.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Header.xaml.cs
index 945ebce04e7c..178010696aea 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Header.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Header.xaml.cs
@@ -3,12 +3,12 @@
namespace UITests.Windows_UI_Xaml_Controls.DatePicker
{
- [Sample("DatePicker")]
+ [Sample("Date Picking")]
public sealed partial class DatePicker_Header : Page
- {
- public DatePicker_Header()
- {
- this.InitializeComponent();
- }
- }
+ {
+ public DatePicker_Header()
+ {
+ this.InitializeComponent();
+ }
+ }
}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Placement.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Placement.xaml.cs
index 03e8ba7ed9a3..2ca8a689a983 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Placement.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_Placement.xaml.cs
@@ -3,7 +3,7 @@
namespace UITests.Windows_UI_Xaml_Controls.DatePicker
{
- [Sample]
+ [Sample("Date Picking")]
public sealed partial class DatePicker_Placement : Page
{
public DatePicker_Placement()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_SampleContent.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_SampleContent.xaml.cs
index 3cced9f1c691..bc9e5666f0f7 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_SampleContent.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/DatePicker/DatePicker_SampleContent.xaml.cs
@@ -3,11 +3,9 @@
using Windows.UI.Xaml.Controls;
using UITests.Shared.Windows_UI_Xaml_Controls.DatePicker.Models;
-// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
-
namespace UITests.Shared.Windows_UI_Xaml_Controls.DatePicker
{
- [SampleControlInfo(viewModelType: typeof(DatePickerViewModel))]
+ [Sample("Date Picking", ViewModelType = typeof(DatePickerViewModel))]
public sealed partial class DatePicker_SampleContent : UserControl
{
public DatePicker_SampleContent()
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ScrollViewerTests/ScrollViewer_Nested.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ScrollViewerTests/ScrollViewer_Nested.xaml
new file mode 100644
index 000000000000..abfaf267bb44
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ScrollViewerTests/ScrollViewer_Nested.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ScrollViewerTests/ScrollViewer_Nested.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ScrollViewerTests/ScrollViewer_Nested.xaml.cs
new file mode 100644
index 000000000000..9b4b61815d2b
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Controls/ScrollViewerTests/ScrollViewer_Nested.xaml.cs
@@ -0,0 +1,14 @@
+using Windows.UI.Xaml.Controls;
+using Uno.UI.Samples.Controls;
+
+namespace UITests.Windows_UI_Xaml_Controls.ScrollViewerTests
+{
+ [Sample("ScrollViewer")]
+ public sealed partial class ScrollViewer_Nested : Page
+ {
+ public ScrollViewer_Nested()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyPropertyGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyPropertyGenerator.cs
index 73f099e3f385..6c96e5fc5417 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyPropertyGenerator.cs
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/DependencyObject/DependencyPropertyGenerator.cs
@@ -287,26 +287,38 @@ private void GenerateAttachedProperty(IndentedStringBuilder builder, INamedTypeS
}
changedCallbackName ??= $"On{propertyName}Changed";
- var propertyChangedMethod = propertyOwnerType.GetMethods().FirstOrDefault(m => m.Name == changedCallbackName);
- if (changedCallback || propertyChangedMethod != null)
+ var propertyChangedMethods = propertyOwnerType.GetMethods().Where(m => m.Name == changedCallbackName).ToArray();
+ if (changedCallback || (propertyChangedMethods?.Any() ?? false))
{
- var isDPChangedEventArgsParam = SymbolEqualityComparer.Default.Equals(propertyChangedMethod?.Parameters.ElementAtOrDefault(1)?.Type, _dependencyPropertyChangedEventArgsSymbol);
- if (isDPChangedEventArgsParam)
+ if (propertyChangedMethods.FirstOrDefault(IsCallbackWithDPChangedArgs) is { } callbackWithEventArgs)
{
builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => {changedCallbackName}(instance, args)");
}
+ else if (propertyChangedMethods.FirstOrDefault(IsCallbackWithDPChangedArgsOnly) is { } callbackWithEventArgsOnly)
+ {
+ builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => {changedCallbackName}(args)");
+ }
+ else if (propertyChangedMethods?.FirstOrDefault(m => m?.Parameters.Length == 2) is { } callbackWithOldAndNew)
+ {
+ builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => {changedCallbackName}(({propertyTypeName})args.OldValue, ({propertyTypeName})args.NewValue)");
+ }
else
{
- builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => {changedCallbackName}(instance, ({propertyTypeName})args.OldValue, ({propertyTypeName})args.NewValue)");
+ builder.AppendLineInvariant($"#error Valid {changedCallbackName} not found. Must be {changedCallbackName}(DependencyPropertyChangedEventArgs), {changedCallbackName}(Instance, DependencyPropertyChangedEventArgs) or {changedCallbackName}(oldValue, newValue)");
}
}
-
builder.AppendLineInvariant($"));");
builder.AppendLineInvariant($"#endregion");
}
+ private bool IsCallbackWithDPChangedArgsOnly(IMethodSymbol m)
+ => SymbolEqualityComparer.Default.Equals(m?.Parameters.FirstOrDefault()?.Type, _dependencyPropertyChangedEventArgsSymbol);
+
+ private bool IsCallbackWithDPChangedArgs(IMethodSymbol m)
+ => m?.Parameters.Length == 2 && SymbolEqualityComparer.Default.Equals(m?.Parameters[1].Type, _dependencyPropertyChangedEventArgsSymbol);
+
static KeyValuePair? GetAttributeValue(AttributeData attribute, string parameterName)
=> attribute?.NamedArguments.FirstOrDefault(kvp => kvp.Key == parameterName);
@@ -391,19 +403,25 @@ private void GenerateProperty(IndentedStringBuilder builder, INamedTypeSymbol ow
}
changedCallbackName ??= $"On{propertyName}Changed";
-
- var propertyChangedMethod = propertySymbol.ContainingType.GetMethods().FirstOrDefault(m => m.Name == changedCallbackName);
- if (changedCallback || propertyChangedMethod != null)
+ var propertyChangedMethods = propertySymbol.ContainingType.GetMethods().Where(m => m.Name == changedCallbackName).ToArray();
+ if (changedCallback || propertyChangedMethods.Any())
{
- var isDPChangedEventArgsParam = SymbolEqualityComparer.Default.Equals(propertyChangedMethod?.Parameters.FirstOrDefault()?.Type, _dependencyPropertyChangedEventArgsSymbol);
- if (isDPChangedEventArgsParam)
+ if (propertyChangedMethods.FirstOrDefault(IsCallbackWithDPChangedArgs) is { } callbackWithEventArgs)
+ {
+ builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => (({containingTypeName})instance).{changedCallbackName}(instance, args)");
+ }
+ else if (propertyChangedMethods.FirstOrDefault(IsCallbackWithDPChangedArgsOnly) is { } callbackWithEventArgsOnly)
{
builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => (({containingTypeName})instance).{changedCallbackName}(args)");
}
- else
+ else if (propertyChangedMethods?.FirstOrDefault(m => m?.Parameters.Length == 2) is { } callbackWithOldAndNew)
{
builder.AppendLineInvariant($"\t\t, propertyChangedCallback: (instance, args) => (({containingTypeName})instance).{changedCallbackName}(({propertyTypeName})args.OldValue, ({propertyTypeName})args.NewValue)");
}
+ else
+ {
+ builder.AppendLineInvariant($"#error Valid {changedCallbackName} not found. Must be {changedCallbackName}(DependencyPropertyChangedEventArgs), {changedCallbackName}(Instance, DependencyPropertyChangedEventArgs) or {changedCallbackName}(oldValue, newValue)");
+ }
}
builder.AppendLineInvariant($"));");
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/RemoteControl/RemoteControlGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/RemoteControl/RemoteControlGenerator.cs
index 08b6d53ac01f..bf5e36b34682 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/RemoteControl/RemoteControlGenerator.cs
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/RemoteControl/RemoteControlGenerator.cs
@@ -3,6 +3,7 @@
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
@@ -111,10 +112,23 @@ private static void BuildEndPointAttribute(GeneratorExecutionContext context, In
var addresses = NetworkInterface.GetAllNetworkInterfaces()
.SelectMany(x => x.GetIPProperties().UnicastAddresses)
.Where(x => !IPAddress.IsLoopback(x.Address));
+ //This is not supported on linux yet: .Where(x => x.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred);
foreach (var addressInfo in addresses)
{
- sb.AppendLineInvariant($"[assembly: global::Uno.UI.RemoteControl.ServerEndpointAttribute(\"{addressInfo.Address}\", {unoRemoteControlPort})]");
+ var address = addressInfo.Address;
+
+ string addressStr;
+ if(address.AddressFamily == AddressFamily.InterNetworkV6)
+ {
+ address.ScopeId = 0; // remove annoying "%xx" on IPv6 addresses
+ addressStr = $"[{address}]";
+ }
+ else
+ {
+ addressStr = address.ToString();
+ }
+ sb.AppendLineInvariant($"[assembly: global::Uno.UI.RemoteControl.ServerEndpointAttribute(\"{addressStr}\", {unoRemoteControlPort})]");
}
}
else
diff --git a/src/Uno.Foundation/PropertyValue.Internal.cs b/src/Uno.Foundation/PropertyValue.Internal.cs
new file mode 100644
index 000000000000..3d715295a534
--- /dev/null
+++ b/src/Uno.Foundation/PropertyValue.Internal.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Windows.Foundation
+{
+ // Those methods are for WinUI code compatibility
+ partial class PropertyValue
+ {
+ internal static void CreateFromDateTime(DateTimeOffset date, out object value)
+ {
+ value = CreateDateTime(date);
+ }
+ }
+}
diff --git a/src/Uno.UI.FluentTheme/Resources/PriorityDefault/CalendarView_themeresources.xaml b/src/Uno.UI.FluentTheme/Resources/PriorityDefault/CalendarView_themeresources.xaml
index 30268c4ac07f..28bbd390db89 100644
--- a/src/Uno.UI.FluentTheme/Resources/PriorityDefault/CalendarView_themeresources.xaml
+++ b/src/Uno.UI.FluentTheme/Resources/PriorityDefault/CalendarView_themeresources.xaml
@@ -1,7 +1,8 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:uno="using:Uno.UI.Xaml.Controls">
@@ -201,6 +202,7 @@
+
@@ -242,8 +244,13 @@
-
-
+
+
+
+
+
+
+
@@ -256,7 +263,12 @@
-
+
+
+
+
+
+
@@ -458,9 +470,9 @@
-
+
diff --git a/src/Uno.UI.FluentTheme/themeresources.xaml b/src/Uno.UI.FluentTheme/themeresources.xaml
index 9ebc3f8cf57c..df4ee2c512ff 100644
--- a/src/Uno.UI.FluentTheme/themeresources.xaml
+++ b/src/Uno.UI.FluentTheme/themeresources.xaml
@@ -1,4 +1,4 @@
-
+
@@ -9623,6 +9623,7 @@
+
@@ -9665,6 +9666,11 @@
+
+
+
+
+
@@ -9678,6 +9684,11 @@
+
+
+
+
+
@@ -9880,9 +9891,9 @@
-
-
-
+
diff --git a/src/Uno.UI.Runtime.Skia.Gtk/GtkCoreWindowExtension.cs b/src/Uno.UI.Runtime.Skia.Gtk/GtkCoreWindowExtension.cs
index fefe77cc8bcb..6527ae97cf51 100644
--- a/src/Uno.UI.Runtime.Skia.Gtk/GtkCoreWindowExtension.cs
+++ b/src/Uno.UI.Runtime.Skia.Gtk/GtkCoreWindowExtension.cs
@@ -65,335 +65,335 @@ public GtkCoreWindowExtension(object owner)
GtkHost.EventBox.ScrollEvent += OnWindowScrollEvent;
GtkHost.EventBox.TouchEvent += OnWindowTouchEvent;
GtkHost.EventBox.ProximityInEvent += OnWindowProximityInEvent;
- GtkHost.EventBox.ProximityOutEvent += OnWindowProximityOutEvent;
-
- InitializeKeyboard();
- }
-
- partial void InitializeKeyboard();
-
- private void OnWindowEnterEvent(object o, EnterNotifyEventArgs args)
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerEntered(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise PointerEntered", e);
- }
- }
-
- private void OnWindowLeaveEvent(object o, LeaveNotifyEventArgs args)
- {
- try
- {
- // The Ungrab mode event is triggered after click
- // even when the pointer does not leave the window.
- // This may need to be removed when we implement
- // native pointer capture support properly.
- if (args.Event.Mode != CrossingMode.Ungrab)
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerExited(ptArgs);
- }
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise PointerExited", e);
- }
- }
-
- private void OnWindowButtonPressEvent(object o, ButtonPressEventArgs args)
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerPressed(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise PointerPressed", e);
- }
- }
-
- private void OnWindowButtonReleaseEvent(object o, ButtonReleaseEventArgs args)
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerReleased(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise PointerReleased", e);
- }
- }
-
- private void OnWindowMotionEvent(object o, MotionNotifyEventArgs args) // a.k.a. move
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerMoved(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise PointerMoved", e);
- }
- }
-
- private void OnWindowScrollEvent(object o, ScrollEventArgs args)
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerWheelChanged(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise PointerExited", e);
- }
- }
-
- private void OnWindowTouchEvent(object o, TouchEventArgs args)
- {
- try
- {
- // Note: We DO NOT used the args.Event as it's causing an InvalidCastException as of 2021-03-09
- if (args.Args.FirstOrDefault() is Gdk.Event evt
- && AsPointerArgs(evt) is { } ptArgs)
- {
- switch (evt.Type)
- {
- case EventType.TouchBegin:
- _ownerEvents.RaisePointerEntered(ptArgs);
- _ownerEvents.RaisePointerPressed(ptArgs);
- break;
-
- case EventType.TouchEnd:
- _ownerEvents.RaisePointerReleased(ptArgs);
- _ownerEvents.RaisePointerExited(ptArgs);
- break;
-
- case EventType.TouchCancel:
- _ownerEvents.RaisePointerMoved(ptArgs);
- break;
- }
-
- _ownerEvents.RaisePointerMoved(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise touch event (one of Enter/Pressed/Move/Release/Exited)", e);
- }
- }
-
- private void OnWindowProximityOutEvent(object o, ProximityOutEventArgs args)
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerExited(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise proximity out event", e);
- }
- }
-
- private void OnWindowProximityInEvent(object o, ProximityInEventArgs args)
- {
- try
- {
- if (AsPointerArgs(args.Event) is { } ptArgs)
- {
- _ownerEvents.RaisePointerEntered(ptArgs);
- }
- }
- catch (Exception e)
- {
- this.Log().Error("Failed to raise proximity in event", e);
- }
- }
-
- private static PointerEventArgs AsPointerArgs(Event evt)
- {
- var dev = EventHelper.GetSourceDevice(evt); // We use GetSourceDevice (and not GetDevice) in order to get the TouchScreen device
- var pointerDevice = ToPointerDevice(dev);
- var pointerDeviceType = pointerDevice.PointerDeviceType;
-
- if (pointerDeviceType == PointerDeviceType.Touch)
- {
- var type = evt.Type;
- if (type != EventType.TouchBegin
- && type != EventType.TouchUpdate
- && type != EventType.TouchEnd
- && type != EventType.TouchCancel)
- {
- // Touch events are sent twice by the OnWindowTouchEvent and the ButtonPressed/Released and MotionEvent.
- // As the pointerId (a.k.a sequence) is available only for touch events, we mute events coming from the mouse handlers.
- return null;
- }
- }
-
- var time = EventHelper.GetTime(evt);
- EventHelper.GetRootCoords(evt, out var x, out var y);
- var rawPosition = new Windows.Foundation.Point(x, y);
- EventHelper.GetCoords(evt, out x, out y);
- var position = new Windows.Foundation.Point(x, y);
- EventHelper.GetState(evt, out var state);
-
- var pointerId = 1u;
- var modifiers = GetKeyModifiers(state);
- var properties = new PointerPointProperties();
-
- switch (evt.Type)
- {
- case EventType.TouchBegin:
- properties.PointerUpdateKind = PointerUpdateKind.LeftButtonPressed;
- break;
-
- case EventType.TouchEnd:
- case EventType.TouchCancel:
- properties.PointerUpdateKind = PointerUpdateKind.LeftButtonReleased;
- break;
-
- case EventType.ButtonPress when EventHelper.GetButton(evt, out var button):
- properties.PointerUpdateKind = button switch
- {
- 1 => LeftButtonPressed,
- 2 => MiddleButtonPressed,
- 3 => RightButtonPressed,
- 4 => XButton1Pressed,
- 5 => XButton2Pressed,
- _ => Other
- };
- break;
-
- case EventType.ButtonRelease when EventHelper.GetButton(evt, out var button):
- properties.PointerUpdateKind = button switch
- {
- 1 => LeftButtonReleased,
- 2 => MiddleButtonReleased,
- 3 => RightButtonReleased,
- 4 => XButton1Released,
- 5 => XButton2Released,
- _ => Other
- };
- break;
-
- case EventType.Scroll when EventHelper.GetScrollDeltas(evt, out var scrollX, out var scrollY):
- var isHorizontal = scrollY == 0;
- properties.IsHorizontalMouseWheel = isHorizontal;
- properties.MouseWheelDelta = (int)(isHorizontal ? scrollX : scrollY);
- break;
-
- case EventType.Scroll: // when no scroll value
- return null;
- }
-
- switch (pointerDevice.PointerDeviceType)
- {
- // https://gtk-rs.org/docs/gdk/struct.ModifierType.html
-
- case PointerDeviceType.Touch:
- properties.IsLeftButtonPressed = evt.Type != EventType.TouchEnd && evt.Type != EventType.TouchCancel;
- pointerId = ((uint?)EventHelper.GetEventSequence(evt)?.Handle) ?? 0u;
- break;
-
- case PointerDeviceType.Mouse:
- properties.IsLeftButtonPressed = IsPressed(state, ModifierType.Button1Mask, properties.PointerUpdateKind, LeftButtonPressed, LeftButtonPressed);
- properties.IsMiddleButtonPressed = IsPressed(state, ModifierType.Button2Mask, properties.PointerUpdateKind, MiddleButtonPressed, MiddleButtonReleased);
- properties.IsRightButtonPressed = IsPressed(state, ModifierType.Button3Mask, properties.PointerUpdateKind, RightButtonPressed, RightButtonReleased);
- properties.IsXButton1Pressed = IsPressed(state, ModifierType.Button4Mask, properties.PointerUpdateKind, XButton1Pressed, XButton1Released);
- properties.IsXButton2Pressed = IsPressed(state, ModifierType.Button5Mask, properties.PointerUpdateKind, XButton1Pressed, XButton2Released);
- break;
-
- case PointerDeviceType.Pen:
- properties.IsBarrelButtonPressed = state.HasFlag(Gdk.ModifierType.Button1Mask);
- // On UWP the IsRight flag is set only when pen touch the screen when the barrel button is already pressed.
- // We accept it as a known limitation that with uno the flag is set as soon as the barrel is pressed,
- // not matter is the pen was already in contact with the screen or not.
- properties.IsRightButtonPressed = properties.IsBarrelButtonPressed;
- properties.IsEraser = dev.Source == InputSource.Eraser;
- if (EventHelper.GetAxis(evt, AxisUse.Pressure, out var pressure))
- {
- properties.Pressure = (float)Math.Min(1.0, pressure);
- }
- break;
- }
-
- var pointerPoint = new Windows.UI.Input.PointerPoint(
- frameId: time,
- timestamp: time,
- device: pointerDevice,
- pointerId: pointerId,
- rawPosition: rawPosition,
- position: position,
- isInContact: properties.HasPressedButton,
- properties: properties
- );
-
- return new PointerEventArgs(pointerPoint, modifiers);
- }
-
- private static VirtualKeyModifiers GetKeyModifiers(Gdk.ModifierType state)
- {
- var modifiers = VirtualKeyModifiers.None;
- if (state.HasFlag(Gdk.ModifierType.ShiftMask))
- {
- modifiers |= VirtualKeyModifiers.Shift;
- }
- if (state.HasFlag(Gdk.ModifierType.ControlMask))
- {
- modifiers |= VirtualKeyModifiers.Control;
- }
- if (state.HasFlag(Gdk.ModifierType.Mod1Mask))
- {
- modifiers |= VirtualKeyModifiers.Menu;
- }
- return modifiers;
- }
-
- private static PointerDevice ToPointerDevice(Gdk.Device sourceDevice)
- {
- switch (sourceDevice.Source)
- {
- // https://gtk-rs.org/docs/gdk/enum.InputSource.html
-
- case InputSource.Pen:
- case InputSource.Eraser:
- case InputSource.TabletPad: // the device is a "pad", a collection of buttons, rings and strips found in drawing tablets.
- case InputSource.Cursor: // the device is a graphics tablet “puck” or similar device.
- return PointerDevice.For(PointerDeviceType.Pen);
-
- case InputSource.Touchscreen:
- return PointerDevice.For(PointerDeviceType.Touch);
-
- case InputSource.Mouse:
- default:
- return PointerDevice.For(PointerDeviceType.Mouse);
- }
- }
-
- private static bool IsPressed(ModifierType state, ModifierType mask, PointerUpdateKind update, PointerUpdateKind pressed, PointerUpdateKind released)
- => update == pressed || (state.HasFlag(mask) && update != released);
- }
-}
+ GtkHost.EventBox.ProximityOutEvent += OnWindowProximityOutEvent;
+
+ InitializeKeyboard();
+ }
+
+ partial void InitializeKeyboard();
+
+ private void OnWindowEnterEvent(object o, EnterNotifyEventArgs args)
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerEntered(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise PointerEntered", e);
+ }
+ }
+
+ private void OnWindowLeaveEvent(object o, LeaveNotifyEventArgs args)
+ {
+ try
+ {
+ // The Ungrab mode event is triggered after click
+ // even when the pointer does not leave the window.
+ // This may need to be removed when we implement
+ // native pointer capture support properly.
+ if (args.Event.Mode != CrossingMode.Ungrab)
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerExited(ptArgs);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise PointerExited", e);
+ }
+ }
+
+ private void OnWindowButtonPressEvent(object o, ButtonPressEventArgs args)
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerPressed(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise PointerPressed", e);
+ }
+ }
+
+ private void OnWindowButtonReleaseEvent(object o, ButtonReleaseEventArgs args)
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerReleased(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise PointerReleased", e);
+ }
+ }
+
+ private void OnWindowMotionEvent(object o, MotionNotifyEventArgs args) // a.k.a. move
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerMoved(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise PointerMoved", e);
+ }
+ }
+
+ private void OnWindowScrollEvent(object o, ScrollEventArgs args)
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerWheelChanged(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise PointerExited", e);
+ }
+ }
+
+ private void OnWindowTouchEvent(object o, TouchEventArgs args)
+ {
+ try
+ {
+ // Note: We DO NOT used the args.Event as it's causing an InvalidCastException as of 2021-03-09
+ if (args.Args.FirstOrDefault() is Gdk.Event evt
+ && AsPointerArgs(evt) is { } ptArgs)
+ {
+ switch (evt.Type)
+ {
+ case EventType.TouchBegin:
+ _ownerEvents.RaisePointerEntered(ptArgs);
+ _ownerEvents.RaisePointerPressed(ptArgs);
+ break;
+
+ case EventType.TouchEnd:
+ _ownerEvents.RaisePointerReleased(ptArgs);
+ _ownerEvents.RaisePointerExited(ptArgs);
+ break;
+
+ case EventType.TouchCancel:
+ _ownerEvents.RaisePointerMoved(ptArgs);
+ break;
+ }
+
+ _ownerEvents.RaisePointerMoved(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise touch event (one of Enter/Pressed/Move/Release/Exited)", e);
+ }
+ }
+
+ private void OnWindowProximityOutEvent(object o, ProximityOutEventArgs args)
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerExited(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise proximity out event", e);
+ }
+ }
+
+ private void OnWindowProximityInEvent(object o, ProximityInEventArgs args)
+ {
+ try
+ {
+ if (AsPointerArgs(args.Event) is { } ptArgs)
+ {
+ _ownerEvents.RaisePointerEntered(ptArgs);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Log().Error("Failed to raise proximity in event", e);
+ }
+ }
+
+ private static PointerEventArgs AsPointerArgs(Event evt)
+ {
+ var dev = EventHelper.GetSourceDevice(evt); // We use GetSourceDevice (and not GetDevice) in order to get the TouchScreen device
+ var pointerDevice = ToPointerDevice(dev);
+ var pointerDeviceType = pointerDevice.PointerDeviceType;
+
+ if (pointerDeviceType == PointerDeviceType.Touch)
+ {
+ var type = evt.Type;
+ if (type != EventType.TouchBegin
+ && type != EventType.TouchUpdate
+ && type != EventType.TouchEnd
+ && type != EventType.TouchCancel)
+ {
+ // Touch events are sent twice by the OnWindowTouchEvent and the ButtonPressed/Released and MotionEvent.
+ // As the pointerId (a.k.a sequence) is available only for touch events, we mute events coming from the mouse handlers.
+ return null;
+ }
+ }
+
+ var time = EventHelper.GetTime(evt);
+ EventHelper.GetRootCoords(evt, out var x, out var y);
+ var rawPosition = new Windows.Foundation.Point(x, y);
+ EventHelper.GetCoords(evt, out x, out y);
+ var position = new Windows.Foundation.Point(x, y);
+ EventHelper.GetState(evt, out var state);
+
+ var pointerId = 1u;
+ var modifiers = GetKeyModifiers(state);
+ var properties = new PointerPointProperties();
+
+ switch (evt.Type)
+ {
+ case EventType.TouchBegin:
+ properties.PointerUpdateKind = PointerUpdateKind.LeftButtonPressed;
+ break;
+
+ case EventType.TouchEnd:
+ case EventType.TouchCancel:
+ properties.PointerUpdateKind = PointerUpdateKind.LeftButtonReleased;
+ break;
+
+ case EventType.ButtonPress when EventHelper.GetButton(evt, out var button):
+ properties.PointerUpdateKind = button switch
+ {
+ 1 => LeftButtonPressed,
+ 2 => MiddleButtonPressed,
+ 3 => RightButtonPressed,
+ 4 => XButton1Pressed,
+ 5 => XButton2Pressed,
+ _ => Other
+ };
+ break;
+
+ case EventType.ButtonRelease when EventHelper.GetButton(evt, out var button):
+ properties.PointerUpdateKind = button switch
+ {
+ 1 => LeftButtonReleased,
+ 2 => MiddleButtonReleased,
+ 3 => RightButtonReleased,
+ 4 => XButton1Released,
+ 5 => XButton2Released,
+ _ => Other
+ };
+ break;
+
+ case EventType.Scroll when EventHelper.GetScrollDeltas(evt, out var scrollX, out var scrollY):
+ var isHorizontal = scrollY == 0;
+ properties.IsHorizontalMouseWheel = isHorizontal;
+ properties.MouseWheelDelta = (int)(isHorizontal ? scrollX : scrollY);
+ break;
+
+ case EventType.Scroll: // when no scroll value
+ return null;
+ }
+
+ switch (pointerDevice.PointerDeviceType)
+ {
+ // https://gtk-rs.org/docs/gdk/struct.ModifierType.html
+
+ case PointerDeviceType.Touch:
+ properties.IsLeftButtonPressed = evt.Type != EventType.TouchEnd && evt.Type != EventType.TouchCancel;
+ pointerId = ((uint?)EventHelper.GetEventSequence(evt)?.Handle) ?? 0u;
+ break;
+
+ case PointerDeviceType.Mouse:
+ properties.IsLeftButtonPressed = IsPressed(state, ModifierType.Button1Mask, properties.PointerUpdateKind, LeftButtonPressed, LeftButtonReleased);
+ properties.IsMiddleButtonPressed = IsPressed(state, ModifierType.Button2Mask, properties.PointerUpdateKind, MiddleButtonPressed, MiddleButtonReleased);
+ properties.IsRightButtonPressed = IsPressed(state, ModifierType.Button3Mask, properties.PointerUpdateKind, RightButtonPressed, RightButtonReleased);
+ properties.IsXButton1Pressed = IsPressed(state, ModifierType.Button4Mask, properties.PointerUpdateKind, XButton1Pressed, XButton1Released);
+ properties.IsXButton2Pressed = IsPressed(state, ModifierType.Button5Mask, properties.PointerUpdateKind, XButton1Pressed, XButton2Released);
+ break;
+
+ case PointerDeviceType.Pen:
+ properties.IsBarrelButtonPressed = state.HasFlag(Gdk.ModifierType.Button1Mask);
+ // On UWP the IsRight flag is set only when pen touch the screen when the barrel button is already pressed.
+ // We accept it as a known limitation that with uno the flag is set as soon as the barrel is pressed,
+ // not matter is the pen was already in contact with the screen or not.
+ properties.IsRightButtonPressed = properties.IsBarrelButtonPressed;
+ properties.IsEraser = dev.Source == InputSource.Eraser;
+ if (EventHelper.GetAxis(evt, AxisUse.Pressure, out var pressure))
+ {
+ properties.Pressure = (float)Math.Min(1.0, pressure);
+ }
+ break;
+ }
+
+ var pointerPoint = new Windows.UI.Input.PointerPoint(
+ frameId: time,
+ timestamp: time,
+ device: pointerDevice,
+ pointerId: pointerId,
+ rawPosition: rawPosition,
+ position: position,
+ isInContact: properties.HasPressedButton,
+ properties: properties
+ );
+
+ return new PointerEventArgs(pointerPoint, modifiers);
+ }
+
+ private static VirtualKeyModifiers GetKeyModifiers(Gdk.ModifierType state)
+ {
+ var modifiers = VirtualKeyModifiers.None;
+ if (state.HasFlag(Gdk.ModifierType.ShiftMask))
+ {
+ modifiers |= VirtualKeyModifiers.Shift;
+ }
+ if (state.HasFlag(Gdk.ModifierType.ControlMask))
+ {
+ modifiers |= VirtualKeyModifiers.Control;
+ }
+ if (state.HasFlag(Gdk.ModifierType.Mod1Mask))
+ {
+ modifiers |= VirtualKeyModifiers.Menu;
+ }
+ return modifiers;
+ }
+
+ private static PointerDevice ToPointerDevice(Gdk.Device sourceDevice)
+ {
+ switch (sourceDevice.Source)
+ {
+ // https://gtk-rs.org/docs/gdk/enum.InputSource.html
+
+ case InputSource.Pen:
+ case InputSource.Eraser:
+ case InputSource.TabletPad: // the device is a "pad", a collection of buttons, rings and strips found in drawing tablets.
+ case InputSource.Cursor: // the device is a graphics tablet “puck” or similar device.
+ return PointerDevice.For(PointerDeviceType.Pen);
+
+ case InputSource.Touchscreen:
+ return PointerDevice.For(PointerDeviceType.Touch);
+
+ case InputSource.Mouse:
+ default:
+ return PointerDevice.For(PointerDeviceType.Mouse);
+ }
+ }
+
+ private static bool IsPressed(ModifierType state, ModifierType mask, PointerUpdateKind update, PointerUpdateKind pressed, PointerUpdateKind released)
+ => update == pressed || (state.HasFlag(mask) && update != released);
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/Helpers/DateTimeOffsetExtensions.cs b/src/Uno.UI.RuntimeTests/Helpers/DateTimeOffsetExtensions.cs
new file mode 100644
index 000000000000..7ba5ee160598
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Helpers/DateTimeOffsetExtensions.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Uno.UI.RuntimeTests.Helpers
+{
+ internal static class DateTimeOffsetExtensions
+ {
+ internal static long UniversalTime(this DateTimeOffset dto)
+ {
+ return dto.UtcTicks;
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/Helpers/ListExtensions.cs b/src/Uno.UI.RuntimeTests/Helpers/ListExtensions.cs
new file mode 100644
index 000000000000..7f51452b1955
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Helpers/ListExtensions.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace Uno.UI.RuntimeTests.Helpers
+{
+ internal static class ListExtensions
+ {
+ // Helpers for WinUI code compatibility
+ internal static T GetAt(this List list, int index) => list[index];
+
+ internal static T GetAt(this IList list, int index) => list[index];
+
+ internal static T GetAt(this IReadOnlyList list, int index) => list[index];
+
+ internal static void SetAt(this IList list, int index, T value) => list[index] = value;
+
+ internal static void Append(this IList list, T item) => list.Add(item);
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/CalendarHelper.h.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/CalendarHelper.h.cs
new file mode 100644
index 000000000000..758f4679a3b7
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/CalendarHelper.h.cs
@@ -0,0 +1,435 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Foundation;
+using Windows.Globalization;
+using Windows.Globalization.DateTimeFormatting;
+using Windows.UI;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Markup;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Tests.Enterprise;
+using Uno.UI.RuntimeTests.Helpers;
+
+using static Private.Infrastructure.TestServices;
+
+namespace Private.Infrastructure
+{
+ internal static class CalendarHelper
+ {
+ internal static (int era, int year, int month, int day) GetDateFromDateTime(DateTimeOffset datetime)
+ {
+ var calendar = new Calendar();
+ calendar.SetDateTime(datetime);
+
+ return (
+ calendar.Era,
+ calendar.Year,
+ calendar.Month,
+ calendar.Day);
+ }
+
+ internal static bool CompareDate(DateTimeOffset lhs, DateTimeOffset rhs)
+ {
+ var ldate = GetDateFromDateTime(lhs);
+ var rdate = GetDateFromDateTime(rhs);
+
+ return ldate == rdate;
+ }
+
+ internal static bool CompareColor(Color lhs, Color rhs)
+ {
+ return lhs.A == rhs.A
+ && lhs.R == rhs.R
+ && lhs.G == rhs.G
+ && lhs.B == rhs.B;
+ }
+
+ internal class DateCollection : List
+ {
+ internal int Size => Count;
+ }
+
+ internal class ColorCollection : List
+ {
+ internal int Size => Count;
+ }
+
+ internal static DependencyObject GetTemplateChild(DependencyObject root, string childName)
+ {
+
+ var count = VisualTreeHelper.GetChildrenCount(root);
+ for (var i = 0; i < count; i++)
+ {
+ var child = VisualTreeHelper.GetChild(root, i);
+ var childAsFE = child as FrameworkElement;
+ if (childAsFE.Name == childName)
+ {
+ return child;
+ }
+
+ var result = GetTemplateChild(child, childName);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ internal static DateTimeOffset ConvertToDateTime(
+ int era, int year, int month, int day, int period = 1,
+ int hour = 12, int minute = 0, int second = 0, int nanosecond = 0)
+ {
+ var calendar = new Calendar
+ {
+ Era = era,
+ Year = year,
+ Month = month,
+ Day = day,
+ Period = period,
+ Hour = hour,
+ Minute = minute,
+ Second = second,
+ Nanosecond = nanosecond
+ };
+ return calendar.GetDateTime();
+ }
+
+ internal static async Task CreateTestResources()
+ {
+ Grid rootPanel = default;
+ await TestServices.RunOnUIThread(()=>
+ {
+ rootPanel = XamlReader.Load(
+ " ")
+ as Grid;
+
+ TestServices.WindowHelper.WindowContent = rootPanel;
+ });
+
+ return rootPanel;
+ }
+
+ internal static bool CompareDateTime(DateTimeOffset lhs, DateTimeOffset rhs)
+ {
+ return lhs.UniversalTime() == rhs.UniversalTime();
+ }
+
+ internal class CalendarViewHelper
+ {
+ // public
+ internal CalendarViewHelper()
+ {
+ m_loadedEvent = new Event();
+ m_selectedDatesChangedEvent = new Event();
+ m_cicEvent = new Event();
+ m_addedDates = new DateCollection();
+ m_removedDates = new DateCollection();
+ m_loadedRegistration = CreateSafeEventRegistration("Loaded");
+ m_selectedDatesChangedRegistration = CreateSafeEventRegistration>("SelectedDatesChanged");
+ m_cicRegistration = CreateSafeEventRegistration>("CalendarViewDayItemChanging");
+ }
+
+ internal async Task GetCalendarView()
+ {
+ if (m_cv == null)
+ {
+ await TestServices.RunOnUIThread(() =>
+ {
+ if (m_cv == null)
+ {
+ m_cv = new CalendarView();
+ TestServices.VERIFY_IS_NOT_NULL(m_cv);
+ }
+ });
+ }
+
+
+ return m_cv;
+ }
+
+ internal DependencyObject GetTemplateChild(string childName)
+ {
+ return GetTemplateChild(m_cv, childName);
+ }
+
+ internal async Task PrepareLoadedEvent()
+ {
+ await TestServices.RunOnUIThread(() =>
+ {
+ m_loadedRegistration.Attach(
+ m_cv,
+ (object sender, RoutedEventArgs e) =>
+ {
+ OnLoaded(sender, e);
+ });
+ });
+ }
+
+ internal async Task PrepareSelectedDatesChangedEvent()
+ {
+ m_addedDates.Clear();
+ m_removedDates.Clear();
+
+ await TestServices.RunOnUIThread(() =>
+ {
+ m_selectedDatesChangedRegistration.Attach(
+ m_cv,
+ (sender, e) =>
+ {
+ OnSelectedDatesChanged(sender, e);
+ });
+ });
+ }
+
+ internal void ExpectAddedDate(DateTimeOffset date)
+ {
+ m_addedDates.Append(date);
+ }
+
+ internal void ExpectRemovedDate(DateTimeOffset date)
+ {
+ m_removedDates.Append(date);
+ }
+
+ // note: PrepareCICEvent must be called before item get realized
+ // the best position is before CalendarView enters visual tree.
+ internal async Task PrepareCICEvent()
+ {
+ await TestServices.RunOnUIThread(() =>
+ {
+ m_cicRegistration.Attach(
+ m_cv,
+ (sender, e) =>
+ {
+ OnCalendarViewDayItemChanging(sender, e);
+ });
+ });
+ }
+
+ internal async Task WaitForLoaded()
+ {
+ await m_loadedEvent.WaitForDefault();
+ TestServices.VERIFY_IS_TRUE(m_loadedEvent.HasFired());
+ m_loadedEvent.Reset();
+ m_loadedRegistration.Detach();
+ }
+
+ internal async Task WaitForSelectedDatesChanged()
+ {
+ await m_selectedDatesChangedEvent.WaitForDefault();
+ TestServices.VERIFY_IS_TRUE(m_selectedDatesChangedEvent.HasFired());
+ m_selectedDatesChangedEvent.Reset();
+ m_selectedDatesChangedRegistration.Detach();
+ }
+
+ internal async Task VerifyNoSelectedDatesChanged()
+ {
+ // we expect no event here, so below statement will timeout and throw WEX.Common.Exception.
+ TestServices.VERIFY_THROWS_WINRT(
+ async () => await m_selectedDatesChangedEvent.WaitFor(TimeSpan.FromMilliseconds(5000), true /* enforceUnderDebugger */),
+ "SelectedDatesChanged event should not raise!");
+ TestServices.VERIFY_IS_FALSE(m_selectedDatesChangedEvent.HasFired());
+ m_selectedDatesChangedRegistration.Detach();
+ }
+
+ internal async Task WaitForCICEvent()
+ {
+ m_cicEvent.WaitForDefault();
+ TestServices.VERIFY_IS_TRUE(m_cicEvent.HasFired());
+ m_cicEvent.Reset();
+ m_cicRegistration.Detach();
+ }
+
+ // private
+ void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ TestServices.LOG_OUTPUT("CalendarViewIntegrationTests: CalendarView Loaded.");
+ m_loadedEvent.Set();
+ }
+
+ void OnSelectedDatesChanged(object sender,
+ CalendarViewSelectedDatesChangedEventArgs e)
+ {
+ TestServices.VERIFY_ARE_EQUAL(e.AddedDates.Count, m_addedDates.Count);
+ TestServices.VERIFY_ARE_EQUAL(e.RemovedDates.Count, m_removedDates.Count);
+
+ for (var i = 0; i < e.AddedDates.Count; ++i)
+ {
+ TestServices.VERIFY_ARE_EQUAL(e.AddedDates.GetAt(i).UniversalTime(), m_addedDates.GetAt(i).UniversalTime());
+ }
+
+ for (var i = 0; i < e.RemovedDates.Count; ++i)
+ {
+ TestServices.VERIFY_ARE_EQUAL(e.RemovedDates.GetAt(i).UniversalTime(), m_removedDates.GetAt(i).UniversalTime());
+ }
+
+ m_selectedDatesChangedEvent.Set();
+ }
+
+ void OnCalendarViewDayItemChanging(object sender,
+ CalendarViewDayItemChangingEventArgs e)
+ {
+ // phase 2: set density bar
+ // phase 5: blackout
+ // phase 7: end cic event
+
+ if (e.Phase == 2)
+ {
+ ColorCollection colors = new ColorCollection();
+ colors.Append(Colors.Red);
+ colors.Append(Colors.Green);
+ colors.Append(Colors.Blue);
+ colors.Append(Colors.Yellow);
+ e.Item.SetDensityColors(colors);
+ }
+ else if (e.Phase == 5)
+ {
+ e.Item.IsBlackout = true;
+ }
+ else if (e.Phase == 7)
+ {
+ m_cicEvent.Set();
+ }
+
+ // keep subscribing cic event until phase 7.
+ if (e.Phase < 7)
+ {
+ e.RegisterUpdateCallback(
+ (sender, e) =>
+ {
+ OnCalendarViewDayItemChanging(sender, e);
+ });
+ }
+ }
+
+ DependencyObject GetTemplateChild(DependencyObject root, string childName)
+ {
+ var count = VisualTreeHelper.GetChildrenCount(root);
+ for (var i = 0; i < count; i++)
+ {
+ var child = VisualTreeHelper.GetChild(root, i);
+ var childAsFE = child as FrameworkElement;
+ if (childAsFE.Name == childName)
+ {
+ return child;
+ }
+
+ var result = GetTemplateChild(child, childName);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ // private
+ CalendarView m_cv;
+
+ SafeEventRegistration m_loadedRegistration;
+
+ SafeEventRegistration> m_selectedDatesChangedRegistration;
+
+ SafeEventRegistration> m_cicRegistration;
+
+ Event m_loadedEvent;
+ Event m_selectedDatesChangedEvent;
+ Event m_cicEvent;
+ DateCollection m_addedDates;
+ DateCollection m_removedDates;
+ };
+
+
+ internal static void DumpDate(DateTimeOffset date, string prefix)
+ {
+ var calendar = new Calendar();
+ calendar.SetDateTime(date);
+ TestServices.LOG_OUTPUT("%s: %d/%d/%d (dd/mm/yy)", prefix, calendar.Year, calendar.Month, calendar.Day);
+ }
+
+ internal static void VerifyDateTimesAreEqual(DateTimeOffset date1, DateTimeOffset date2)
+ {
+ if (date1.UniversalTime() != date2.UniversalTime())
+ {
+ var calendar = new Calendar();
+ calendar.SetDateTime(date1);
+ TestServices.LOG_OUTPUT("UTC %lld: %d/%d/%d %d:%d:%d %d", date1.UniversalTime(), calendar.Year, calendar.Month,
+ calendar.Day, calendar.Hour, calendar.Minute, calendar.Second, calendar.Nanosecond);
+ calendar.SetDateTime(date2);
+ TestServices.LOG_OUTPUT("UTC %lld: %d/%d/%d %d:%d:%d %d", date2.UniversalTime(), calendar.Year, calendar.Month,
+ calendar.Day, calendar.Hour, calendar.Minute, calendar.Second, calendar.Nanosecond);
+ TestServices.VERIFY_ARE_EQUAL(date1.UniversalTime(), date2.UniversalTime());
+ }
+ }
+
+ internal static void CheckFocusedItem()
+ {
+#if WINDOWS_UWP
+ var item = FocusManager.GetFocusedElement();
+#else
+ var item = FocusManager.GetFocusedElement((TestServices.WindowHelper.WindowContent as UIElement)?.XamlRoot);
+#endif
+ TestServices.LOG_OUTPUT("Type of focused item is: %s", item.GetType().FullName);
+ var itemAsFE = (FrameworkElement)(item);
+ if (itemAsFE is { })
+ {
+ var point = itemAsFE.TransformToVisual(null).TransformPoint(new Point(0, 0));
+ TestServices.LOG_OUTPUT("Focused item position. x %f, y %f, width %f, height %f", point.X, point.Y,
+ itemAsFE.ActualWidth, itemAsFE.ActualHeight);
+ }
+
+ var itemAsDayItem =
+ (CalendarViewDayItem)(item);
+ if (itemAsDayItem is { })
+ {
+ TestServices.LOG_OUTPUT("Focused item is a day item, date is %s",
+ DateTimeFormatter.ShortDate.Format(itemAsDayItem.Date)
+ );
+ }
+ }
+
+ internal static bool AreClose(double a, double b, double threshold = 0.1)
+ {
+ TestServices.LOG_OUTPUT("AreClose? %lf, %lf", a, b);
+ return Math.Abs(a - b) <= threshold;
+ }
+
+ internal static void ReplaceAccentColorForTesting(CalendarView cv)
+ {
+ // replace the accent colors to some color ants to make sure test results are not affected by the accent color.
+ cv.TodayForeground = new SolidColorBrush(Colors.Red);
+ cv.SelectedBorderBrush = new SolidColorBrush(Colors.Red);
+ cv.SelectedPressedBorderBrush = new SolidColorBrush(Colors.Green);
+ cv.SelectedHoverBorderBrush = new SolidColorBrush(Colors.Blue);
+ }
+
+
+ internal static string[] GetAllSupportedCalendarIdentifiers()
+ {
+ return new []
+ {
+ "PersianCalendar",
+ "GregorianCalendar",
+ "HebrewCalendar",
+ "HijriCalendar",
+ "JapaneseCalendar",
+ "JulianCalendar",
+ "KoreanCalendar",
+ "TaiwanCalendar",
+ "ThaiCalendar",
+ "UmAlQuraCalendar"
+ };
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/CommonTestSetupHelper.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/CommonTestSetupHelper.cs
new file mode 100644
index 000000000000..e1067c36f1f7
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/CommonTestSetupHelper.cs
@@ -0,0 +1,16 @@
+namespace Private.Infrastructure
+{
+ internal static class CommonTestSetupHelper
+ {
+ private static bool _isInitialized = false;
+
+ internal static async void CommonTestClassSetup()
+ {
+ if (!_isInitialized)
+ {
+ TestServices.EnsureInitialized();
+ }
+ _isInitialized = true;
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/CompareDate.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/CompareDate.cs
new file mode 100644
index 000000000000..db4a24d53a5f
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/CompareDate.cs
@@ -0,0 +1,6 @@
+using System;
+
+namespace Private.Infrastructure
+{
+ public delegate bool CompareDate(DateTimeOffset dto1, DateTimeOffset dto2);
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/MockDComp.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/MockDComp.cs
new file mode 100644
index 000000000000..e53fcc0fcb71
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/MockDComp.cs
@@ -0,0 +1,10 @@
+namespace Private.Infrastructure
+{
+ public class MockDComp
+ {
+ public enum SurfaceComparison
+ {
+ NoComparison
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/PointerFinger.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/PointerFinger.cs
new file mode 100644
index 000000000000..402025240354
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/PointerFinger.cs
@@ -0,0 +1,8 @@
+namespace Private.Infrastructure
+{
+ public enum PointerFinger
+ {
+ Finger1,
+ Finger2
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.InputHelper.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.InputHelper.cs
new file mode 100644
index 000000000000..434feff69982
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.InputHelper.cs
@@ -0,0 +1,46 @@
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Private.Infrastructure
+{
+ partial class TestServices
+ {
+ public static class InputHelper
+ {
+ public static void DynamicPressCenter(UIElement element, double x, double y, PointerFinger finger)
+ {
+ throw new System.NotImplementedException();
+ }
+ public static void DynamicRelease(PointerFinger finger)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void MoveMouse(Point position)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void MoveMouse(UIElement element)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void Tap(UIElement element)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void ScrollMouseWheel(CalendarView cv, int i)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void LeftMouseClick(UIElement element)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.KeyboardHelper.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.KeyboardHelper.cs
new file mode 100644
index 000000000000..5b599ed8abee
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.KeyboardHelper.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace Private.Infrastructure
+{
+ public partial class TestServices
+ {
+ public static class KeyboardHelper
+ {
+ public static void PressKeySequence(string keys)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void Down()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public static void Up()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ internal static void ShiftTab()
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void Tab()
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void PageDown()
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void Escape()
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void Enter()
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.WindowHelper.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.WindowHelper.cs
new file mode 100644
index 000000000000..525ff2901ac2
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.WindowHelper.cs
@@ -0,0 +1,300 @@
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using System.Runtime.CompilerServices;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Tests.Enterprise;
+#if NETFX_CORE
+using Uno.UI.Extensions;
+#elif __IOS__
+using UIKit;
+#elif __MACOS__
+using AppKit;
+#else
+using Uno.UI;
+#endif
+
+namespace Private.Infrastructure
+{
+ public partial class TestServices
+ {
+ public class WindowHelper
+ {
+ private static UIElement _originalWindowContent = null;
+
+ public static bool UseActualWindowRoot { get; set; }
+
+ public static object WindowContent
+ {
+ get
+ {
+ if (UseActualWindowRoot)
+ {
+ return Windows.UI.Xaml.Window.Current.Content;
+ }
+ return EmbeddedTestRootControl.Content;
+ }
+
+ internal set
+ {
+ if (UseActualWindowRoot)
+ {
+ Windows.UI.Xaml.Window.Current.Content = value as UIElement;
+ }
+ else if (EmbeddedTestRootControl is ContentControl content)
+ {
+ content.Content = value;
+ }
+ else
+ {
+ Console.WriteLine("Failed to get test content control");
+ }
+ }
+ }
+
+ public static void SaveOriginalWindowContent()
+ {
+ _originalWindowContent = Windows.UI.Xaml.Window.Current.Content;
+ }
+
+ public static void RestoreOriginalWindowContent()
+ {
+ if (_originalWindowContent != null)
+ {
+ Windows.UI.Xaml.Window.Current.Content = _originalWindowContent;
+ _originalWindowContent = null;
+ }
+ }
+
+ public static ContentControl EmbeddedTestRootControl { get; set; }
+
+ public static UIElement RootElement => UseActualWindowRoot ?
+ Windows.UI.Xaml.Window.Current.Content :
+ EmbeddedTestRootControl;
+
+ internal static async Task WaitForIdle()
+ {
+ await RootElement.Dispatcher.RunIdleAsync(_ => { /* Empty to wait for the idle queue to be reached */ });
+ await RootElement.Dispatcher.RunIdleAsync(_ => { /* Empty to wait for the idle queue to be reached */ });
+ }
+
+ ///
+ /// Waits for to be loaded and measured in the visual tree.
+ ///
+ ///
+ /// On UWP, may not always wait long enough for the control to be properly measured.
+ ///
+ /// This method assumes that the control will have a non-zero size once loaded, so it's not appropriate for elements that are
+ /// collapsed, empty, etc.
+ ///
+ internal static async Task WaitForLoaded(FrameworkElement element)
+ {
+ await WaitFor(IsLoaded, message: $"{element} loaded");
+ bool IsLoaded()
+ {
+ if (element.ActualHeight == 0 || element.ActualWidth == 0)
+ {
+ return false;
+ }
+
+ if (element is Control control && control.FindFirstChild(includeCurrent: false) == null)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ internal static async Task WaitForRelayouted(FrameworkElement frameworkElement)
+ {
+ var isRelayouted = false;
+
+ void OnLayoutUpdated(object _, object __)
+ {
+ frameworkElement.LayoutUpdated -= OnLayoutUpdated;
+ isRelayouted = true;
+ }
+
+ frameworkElement.LayoutUpdated += OnLayoutUpdated;
+
+ await WaitFor(() => isRelayouted, message: $"{frameworkElement} re-layouted");
+ }
+
+ internal static async Task WaitForEqual(double expected, Func actualFunc, double tolerance = 1.0, int timeoutMS = 1000)
+ {
+ if (actualFunc is null)
+ {
+ throw new ArgumentNullException(nameof(actualFunc));
+ }
+
+ var actual = actualFunc();
+ if (ApproxEquals(actual))
+ {
+ return;
+ }
+
+ var stopwatch = Stopwatch.StartNew();
+ while (stopwatch.ElapsedMilliseconds < timeoutMS)
+ {
+ await WaitForIdle();
+ actual = actualFunc();
+ if (ApproxEquals(actual))
+ {
+ return;
+ }
+ }
+
+ throw new AssertFailedException($"Timed out waiting for equality condition to be met. Expected {expected} but last received value was {actual}.");
+
+ bool ApproxEquals(double actualValue) => Math.Abs(expected - actualValue) < tolerance;
+ }
+
+ ///
+ /// Wait until a specified is met.
+ ///
+ /// The maximum time to wait before failing the test, in milliseconds.
+ internal static async Task WaitFor(Func condition, int timeoutMS = 1000, string message = null, [CallerMemberName] string callerMemberName = null, [CallerLineNumber] int lineNumber = 0)
+ {
+ if (condition())
+ {
+ return;
+ }
+
+ var stopwatch = Stopwatch.StartNew();
+ while (stopwatch.ElapsedMilliseconds < timeoutMS)
+ {
+ await WaitForIdle();
+ if (condition())
+ {
+ return;
+ }
+ }
+
+ message ??= $"{callerMemberName}():{lineNumber}";
+
+ throw new AssertFailedException("Timed out waiting for condition to be met. " + message);
+ }
+
+ internal static async Task WaitFor(
+ Func condition,
+ T expected,
+ Func messageBuilder = null,
+ Func comparer = null,
+ int timeoutMS = 1000,
+ [CallerMemberName] string callerMemberName = null,
+ [CallerLineNumber] int lineNumber = 0)
+ {
+ comparer ??= (v1, v2) => Equals(v1, v2);
+
+ T value = condition();
+ if (comparer(value, expected))
+ {
+ return;
+ }
+
+ var stopwatch = Stopwatch.StartNew();
+ while (stopwatch.ElapsedMilliseconds < timeoutMS)
+ {
+ await WaitForIdle();
+ value = condition();
+ if (comparer(value, expected))
+ {
+ return;
+ }
+ }
+
+ var customMsg = messageBuilder != null ? messageBuilder(value) : $"Got {value}, expected {expected}";
+ var message = $"{callerMemberName}():{lineNumber} {customMsg}";
+
+ throw new AssertFailedException("Timed out waiting for condition to be met. " + message);
+ }
+
+ internal static async Task WaitForNonNull(
+ Func getT,
+ int timeoutMS = 1000,
+ string message = null,
+ [CallerMemberName] string callerMemberName = null,
+ [CallerLineNumber] int lineNumber = 0)
+ where T : class
+ {
+ if (getT is null)
+ {
+ throw new ArgumentNullException(nameof(getT));
+ }
+
+ if (getT() is { } t)
+ {
+ return t;
+ }
+
+ var stopwatch = Stopwatch.StartNew();
+ while (stopwatch.ElapsedMilliseconds < timeoutMS)
+ {
+ await WaitForIdle();
+
+
+ if (getT() is { } t2)
+ {
+ return t2;
+ }
+ }
+
+ message ??= $"{callerMemberName}():{lineNumber} Never received non-null value";
+
+ throw new AssertFailedException("Timed out waiting for condition to be met. " + message);
+ }
+
+#if DEBUG
+ ///
+ /// This will wait. Forever. Useful when debugging a runtime test if you wish to visually inspect or interact with a view added
+ /// by the test. (To break out of the loop, just set 'shouldWait = false' via the Immediate Window.)
+ ///
+ internal static async Task WaitForever()
+ {
+ var shouldWait = true;
+ while (shouldWait)
+ {
+ await Task.Delay(1000);
+ }
+ }
+#endif
+
+ internal static void ShutdownXaml() { }
+ internal static void VerifyTestCleanup() { }
+
+ internal static void SetWindowSizeOverride(object p) { }
+
+ private static readonly TimeSpan FiveMinutes = TimeSpan.FromMinutes(5);
+
+ public static async Task SynchronouslyTickUIThread(int ticks)
+ {
+ var tickCompleteEvent = new Event();
+
+ for (var i = 0; i < ticks; i++)
+ {
+ await RunOnUIThread(() =>
+ {
+ CompositionTarget.Rendering += OnCompositionTargetOnRendering;
+ });
+
+ void OnCompositionTargetOnRendering(object sender, object o)
+ {
+ CompositionTarget.Rendering -= OnCompositionTargetOnRendering;
+ tickCompleteEvent.Set();
+ }
+
+ await tickCompleteEvent.WaitFor(FiveMinutes);
+ }
+ }
+
+ internal static void ResetWindowContentAndWaitForIdle()
+ {
+
+ }
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.cs
index a0195c71718a..f8e7ee9b8147 100644
--- a/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.cs
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/common/TestServices.cs
@@ -1,288 +1,70 @@
using System;
-using System.Diagnostics;
+using System.Globalization;
using System.Threading.Tasks;
+using Windows.UI.Xaml.Tests.Enterprise;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
-using System.Runtime.CompilerServices;
-#if NETFX_CORE
-using Uno.UI.Extensions;
-#elif __IOS__
-using UIKit;
-#elif __MACOS__
-using AppKit;
-#else
-using Uno.UI;
-#endif
+using FluentAssertions;
namespace Private.Infrastructure
{
- public class TestServices
+ public static partial class TestServices
{
- public class WindowHelper
- {
- private static UIElement _originalWindowContent = null;
-
- public static bool UseActualWindowRoot { get; set; }
-
- public static object WindowContent
- {
- get
- {
- if (UseActualWindowRoot)
- {
- return Windows.UI.Xaml.Window.Current.Content;
- }
- return EmbeddedTestRootControl.Content;
- }
-
- internal set
- {
- if (UseActualWindowRoot)
- {
- Windows.UI.Xaml.Window.Current.Content = value as UIElement;
- }
- else if (EmbeddedTestRootControl is ContentControl content)
- {
- content.Content = value;
- }
- else
- {
- Console.WriteLine("Failed to get test content control");
- }
- }
- }
-
- public static void SaveOriginalWindowContent()
- {
- _originalWindowContent = Windows.UI.Xaml.Window.Current.Content;
- }
-
- public static void RestoreOriginalWindowContent()
- {
- if (_originalWindowContent != null)
- {
- Windows.UI.Xaml.Window.Current.Content = _originalWindowContent;
- _originalWindowContent = null;
- }
- }
-
- public static ContentControl EmbeddedTestRootControl { get; set; }
-
- public static UIElement RootElement => UseActualWindowRoot ?
- Windows.UI.Xaml.Window.Current.Content :
- EmbeddedTestRootControl;
-
- internal static async Task WaitForIdle()
- {
- await RootElement.Dispatcher.RunIdleAsync(_ => { /* Empty to wait for the idle queue to be reached */ });
- await RootElement.Dispatcher.RunIdleAsync(_ => { /* Empty to wait for the idle queue to be reached */ });
- }
-
- ///
- /// Waits for to be loaded and measured in the visual tree.
- ///
- ///
- /// On UWP, may not always wait long enough for the control to be properly measured.
- ///
- /// This method assumes that the control will have a non-zero size once loaded, so it's not appropriate for elements that are
- /// collapsed, empty, etc.
- ///
- internal static async Task WaitForLoaded(FrameworkElement element)
- {
- await WaitFor(IsLoaded, message: $"{element} loaded");
- bool IsLoaded()
- {
- if (element.ActualHeight == 0 || element.ActualWidth == 0)
- {
- return false;
- }
-
- if (element is Control control && control.FindFirstChild(includeCurrent: false) == null)
- {
- return false;
- }
- return true;
- }
- }
+ public class Utilities
+ {
+ internal static void VerifyMockDCompOutput(MockDComp.SurfaceComparison comparison, string step) { }
+ internal static void VerifyMockDCompOutput(MockDComp.SurfaceComparison comparison) { }
- internal static async Task WaitForRelayouted(FrameworkElement frameworkElement)
+ internal static void VerifyUIElementTree()
{
- var isRelayouted = false;
-
- void OnLayoutUpdated(object _, object __)
- {
- frameworkElement.LayoutUpdated -= OnLayoutUpdated;
- isRelayouted = true;
- }
-
- frameworkElement.LayoutUpdated += OnLayoutUpdated;
-
- await WaitFor(() => isRelayouted, message: $"{frameworkElement} re-layouted");
}
- internal static async Task WaitForEqual(double expected, Func actualFunc, double tolerance = 1.0, int timeoutMS = 1000)
+ public static void SetTimeZone(string tzid)
{
- if (actualFunc is null)
- {
- throw new ArgumentNullException(nameof(actualFunc));
- }
-
- var actual = actualFunc();
- if (ApproxEquals(actual))
- {
- return;
- }
-
- var stopwatch = Stopwatch.StartNew();
- while (stopwatch.ElapsedMilliseconds < timeoutMS)
- {
- await WaitForIdle();
- actual = actualFunc();
- if (ApproxEquals(actual))
- {
- return;
- }
- }
-
- throw new AssertFailedException($"Timed out waiting for equality condition to be met. Expected {expected} but last received value was {actual}.");
-
- bool ApproxEquals(double actualValue) => Math.Abs(expected - actualValue) < tolerance;
+ throw new NotImplementedException();
}
- ///
- /// Wait until a specified is met.
- ///
- /// The maximum time to wait before failing the test, in milliseconds.
- internal static async Task WaitFor(Func condition, int timeoutMS = 1000, string message = null, [CallerMemberName] string callerMemberName = null, [CallerLineNumber] int lineNumber = 0)
+ internal static void EnableChangingTimeZone(bool isEnabled)
{
- if (condition())
- {
- return;
- }
-
- var stopwatch = Stopwatch.StartNew();
- while (stopwatch.ElapsedMilliseconds < timeoutMS)
- {
- await WaitForIdle();
- if (condition())
- {
- return;
- }
- }
-
- message ??= $"{callerMemberName}():{lineNumber}";
- throw new AssertFailedException("Timed out waiting for condition to be met. " + message);
- }
-
- internal static async Task WaitFor(
- Func condition,
- T expected,
- Func messageBuilder = null,
- Func comparer = null,
- int timeoutMS = 1000,
- [CallerMemberName] string callerMemberName = null,
- [CallerLineNumber] int lineNumber = 0)
- {
- comparer ??= (v1, v2) => Equals(v1, v2);
-
- T value = condition();
- if (comparer(value, expected))
- {
- return;
- }
-
- var stopwatch = Stopwatch.StartNew();
- while (stopwatch.ElapsedMilliseconds < timeoutMS)
- {
- await WaitForIdle();
- value = condition();
- if (comparer(value, expected))
- {
- return;
- }
- }
-
- var customMsg = messageBuilder != null ? messageBuilder(value) : $"Got {value}, expected {expected}";
- var message = $"{callerMemberName}():{lineNumber} {customMsg}";
-
- throw new AssertFailedException("Timed out waiting for condition to be met. " + message);
- }
-
- internal static async Task WaitForNonNull(
- Func getT,
- int timeoutMS = 1000,
- string message = null,
- [CallerMemberName] string callerMemberName = null,
- [CallerLineNumber] int lineNumber = 0)
- where T : class
- {
- if (getT is null)
- {
- throw new ArgumentNullException(nameof(getT));
- }
-
- if (getT() is { } t)
- {
- return t;
- }
-
- var stopwatch = Stopwatch.StartNew();
- while (stopwatch.ElapsedMilliseconds < timeoutMS)
- {
- await WaitForIdle();
-
-
- if (getT() is { } t2)
- {
- return t2;
- }
- }
-
- message ??= $"{callerMemberName}():{lineNumber} Never received non-null value";
-
- throw new AssertFailedException("Timed out waiting for condition to be met. " + message);
}
+ }
-#if DEBUG
- ///
- /// This will wait. Forever. Useful when debugging a runtime test if you wish to visually inspect or interact with a view added
- /// by the test. (To break out of the loop, just set 'shouldWait = false' via the Immediate Window.)
- ///
- internal static async Task WaitForever()
- {
- var shouldWait = true;
- while (shouldWait)
- {
- await Task.Delay(1000);
- }
- }
+ internal static async Task RunOnUIThread(Action action)
+ {
+#if __WASM__
+ action();
+#else
+ await WindowHelper.RootElement.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action());
#endif
+ }
- internal static void ShutdownXaml() { }
- internal static void VerifyTestCleanup() { }
+ internal static void EnsureInitialized() { }
- internal static void SetWindowSizeOverride(object p) { }
+ public static void VERIFY_IS_NOT_NULL(object value)
+ {
+ Assert.IsNotNull(value);
}
- public class Utilities
+ public static void VERIFY_IS_NULL(object value)
{
- internal static void VerifyMockDCompOutput(MockDComp.SurfaceComparison comparison, string step) { }
- internal static void VerifyMockDCompOutput(MockDComp.SurfaceComparison comparison) { }
+ Assert.IsNull(value);
}
- internal static async Task RunOnUIThread(Action action)
+ public static void THROW_IF_NULL_WITH_MSG(object value, string msg)
{
-#if __WASM__
- action();
-#else
- await WindowHelper.RootElement.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => action());
-#endif
+ Assert.IsNotNull(value, msg);
}
- internal static void EnsureInitialized() { }
+ public static void VERIFY_IS_TRUE(bool value)
+ {
+ Assert.IsTrue(value);
+ }
+
+ public static void VERIFY_IS_FALSE(bool value)
+ {
+ Assert.IsFalse(value);
+ }
public static void VERIFY_IS_LESS_THAN(double actual, double expected)
{
@@ -305,9 +87,28 @@ public static void VERIFY_THROWS_WINRT(Action action, Type exceptionType)
}
}
+ public static void VERIFY_THROWS_WINRT(Action action, string text)
+ where TExpectedException : Exception
+ {
+ var exceptionType = typeof(TExpectedException);
+ try
+ {
+ action();
+ Assert.Fail($"Exception of type {exceptionType} is expected: {text}");
+ }
+ catch (Exception e)
+ {
+ if (e.GetType() != exceptionType)
+ {
+ Assert.Fail($"Exception of type {exceptionType} is expected (was {e.GetType()}. {text}");
+ }
+ }
+ }
+
internal static void VERIFY_ARE_EQUAL(T actual, T expected)
{
- Assert.AreEqual(expected: expected, actual: actual);
+ //Assert.AreEqual(expected: expected, actual: actual);
+ actual.Should().Be(expected);
}
internal static void VERIFY_ARE_VERY_CLOSE(double actual, double expected, double tolerance = 0.1d)
@@ -315,13 +116,56 @@ internal static void VERIFY_ARE_VERY_CLOSE(double actual, double expected, doubl
var difference = Math.Abs(actual - expected);
Assert.IsTrue(difference <= tolerance, $"Expected <{expected}>, actual <{actual}> (tolerance = {tolerance})");
}
- }
- public class MockDComp
- {
- public enum SurfaceComparison
+ internal static void LOG_OUTPUT(string log, params object[] arguments)
+ {
+ if (arguments != null && arguments.Length != 0)
+ {
+ var offset = 0;
+ for (var i = 0;; i++)
+ {
+ offset = log.IndexOf('%', offset + 1);
+ if (offset < 0 || offset + 1 >= log.Length)
+ {
+ break; // finished
+ }
+
+ string replacement = default;
+
+ switch(log[offset + 1])
+ {
+ case 's':
+ case 'd':
+ case 'f':
+ case 'i':
+ case 'g':
+ replacement = $"{{{i}}}";
+ break;
+ case 'x':
+ replacement = $"{{{i}:x}}";
+ break;
+ case 'X':
+ replacement = $"{{{i}:X}}";
+ break;
+ }
+
+ if (replacement is { })
+ {
+ log = log.Substring(0, offset) + replacement + log.Substring(offset + 2);
+ }
+ }
+
+ log = string.Format(CultureInfo.InvariantCulture, log, args: arguments);
+ }
+
+ Console.WriteLine(log);
+ }
+
+ internal static SafeEventRegistration CreateSafeEventRegistration(string eventName)
+ where T : class
+ where TDelegate : Delegate
{
- NoComparison
+ return new SafeEventRegistration(eventName);
}
}
}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/BaseDxamlTestClass.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/BaseDxamlTestClass.cs
new file mode 100644
index 000000000000..4b2411fbae9f
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/BaseDxamlTestClass.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Threading.Tasks;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Media;
+using Private.Infrastructure;
+
+namespace Windows.UI.Xaml.Tests.Enterprise
+{
+ public abstract class BaseDxamlTestClass
+ {
+
+ internal SafeEventRegistration CreateSafeEventRegistration(string eventName)
+ where TElement : class
+ where TDelegate : Delegate
+ {
+ return new SafeEventRegistration(eventName);
+ }
+
+ protected static Task RunOnUIThread(Action action)
+ {
+ return TestServices.RunOnUIThread(action);
+ }
+
+ protected static void VERIFY_IS_TRUE(bool value)
+ {
+ TestServices.VERIFY_IS_TRUE(value);
+ }
+
+ protected static void VERIFY_IS_FALSE(bool value)
+ {
+ TestServices.VERIFY_IS_FALSE(value);
+ }
+
+ protected static void VERIFY_ARE_EQUAL(T actual, T expected)
+ {
+ TestServices.VERIFY_ARE_EQUAL(actual, expected);
+ }
+
+ protected static void VERIFY_IS_NULL(object value)
+ {
+ TestServices.VERIFY_IS_NULL(value);
+ }
+
+ protected static void LOG_OUTPUT(string log, params object[] arguments)
+ {
+ TestServices.LOG_OUTPUT(log, arguments);
+ }
+
+ protected static void VERIFY_THROWS_WINRT(Action action, Type exceptionType)
+ {
+ TestServices.VERIFY_THROWS_WINRT(action, exceptionType);
+ }
+
+ protected static void VERIFY_THROWS_WINRT(Action action, string text)
+ where TExpectedException : Exception
+ {
+ TestServices.VERIFY_THROWS_WINRT(action, text);
+ }
+
+ protected static int ARRAYSIZE(Array array)
+ {
+ return array.Length;
+ }
+
+ protected static FrameworkElement FrameworkElement(object o) => o as FrameworkElement;
+ protected static Control Control(object o) => o as Control;
+ protected static Grid Grid(object o) => o as Grid;
+ protected static Flyout Flyout(object o) => o as Flyout;
+ protected static StackPanel StackPanel(object o) => o as StackPanel;
+ protected static ScrollViewer ScrollViewer(object o) => o as ScrollViewer;
+ protected static Border Border(object o) => o as Border;
+ protected static Button Button(object o) => o as Button;
+ protected static TextBlock TextBlock(object o) => o as TextBlock;
+ protected static string String(object o) => o as string;
+ protected static SolidColorBrush SolidColorBrush(object o) => o as SolidColorBrush;
+
+ protected static CalendarView CalendarView(object o) => o as CalendarView;
+ protected static CalendarDatePicker CalendarDatePicker(object o) => o as CalendarDatePicker;
+ protected static CalendarPanel CalendarPanel(object o) => o as CalendarPanel;
+ protected static CalendarViewDayItem CalendarViewDayItem(object o) => o as CalendarViewDayItem;
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/Event.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/Event.cs
new file mode 100644
index 000000000000..d4af03186519
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/Event.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Windows.UI.Xaml.Tests.Enterprise
+{
+ internal class Event
+ {
+ private readonly ManualResetEventSlim _event = new ManualResetEventSlim();
+
+ internal int FiredCount { get; private set; }
+
+ internal async Task WaitForDefault(int timeout = 5000, CancellationToken ct = default)
+ {
+ var h = _event.WaitHandle;
+ RegisteredWaitHandle registration = default;
+ try
+ {
+ if (h.WaitOne(0))
+ {
+ return true;
+ }
+
+ if (timeout == 0)
+ {
+ return false;
+ }
+
+ var tcs = new TaskCompletionSource();
+
+ using var _ = ct.Register(() => tcs.TrySetCanceled());
+
+ registration = ThreadPool
+ .RegisterWaitForSingleObject(
+ h,
+ (_, __) =>
+ {
+ tcs.TrySetResult(true);
+ },
+ null,
+ timeout,
+ executeOnlyOnce: true);
+
+ return await tcs.Task;
+ }
+ finally
+ {
+ registration?.Unregister(h);
+ }
+ }
+
+ internal void Set()
+ {
+ FiredCount++;
+ _event.Set();
+ }
+
+ internal bool HasFired() => _event.IsSet;
+
+ public void Reset()
+ {
+ _event.Reset();
+ }
+
+ public Task WaitFor(TimeSpan timeout, CancellationToken ct = default)
+ {
+ return WaitForDefault((int)timeout.TotalMilliseconds, ct);
+ }
+
+ internal Task WaitFor(TimeSpan timeout, bool enforceUnderDebugger, CancellationToken ct = default)
+ {
+ if (!enforceUnderDebugger && Debugger.IsAttached)
+ {
+ return Task.CompletedTask;
+ }
+
+ return WaitForDefault((int)timeout.TotalMilliseconds, ct);
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/SafeEventRegistration.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/SafeEventRegistration.cs
new file mode 100644
index 000000000000..190d39aba34d
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/SafeEventRegistration.cs
@@ -0,0 +1,36 @@
+#nullable enable
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
+using Windows.UI.Xaml.Controls;
+using Uno.Disposables;
+
+namespace Windows.UI.Xaml.Tests.Enterprise
+{
+ internal class SafeEventRegistration
+ where TElement : class
+ where TDelegate : Delegate
+ {
+ private readonly EventInfo _eventInfo;
+ private IDisposable? _last;
+
+ public SafeEventRegistration(string eventName)
+ {
+ _eventInfo = typeof(TElement).GetEvent(eventName);
+ }
+
+ internal IDisposable Attach(TElement element, TDelegate handler)
+ {
+ Detach(); // Detach any previous handler
+
+ _eventInfo.GetAddMethod().Invoke(element, new object[] {handler});
+
+ return _last = Disposable.Create(() =>
+ {
+ _eventInfo.GetRemoveMethod().Invoke(element, new object[] {handler});
+ });
+ }
+
+ internal void Detach() => _last?.Dispose();
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendardatepicker/CalendarDatePickerHelper.h.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendardatepicker/CalendarDatePickerHelper.h.cs
new file mode 100644
index 000000000000..56e5a942679d
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendardatepicker/CalendarDatePickerHelper.h.cs
@@ -0,0 +1,288 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using System.Threading.Tasks;
+using Windows.Foundation;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Tests.Enterprise;
+using Uno.UI.RuntimeTests.Helpers;
+
+using static Private.Infrastructure.TestServices;
+using static Private.Infrastructure.CalendarHelper;
+using Private.Infrastructure;
+
+namespace Windows.UI.Xaml.Tests.Common
+{
+
+ internal class CalendarDatePickerHelper
+ {
+ // public
+ internal CalendarDatePickerHelper()
+ {
+ m_loadedEvent = new Event();
+ m_selectedDatesChangedEvent = new Event();
+ m_cicEvent = new Event();
+ m_openedEvent = new Event();
+ m_closedEvent = new Event();
+ m_addedDates = new CalendarHelper.DateCollection();
+ m_removedDates = new CalendarHelper.DateCollection();
+ m_loadedRegistration = CreateSafeEventRegistration("Loaded");
+ m_selectedDatesChangedRegistration = CreateSafeEventRegistration>("DateChanged");
+ m_cicRegistration = CreateSafeEventRegistration("CalendarViewDayItemChanging");
+ m_openedRegistration = CreateSafeEventRegistration>("Opened");
+ m_closedRegistration = CreateSafeEventRegistration>("Closed");
+ }
+
+ internal async Task GetCalendarDatePicker()
+ {
+ if (m_cp == null)
+ {
+ await RunOnUIThread(() =>
+ {
+ EnsurePickerCreated();
+ });
+ }
+
+ return m_cp;
+ }
+
+ private void EnsurePickerCreated()
+ {
+ if (m_cp == null)
+ {
+ m_cp = new CalendarDatePicker();
+ }
+
+ VERIFY_IS_NOT_NULL(m_cp);
+ }
+
+ internal DependencyObject GetTemplateChild(string childName)
+ {
+ EnsurePickerCreated();
+ return GetTemplateChild(m_cp, childName);
+ }
+
+ internal DependencyObject GetTemplateChild(DependencyObject root, string childName)
+ {
+ return CalendarHelper.GetTemplateChild(root, childName);
+ }
+
+
+ internal Task PrepareLoadedEvent()
+ {
+ return RunOnUIThread(() =>
+ {
+ EnsurePickerCreated();
+ m_loadedRegistration.Attach(
+ m_cp,
+ (sender, e) =>
+ {
+ OnLoaded();
+ });
+ });
+ }
+
+ internal Task PrepareOpenedEvent()
+ {
+ return RunOnUIThread(() =>
+ {
+ EnsurePickerCreated();
+ m_openedRegistration.Attach(
+ m_cp,
+ ( sender, e) =>
+ {
+ OnOpened();
+ });
+ });
+ }
+
+ internal Task PrepareClosedEvent()
+ {
+ return RunOnUIThread(() =>
+ {
+ EnsurePickerCreated();
+ m_closedRegistration.Attach(
+ m_cp,
+ (sender, e) =>
+ {
+ OnClosed();
+ });
+ });
+ }
+
+ internal Task PrepareSelectedDateChangedEvent()
+ {
+ m_addedDates.Clear();
+ m_removedDates.Clear();
+
+ return RunOnUIThread(() =>
+ {
+ EnsurePickerCreated();
+ m_selectedDatesChangedRegistration.Attach(
+ m_cp,
+ (sender, e) =>
+ {
+ OnCalendarViewSelectedDateChanged(sender, e);
+ });
+ });
+ }
+
+ internal void ExpectAddedDate(DateTimeOffset date)
+ {
+ m_addedDates.Append(date);
+ }
+
+ internal void ExpectRemovedDate(DateTimeOffset date)
+ {
+ m_removedDates.Append(date);
+ }
+
+ // note: PrepareCICEvent must be called before item get realized
+ // the best position is before CalendarDatePicker enters visual tree.
+ internal Task PrepareCICEvent()
+ {
+ return RunOnUIThread(() =>
+ {
+ EnsurePickerCreated();
+ m_cicRegistration.Attach(
+ m_cp,
+ (sender, e) =>
+ {
+ OnCalendarViewDayItemChanging(sender as CalendarView, e);
+ });
+ });
+ }
+
+ internal async Task WaitForLoaded()
+ {
+ await m_loadedEvent.WaitForDefault();
+ VERIFY_IS_TRUE(m_loadedEvent.HasFired());
+ m_loadedEvent.Reset();
+ m_loadedRegistration.Detach();
+ }
+
+ internal async Task WaitForOpened()
+ {
+ await m_openedEvent.WaitForDefault();
+ VERIFY_IS_TRUE(m_openedEvent.HasFired());
+ m_openedEvent.Reset();
+ m_openedRegistration.Detach();
+ }
+
+ internal async Task WaitForClosed()
+ {
+ await m_closedEvent.WaitForDefault();
+ VERIFY_IS_TRUE(m_closedEvent.HasFired());
+ m_closedEvent.Reset();
+ m_closedRegistration.Detach();
+ }
+
+ internal async Task WaitForSelectedDatesChanged()
+ {
+ await m_selectedDatesChangedEvent.WaitForDefault();
+ VERIFY_IS_TRUE(m_selectedDatesChangedEvent.HasFired());
+ m_selectedDatesChangedEvent.Reset();
+ m_selectedDatesChangedRegistration.Detach();
+ }
+
+ internal async Task WaitForCICEvent()
+ {
+ await m_cicEvent.WaitForDefault();
+ VERIFY_IS_TRUE(m_cicEvent.HasFired());
+ m_cicEvent.Reset();
+ m_cicRegistration.Detach();
+ }
+
+ // private
+ void OnLoaded()
+ {
+ LOG_OUTPUT("CalendarDatePickerIntegrationTests: CalendarDatePicker Loaded.");
+ m_loadedEvent.Set();
+ }
+
+ void OnOpened()
+ {
+ LOG_OUTPUT("CalendarDatePickerIntegrationTests: CalendarDatePicker CalendarView flyout Opened.");
+ m_openedEvent.Set();
+ }
+
+ void OnClosed()
+ {
+ LOG_OUTPUT("CalendarDatePickerIntegrationTests: CalendarDatePicker CalendarView flyout Closed.");
+ m_closedEvent.Set();
+ }
+
+ void OnCalendarViewSelectedDateChanged(object sender, CalendarDatePickerDateChangedEventArgs e)
+ {
+ var addedSize = e.NewDate is { } ? 1 : 0;
+ var removedSize = e.OldDate is { } ? 1 : 0;
+ VERIFY_ARE_EQUAL(m_addedDates.Size, addedSize);
+ VERIFY_ARE_EQUAL(m_removedDates.Size, removedSize);
+
+ if (e.NewDate is { })
+ {
+ VERIFY_ARE_EQUAL(e.NewDate.Value.UniversalTime(), m_addedDates.GetAt(0).UniversalTime());
+ }
+
+ if (e.OldDate is { })
+ {
+ VERIFY_ARE_EQUAL(e.OldDate.Value.UniversalTime(), m_removedDates.GetAt(0).UniversalTime());
+ }
+
+ m_selectedDatesChangedEvent.Set();
+ }
+
+ void OnCalendarViewDayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs e)
+ {
+ // phase 2: set density bar
+ // phase 5: blackout
+ // phase 7: end cic event
+
+ if (e.Phase == 2)
+ {
+ CalendarHelper.ColorCollection colors = new CalendarHelper.ColorCollection();
+ colors.Append(Colors.Red);
+ colors.Append(Colors.Green);
+ colors.Append(Colors.Blue);
+ colors.Append(Colors.Yellow);
+ e.Item.SetDensityColors(colors);
+ }
+ else if (e.Phase == 5)
+ {
+ e.Item.IsBlackout = true;
+ }
+ else if (e.Phase == 7)
+ {
+ m_cicEvent.Set();
+ }
+
+ // keep subscribing cic event until phase 7.
+ if (e.Phase < 7)
+ {
+ e.RegisterUpdateCallback(
+ (sender, e) =>
+ {
+ OnCalendarViewDayItemChanging(sender, e);
+ });
+ }
+ }
+
+ // private
+ CalendarDatePicker m_cp;
+
+ SafeEventRegistration m_loadedRegistration;
+
+ SafeEventRegistration> m_selectedDatesChangedRegistration;
+ SafeEventRegistration m_cicRegistration;
+ SafeEventRegistration> m_openedRegistration;
+ SafeEventRegistration> m_closedRegistration;
+ Event m_loadedEvent;
+ Event m_selectedDatesChangedEvent;
+ Event m_cicEvent;
+ Event m_openedEvent;
+ Event m_closedEvent;
+ CalendarHelper.DateCollection m_addedDates;
+ CalendarHelper.DateCollection m_removedDates;
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendardatepicker/CalendarDatePickerIntegrationTests.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendardatepicker/CalendarDatePickerIntegrationTests.cs
new file mode 100644
index 000000000000..6c26a608bd86
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendardatepicker/CalendarDatePickerIntegrationTests.cs
@@ -0,0 +1,962 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+#if !WINDOWS_UWP
+
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Foundation;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Markup;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Tests.Common;
+using FluentAssertions.Execution;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Private.Infrastructure;
+using Uno.UI.RuntimeTests.Helpers;
+using Uno.UI.RuntimeTests.MUX.Helpers;
+
+using static Private.Infrastructure.TestServices;
+using static Private.Infrastructure.CalendarHelper;
+
+namespace Windows.UI.Xaml.Tests.Enterprise.CalendarDatePickerTests
+{
+ [TestClass]
+ [Ignore("Validating regressions only for now")]
+ public partial class CalendarDatePickerIntegrationTests : BaseDxamlTestClass
+ {
+ [ClassInitialize]
+ public void ClassSetup()
+ {
+ CommonTestSetupHelper.CommonTestClassSetup();
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ TestServices.WindowHelper.VerifyTestCleanup();
+ }
+
+ //
+ // Test Cases
+ //
+ [TestMethod]
+ public async Task ValidateDefaultPropertyValues()
+ {
+ await RunOnUIThread(() =>
+ {
+ using var _ = new AssertionScope();
+
+ var cp = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+ VERIFY_ARE_EQUAL(cp.FirstDayOfWeek, Windows.Globalization.DayOfWeek.Sunday);
+ VERIFY_ARE_EQUAL(cp.DisplayMode, Windows.UI.Xaml.Controls.CalendarViewDisplayMode.Month);
+ VERIFY_ARE_EQUAL(cp.IsTodayHighlighted, true);
+ VERIFY_ARE_EQUAL(cp.IsOutOfScopeEnabled, true);
+ VERIFY_ARE_EQUAL(cp.IsGroupLabelVisible, false);
+
+ Windows.Globalization.Calendar calendar = new Windows.Globalization.Calendar();
+ calendar.SetToNow();
+
+ calendar.AddYears(-100);
+
+ calendar.Month = calendar.FirstMonthInThisYear;
+ calendar.Day = calendar.FirstDayInThisMonth;
+
+ var minDate = calendar.GetDateTime();
+
+ calendar.AddYears(200);
+
+ calendar.Month = calendar.LastMonthInThisYear;
+ calendar.Day = calendar.LastDayInThisMonth;
+
+ var maxDate = calendar.GetDateTime();
+
+ CompareDate comparer = CalendarHelper.CompareDate;
+ VERIFY_IS_TRUE(comparer(cp.MinDate, minDate));
+ VERIFY_IS_TRUE(comparer(cp.MaxDate, maxDate));
+ });
+ }
+
+ [TestMethod]
+ public async Task CanEnterAndLeaveLiveTree()
+ {
+ // Note: CalendarDatePicker can't use below commented line to test "CanEnterAndLeaveLiveTree"
+ // the problem is in below helper, we did these:
+ // 1. create CalendarDatePicker
+ // 2. added into visual tree
+ // 3. test loaded and unloaded event
+ // 4. remove CalendarDatePicker from visual tree
+ // 5. destroy CalendarDatePicker
+ // .....
+
+ // because we destroy CalendarDatePicker after we remove it from visual tree, so if there are any left work in build tree services, they can't be cleaned up correctly.
+ // this should happens on ListView and GridView, however for default ListView and GridView (especially in below helper method) are empty and there is no buildtree work.
+ // But for default CalendarDatePicker, we have! because default CalendarDatePicker will show the dates in 3 years.
+
+ //Generic.FrameworkElementTests.CanEnterAndLeaveLiveTree();
+
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ });
+
+ helper.WaitForLoaded();
+
+ // remove from visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Clear();
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+
+ [TestMethod]
+ public async Task CanOpenFlyoutByTapping()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ Grid root = null;
+ TextBlock dateText = null;
+ FlyoutBase flyout = null;
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ root = Grid(helper.GetTemplateChild("Root"));
+ dateText = TextBlock(helper.GetTemplateChild("DateText"));
+
+ VERIFY_IS_NOT_NULL(root);
+ VERIFY_IS_NOT_NULL(dateText);
+
+ flyout = FlyoutBase.GetAttachedFlyout(root);
+ VERIFY_IS_NOT_NULL(flyout);
+ });
+
+ helper.PrepareOpenedEvent();
+
+ TestServices.InputHelper.Tap(dateText);
+
+ helper.WaitForOpened();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ helper.PrepareClosedEvent();
+
+ await RunOnUIThread(() =>
+ {
+ // close the flyout before exiting.
+ flyout.Hide();
+ });
+ helper.WaitForClosed();
+
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+
+
+ [TestMethod]
+ public async Task CanOpenFlyoutByKeyboard()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ cp.Focus(Windows.UI.Xaml.FocusState.Programmatic);
+ });
+
+ helper.PrepareOpenedEvent();
+ await TestServices.WindowHelper.WaitForIdle();
+
+ // press enter to open flyout
+ TestServices.KeyboardHelper.Enter();
+
+ helper.WaitForOpened();
+
+ // escape to close the flyout
+ TestServices.KeyboardHelper.Escape();
+
+ await TestServices.WindowHelper.WaitForIdle();
+ helper.PrepareOpenedEvent();
+
+ await RunOnUIThread(() =>
+ {
+ cp.Focus(Windows.UI.Xaml.FocusState.Programmatic);
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+
+ // press space to open flyout
+ TestServices.KeyboardHelper.PressKeySequence("$d$_ #$u$_ ");
+
+ helper.WaitForOpened();
+
+ // escape to close the flyout
+ TestServices.KeyboardHelper.Escape();
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+
+
+ [TestMethod]
+ public async Task CanOpenCloseFlyoutBySettingIsCalendarOpen()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ helper.PrepareOpenedEvent();
+
+ await RunOnUIThread(() =>
+ {
+ cp.IsCalendarOpen = true;
+ });
+ helper.WaitForOpened();
+
+ helper.PrepareClosedEvent();
+
+ await RunOnUIThread(() =>
+ {
+ cp.IsCalendarOpen = false;
+ });
+
+ helper.WaitForClosed();
+
+ await RunOnUIThread(() =>
+ {
+ // disable CP to make sure input pane is not open during clean up.
+ cp.IsEnabled = false;
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+
+ [TestMethod]
+ public async Task CanCloseFlyoutBySelectingADate()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ TextBlock dateText = null;
+ Grid root = null;
+ FlyoutBase flyout = null;
+ CalendarView calendarView = null;
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ cp.IsOutOfScopeEnabled = false;
+ rootPanel.Children.Append(cp);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ dateText = TextBlock(helper.GetTemplateChild("DateText"));
+ VERIFY_IS_NOT_NULL(dateText);
+
+ root = Grid(helper.GetTemplateChild("Root"));
+ VERIFY_IS_NOT_NULL(root);
+
+ flyout = FlyoutBase.GetAttachedFlyout(root);
+ VERIFY_IS_NOT_NULL(flyout);
+
+ var content = Flyout(flyout).Content;
+ calendarView = CalendarView(content);
+ VERIFY_IS_NOT_NULL(calendarView);
+
+
+ calendarView.MinDate = ConvertToDateTime(1, 2000, 1, 1);
+ calendarView.MaxDate = ConvertToDateTime(1, 2001, 1, 1);
+ calendarView.UpdateLayout();
+ });
+
+
+ TestServices.InputHelper.Tap(dateText);
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ helper.PrepareClosedEvent();
+
+ await RunOnUIThread(() =>
+ {
+ calendarView.SelectedDates.Append(ConvertToDateTime(1, 2000, 10, 21));
+ });
+
+ helper.WaitForClosed();
+
+ await RunOnUIThread(() =>
+ {
+ LOG_OUTPUT("actual text: %s.", dateText.Text);
+ // Note: below string contains invisible unicode characters (BiDi characters),
+ // you should always use copy&paste to get the string, directly
+ // type the string will cause the string comparison fails.
+ VERIFY_IS_TRUE(dateText.Text == "10/21/2000");
+ });
+
+ await RunOnUIThread(() =>
+ {
+ // disable CP to make sure input pane is not open during clean up.
+ cp.IsEnabled = false;
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+
+ [TestMethod]
+ public async Task ValidateDateIsCoerced()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ cp.MinDate = ConvertToDateTime(1, 2000, 1, 1);
+ cp.MaxDate = ConvertToDateTime(1, 2002, 1, 1);
+ cp.Date = ConvertToDateTime(1, 2001, 1, 1);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ VERIFY_IS_NOT_NULL(cp.Date);
+ VERIFY_ARE_EQUAL(cp.Date.Value.UniversalTime(), ConvertToDateTime(1, 2001, 1, 1).UniversalTime());
+
+ // make date beyond the range.
+ // it should be coerced to min/max
+ cp.Date = ConvertToDateTime(1, 2010, 1, 1);
+ cp.UpdateLayout();
+ VERIFY_IS_NOT_NULL(cp.Date);
+ VERIFY_ARE_EQUAL(cp.Date.Value.UniversalTime(), cp.MaxDate.UniversalTime());
+
+ cp.Date = ConvertToDateTime(1, 1999, 1, 1);
+ cp.UpdateLayout();
+ VERIFY_IS_NOT_NULL(cp.Date);
+ VERIFY_ARE_EQUAL(cp.Date.Value.UniversalTime(), cp.MinDate.UniversalTime());
+ });
+ }
+
+ [TestMethod]
+ public async Task CanFormatDate()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ TextBlock dateText = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ cp.MinDate = ConvertToDateTime(1, 2000, 1, 1);
+ cp.MaxDate = ConvertToDateTime(1, 2002, 1, 1);
+ cp.Date = ConvertToDateTime(1, 2001, 1, 1);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ dateText = TextBlock(helper.GetTemplateChild("DateText"));
+ VERIFY_IS_NOT_NULL(dateText);
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ cp.DateFormat = "{dayofweek.full}, {month.full} {day.integer}, {year.full}"; // equivalent to "longdate"
+ cp.UpdateLayout();
+
+ LOG_OUTPUT("actual text: %s.", dateText.Text);
+ VERIFY_IS_TRUE(dateText.Text == "Monday, January 1, 2001");
+ });
+ }
+
+ [TestMethod]
+ public async Task SettingCalendarIdentifierChangesDateFormat()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ TextBlock dateText = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ cp.MinDate = ConvertToDateTime(1, 2000, 1, 1);
+ cp.MaxDate = ConvertToDateTime(1, 2002, 1, 1);
+ cp.Date = ConvertToDateTime(1, 2001, 1, 1);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ dateText = TextBlock(helper.GetTemplateChild("DateText"));
+ VERIFY_IS_NOT_NULL(dateText);
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ cp.DateFormat = "{dayofweek.full}, {month.full} {day.integer}, {year.full}"; // equivalent to "longdate"
+ cp.UpdateLayout();
+
+ cp.CalendarIdentifier = Windows.Globalization.CalendarIdentifiers.Taiwan;
+
+ LOG_OUTPUT("actual text: %s.", dateText.Text);
+ VERIFY_IS_TRUE(dateText.Text == "Monday, January 1, 90");
+ });
+
+ }
+
+ [TestMethod]
+ public async Task PressingDoesNotOpenMenuFlyout()
+ {
+ TestCleanupWrapper cleanup;
+
+ Grid rootPanel = null;
+ Grid root = null;
+ TextBlock dateText = null;
+ FlyoutBase flyout = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ var gridPointerPressedEvent = new Event();
+ var gridPointerPressedRegistration = CreateSafeEventRegistration("PointerPressed");
+
+ rootPanel = await CreateTestResources();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ cp.MinDate = ConvertToDateTime(1, 2000, 1, 1);
+ cp.MaxDate = ConvertToDateTime(1, 2002, 1, 1);
+ cp.Date = ConvertToDateTime(1, 2001, 1, 1);
+
+ gridPointerPressedRegistration.Attach(rootPanel,
+ (object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs args) =>
+ {
+ gridPointerPressedEvent.Set();
+ });
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ root = Grid(helper.GetTemplateChild("Root"));
+ dateText = TextBlock(helper.GetTemplateChild("DateText"));
+
+ VERIFY_IS_NOT_NULL(root);
+ VERIFY_IS_NOT_NULL(dateText);
+
+ flyout = FlyoutBase.GetAttachedFlyout(root);
+ VERIFY_IS_NOT_NULL(flyout);
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+
+ helper.PrepareOpenedEvent();
+
+ TestServices.InputHelper.Tap(dateText);
+
+ helper.WaitForOpened();
+
+ await TestServices.WindowHelper.WaitForIdle();
+ helper.PrepareClosedEvent();
+
+ await RunOnUIThread(() =>
+ {
+ // close the flyout before exiting.
+ flyout.Hide();
+ });
+ helper.WaitForClosed();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ VERIFY_IS_FALSE(gridPointerPressedEvent.HasFired());
+ }
+
+ [TestMethod]
+ public async Task ValidateUIElementTree()
+ {
+ TestCleanupWrapper cleanup;
+
+ StackPanel rootPanel = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+
+ await helper.PrepareLoadedEvent();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cdp = await helper.GetCalendarDatePicker();
+
+ await RunOnUIThread(() =>
+ {
+ rootPanel = StackPanel(XamlReader.Load(
+ " ")
+ );
+
+ global::Private.Infrastructure.TestServices.WindowHelper.WindowContent = rootPanel;
+ });
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cdp);
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ cdp.Focus(Windows.UI.Xaml.FocusState.Pointer);
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ TestServices.Utilities.VerifyUIElementTree();
+ }
+
+ [TestMethod]
+ public async Task ValidateVisualStates()
+ {
+ //WUCRenderingScopeGuard guard(DCompRendering.WUCCompleteSynchronousCompTree, false /* resizeWindow */);
+
+ TestServices.WindowHelper.SetWindowSizeOverride(new Size(400, 400));
+
+ StackPanel rootPanel = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ await helper.PrepareLoadedEvent();
+ CalendarDatePicker cpNormal = await helper.GetCalendarDatePicker();
+ CalendarDatePicker cpPressed = null;
+ CalendarDatePicker cpPointerOver = null;
+ CalendarDatePicker cpDisabled = null;
+ CalendarDatePicker cpFocused = null;
+ CalendarDatePicker cpSelected = null;
+
+ await RunOnUIThread(() =>
+ {
+ rootPanel = StackPanel(XamlReader.Load(
+ " ")
+ );
+
+ global::Private.Infrastructure.TestServices.WindowHelper.WindowContent = rootPanel;
+ });
+
+
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ cpPressed = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+ cpPointerOver = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+ cpDisabled = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+ cpFocused = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+ cpSelected = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+
+ rootPanel.Children.Append(cpNormal);
+ rootPanel.Children.Append(cpPressed);
+ rootPanel.Children.Append(cpPointerOver);
+ rootPanel.Children.Append(cpDisabled);
+ rootPanel.Children.Append(cpFocused);
+ rootPanel.Children.Append(cpSelected);
+
+ cpNormal.Header = "Normal";
+ cpPressed.Header = "Pressed";
+ cpPointerOver.Header = "PointerOver";
+ cpDisabled.Header = "Disabled";
+ cpFocused.Header = "Focused";
+ cpSelected.Header = "Selected";
+ });
+
+ helper.WaitForLoaded();
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ //cpNormal stays in common state
+ VisualStateManager.GoToState(cpPressed, "Pressed", true);
+ VisualStateManager.GoToState(cpPointerOver, "PointerOver", true);
+ VisualStateManager.GoToState(cpDisabled, "Disabled", true);
+ VisualStateManager.GoToState(cpFocused, "Focused", true);
+ VisualStateManager.GoToState(cpSelected, "Selected", true);
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ TestServices.Utilities.VerifyMockDCompOutput(MockDComp.SurfaceComparison.NoComparison);
+ }
+
+#if !WINDOWS_UWP
+ [TestMethod]
+ public async Task DonotResizeCalendarView()
+ {
+ TestCleanupWrapper cleanup;
+
+ TestServices.WindowHelper.SetWindowSizeOverride(new Size(400, 400));
+
+ Grid rootPanel = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel = Grid(XamlReader.Load(
+ " ")
+ );
+ rootPanel.Children.Append(cp);
+ global::Private.Infrastructure.TestServices.WindowHelper.WindowContent = rootPanel;
+ cp.IsCalendarOpen = true;
+ // there is not enough space to show the flyout, before this fix, the flyoutpresenter's content will be clipped
+ cp.HorizontalAlignment = HorizontalAlignment.Center;
+ cp.VerticalAlignment = VerticalAlignment.Center;
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ var popups = VisualTreeHelper.GetOpenPopupsForXamlRoot(rootPanel.XamlRoot);
+ VERIFY_ARE_EQUAL(popups.Count, 1);
+ var popup = popups.GetAt(0);
+ var presenter = FrameworkElement(popup.Child);
+ LOG_OUTPUT("actual height: %lf. expected height: 332.", presenter.ActualHeight);
+ // was 284 before this fix, the calendarview is clipped.
+ VERIFY_ARE_EQUAL(presenter.ActualHeight, 332);
+
+ cp.IsCalendarOpen = false;
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+#endif
+
+ [TestMethod]
+ public async Task CanPresetDate()
+ {
+ TestCleanupWrapper cleanup;
+
+ TestServices.WindowHelper.SetWindowSizeOverride(new Size(400, 400));
+
+ Grid rootPanel = null;
+
+ CalendarDatePickerHelper helper = new CalendarDatePickerHelper();
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = await helper.GetCalendarDatePicker();
+ CalendarView calendarView = null;
+ TextBlock dateText = null;
+
+ rootPanel = await CreateTestResources();
+ var date1 = ConvertToDateTime(1, 2000, 10, 21);
+ var date2 = ConvertToDateTime(1, 2003, 1, 1);
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ rootPanel.Children.Append(cp);
+ cp.Date = date1;
+ cp.IsCalendarOpen = true;
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ var root = Grid(helper.GetTemplateChild("Root"));
+ VERIFY_IS_NOT_NULL(root);
+
+ var flyout = FlyoutBase.GetAttachedFlyout(root);
+ VERIFY_IS_NOT_NULL(flyout);
+
+ var content = Flyout(flyout).Content;
+ calendarView = CalendarView(content);
+ VERIFY_IS_NOT_NULL(calendarView);
+
+ dateText = TextBlock(helper.GetTemplateChild("DateText"));
+
+ VERIFY_IS_NOT_NULL(dateText);
+ });
+
+ await RunOnUIThread(() =>
+ {
+ LOG_OUTPUT("actual text: %s.", dateText.Text);
+ // Note: below string contains invisible unicode characters (BiDi characters),
+ // you should always use copy&paste to get the string, directly
+ // type the string will cause the string comparison fails.
+ VERIFY_IS_TRUE(dateText.Text == "10/21/2000");
+
+ VERIFY_ARE_EQUAL(calendarView.SelectedDates.Count, 1);
+ VERIFY_ARE_EQUAL(calendarView.SelectedDates.GetAt(0).UniversalTime(), date1.UniversalTime());
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ cp.Date = null;
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ LOG_OUTPUT("actual text: %s.", dateText.Text);
+ // clear the Date property will display placehoder text.
+ VERIFY_IS_TRUE(dateText.Text == cp.PlaceholderText);
+
+ VERIFY_ARE_EQUAL(calendarView.SelectedDates.Count, 0);
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ cp.Date = date2;
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await RunOnUIThread(() =>
+ {
+ LOG_OUTPUT("actual text: %s.", dateText.Text);
+ // Note: below string contains invisible unicode characters (BiDi characters),
+ // you should always use copy&paste to get the string, directly
+ // type the string will cause the string comparison fails.
+ VERIFY_IS_TRUE(dateText.Text == "1/1/2003");
+
+ VERIFY_ARE_EQUAL(calendarView.SelectedDates.Count, 1);
+ VERIFY_ARE_EQUAL(calendarView.SelectedDates.GetAt(0).UniversalTime(), date2.UniversalTime());
+
+ cp.IsCalendarOpen = false;
+ });
+
+
+ await TestServices.WindowHelper.WaitForIdle();
+ }
+
+ [TestMethod]
+ public async Task VerifyTwoWayBinding()
+ {
+ TestCleanupWrapper cleanup;
+ StackPanel rootPanel = null;
+ Windows.UI.Xaml.Controls.CalendarDatePicker cdp1 = null;
+ Windows.UI.Xaml.Controls.CalendarDatePicker cdp2 = null;
+ var date1 = ConvertToDateTime(1, 2000, 1, 1);
+ var date2 = ConvertToDateTime(1, 2000, 1, 2);
+
+ await RunOnUIThread(() =>
+ {
+ rootPanel = StackPanel(XamlReader.Load(
+ " " +
+ " " +
+ " " +
+ ""
+ ));
+
+ global::Private.Infrastructure.TestServices.WindowHelper.WindowContent = rootPanel;
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ cdp1 = CalendarDatePicker(rootPanel.Children.GetAt(0));
+ cdp2 = CalendarDatePicker(rootPanel.Children.GetAt(1));
+ cdp1.Date = date1;
+ // due to Bug 5678196:{Binding} doesn't work on nullable properties when source is null
+ // we can't test the scenario that cdp1.Date is null
+ CalendarHelper.DumpDate(cdp1.Date.Value, "Changing cdp1.Date to");
+ CalendarHelper.DumpDate(cdp2.Date.Value, "Now cdp2.Date is");
+ VERIFY_ARE_EQUAL(cdp1.Date.Value.UniversalTime(), cdp2.Date.Value.UniversalTime());
+
+ cdp2.Date = date2;
+ CalendarHelper.DumpDate(cdp2.Date.Value, "Changing cdp2.Date to");
+ CalendarHelper.DumpDate(cdp1.Date.Value, "Now cdp1.Date is");
+ VERIFY_ARE_EQUAL(cdp1.Date.Value.UniversalTime(), cdp2.Date.Value.UniversalTime());
+ });
+
+ }
+
+ [TestMethod]
+ public async Task TestDateChangedEventWhenAssignDateToSameValue()
+ {
+ TestCleanupWrapper cleanup;
+ Grid rootPanel = null;
+ Windows.UI.Xaml.Controls.CalendarDatePicker cp = null;
+
+ rootPanel = await CreateTestResources();
+ var date = ConvertToDateTime(1, 2000, 1, 1);
+
+ // load into visual tree
+ await RunOnUIThread(() =>
+ {
+ cp = new Windows.UI.Xaml.Controls.CalendarDatePicker();
+ cp.Date = date;
+
+ rootPanel.Children.Append(cp);
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ var dateChangedEvent = new Event();
+ var dateChangedRegistration = CreateSafeEventRegistration>("DateChanged");
+
+ dateChangedRegistration.Attach(cp,
+ (sender, args) =>
+ {
+ dateChangedEvent.Set();
+ });
+
+ await RunOnUIThread(() =>
+ {
+ cp.Date = date;
+ });
+
+ await TestServices.WindowHelper.WaitForIdle();
+
+ // should not raise DateChanged event because we set the same date to cp.Date.
+ VERIFY_IS_FALSE(dateChangedEvent.HasFired());
+ }
+
+ [TestMethod]
+ public async Task ValidateOverlayBrush()
+ {
+ TestCleanupWrapper cleanup;
+
+ CalendarDatePicker calendarDatePicker = null;
+
+ await RunOnUIThread(() =>
+ {
+ calendarDatePicker = new CalendarDatePicker();
+ calendarDatePicker.LightDismissOverlayMode = LightDismissOverlayMode.On;
+
+ TestServices.WindowHelper.WindowContent = calendarDatePicker;
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+
+ TestServices.InputHelper.Tap(calendarDatePicker);
+ await TestServices.WindowHelper.WaitForIdle();
+
+ FlyoutBase flyout = null;
+ await RunOnUIThread(() =>
+ {
+ var root = FrameworkElement(VisualTreeHelper.GetChild(calendarDatePicker, 0));
+ flyout = FlyoutBase.GetAttachedFlyout(root);
+ THROW_IF_NULL_WITH_MSG(flyout, "An overlay element should exist for the flyout.");
+ });
+
+ FlyoutHelper.ValidateOpenFlyoutOverlayBrush("CalendarDatePickerLightDismissOverlayBackground");
+
+ FlyoutHelper.HideFlyout(flyout);
+ }
+
+ }
+}
+
+#endif
diff --git a/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendarview/CalendarViewAutomationPeerIntegrationTests.cs b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendarview/CalendarViewAutomationPeerIntegrationTests.cs
new file mode 100644
index 000000000000..368b4572cb47
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/IntegrationTests/dxaml/controls/calendarview/CalendarViewAutomationPeerIntegrationTests.cs
@@ -0,0 +1,744 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Windows.UI.Xaml.Automation.Peers;
+using Windows.UI.Xaml.Automation.Provider;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Tests.Common;
+using Windows.UI.Xaml.Tests.Enterprise;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Private.Infrastructure;
+using Uno.UI.RuntimeTests.MUX.Helpers;
+using Uno.UI.RuntimeTests.Helpers;
+
+using static Private.Infrastructure.TestServices;
+using static Private.Infrastructure.CalendarHelper;
+
+namespace Windows.UI.Tests.Enterprise.CalendarViewTests
+{
+ [TestClass]
+ [Ignore("Validating regressions only for now")]
+ public class CalendarViewAutomationPeerIntegrationTests : BaseDxamlTestClass
+ {
+ // PeerFromProvider is protected even though conceptually it's a static method.
+ // BridgeAutomationPeer is a little hack to allow us to call PeerFromProvider in our tests.
+ private class BridgeAutomationPeer : AutomationPeer
+ {
+ // public
+ internal AutomationPeer CallPeerFromProvider(IRawElementProviderSimple provider)
+ {
+ return PeerFromProvider(provider);
+ }
+ };
+
+ [ClassInitialize]
+ void ClassSetup()
+ {
+ CommonTestSetupHelper.CommonTestClassSetup();
+ }
+
+ [ClassCleanup]
+ void TestCleanup()
+ {
+ TestServices.WindowHelper.VerifyTestCleanup();
+ }
+
+ //
+ // Test Cases
+ //
+
+ //[TestMethod]
+ //public async Task VerifyUIATree()
+ //{
+ // TestCleanupWrapper cleanup;
+ // Grid rootPanel = null;
+
+ // var helper = new CalendarHelper.CalendarViewHelper();
+ // helper.PrepareLoadedEvent();
+ // CalendarView cv = await helper.GetCalendarView();
+
+ // AutomationPropertyChangedHandler spAutomationPropertyChangedEventHandler;
+ // Automation.AutomationClient.UIAElementInfo uiaInfo;
+ // uiaInfo.m_Name = "CalendarView";
+ // uiaInfo.m_AutomationID = "CalendarView";
+ // uiaInfo.m_cType = UIA_CalendarControlTypeId;
+
+ // IUIAutomationElement spUIAutomationElement;
+ // IUIAutomation spAutomation;
+ // IUIAutomationElement spAutomationHeaderElement;
+ // IUIAutomationInvokePattern spInvokePattern;
+
+ // CreateTestResources(rootPanel);
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.MinDate = ConvertToDateTime(1, 2000, 11, 15);
+ // cv.MaxDate = ConvertToDateTime(1, 2001, 1, 15);
+ // AutomationProperties.SetName(cv, uiaInfo.m_Name);
+ // rootPanel.Children.Add(cv);
+ // });
+
+ // helper.WaitForLoaded();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // var spAutomationClientManager =
+ // AutomationClient.AutomationClientManager.CreateAutomationClientManagerFromInfo(uiaInfo);
+ // spAutomationClientManager.GetCurrentUIAutomationElement(spUIAutomationElement);
+ // VERIFY_IS_NOT_NULL(spUIAutomationElement);
+
+ // spAutomationClientManager.GetAutomation(spAutomation);
+ // VERIFY_IS_NOT_NULL(spAutomation);
+
+ // wrl.ComPtr spUIAutomationMonthViewSVCondition;
+ // Common.AutoVariant autoMonthViewSV;
+ // autoMonthViewSV.SetString("MonthViewScrollViewer");
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoMonthViewSV.Storage()), spUIAutomationMonthViewSVCondition));
+
+ // wrl.ComPtr spAutomationMonthViewSVElement;
+ // VERIFY_SUCCEEDED(spUIAutomationElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationMonthViewSVCondition, spAutomationMonthViewSVElement));
+ // VERIFY_IS_NOT_NULL(spAutomationMonthViewSVElement);
+
+ // wrl.ComPtr spUIAutomationDatesCondition;
+ // Common.AutoVariant autoVarType;
+ // autoVarType.SetInt(UIA_DataItemControlTypeId);
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId,
+ // *(autoVarType.Storage()), spUIAutomationDatesCondition));
+ // wrl.ComPtr spDateItems;
+ // VERIFY_SUCCEEDED(spAutomationMonthViewSVElement.FindAll(TreeScope.TreeScope_Children,
+ // spUIAutomationDatesCondition, spDateItems));
+ // int size = 0;
+ // size = VERIFY_SUCCEEDED(spDateItems.Length);
+ // WEX.Logging.Log.Comment("Visible Dates are less than or equal to 42");
+ // VERIFY_IS_LESS_THAN_OR_EQUAL(size, 42);
+
+ // wrl.ComPtr spUIAutomationAutomationIDCondition;
+ // Common.AutoVariant autoHeaderButton;
+ // autoHeaderButton.SetString("HeaderButton");
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoHeaderButton.Storage()), spUIAutomationAutomationIDCondition));
+
+ // VERIFY_SUCCEEDED(spUIAutomationElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationAutomationIDCondition, spAutomationHeaderElement));
+ // VERIFY_IS_NOT_NULL(spAutomationHeaderElement);
+
+ // VERIFY_SUCCEEDED(spAutomationHeaderElement.GetCurrentPatternAs(UIA_InvokePatternId,
+ // __uuidof(IUIAutomationInvokePattern), spInvokePattern));
+ // VERIFY_IS_NOT_NULL(spInvokePattern);
+
+ // VERIFY_SUCCEEDED(spInvokePattern.Invoke());
+ // });
+
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // wrl.ComPtr spUIAutomationYearViewSVCondition;
+ // Common.AutoVariant autoYearViewSV;
+ // autoYearViewSV.SetString("YearViewScrollViewer");
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoYearViewSV.Storage()), spUIAutomationYearViewSVCondition));
+
+ // wrl.ComPtr spAutomationYearViewSVElement;
+ // VERIFY_SUCCEEDED(spUIAutomationElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationYearViewSVCondition, spAutomationYearViewSVElement));
+ // VERIFY_IS_NOT_NULL(spAutomationYearViewSVElement);
+
+ // wrl.ComPtr spUIAutomationDatesCondition;
+ // Common.AutoVariant autoVarType;
+ // autoVarType.SetInt(UIA_ButtonControlTypeId);
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId,
+ // *(autoVarType.Storage()), spUIAutomationDatesCondition));
+ // wrl.ComPtr spDateItems;
+ // VERIFY_SUCCEEDED(spAutomationYearViewSVElement.FindAll(TreeScope.TreeScope_Children,
+ // spUIAutomationDatesCondition, spDateItems));
+ // int size = 0;
+ // size = VERIFY_SUCCEEDED(spDateItems.Length);
+ // WEX.Logging.Log.Comment("There are 3 month in the view between 2000-11-15 and 2001-1-15");
+ // VERIFY_ARE_EQUAL(size, 3);
+
+ // VERIFY_SUCCEEDED(spInvokePattern.Invoke());
+ // });
+
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // wrl.ComPtr spUIAutomationDecadeViewSVCondition;
+ // Common.AutoVariant autoDecadeViewSV;
+ // autoDecadeViewSV.SetString("DecadeViewScrollViewer");
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoDecadeViewSV.Storage()), spUIAutomationDecadeViewSVCondition));
+
+ // wrl.ComPtr spAutomationDecadeViewSVElement;
+ // VERIFY_SUCCEEDED(spUIAutomationElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationDecadeViewSVCondition, spAutomationDecadeViewSVElement));
+ // VERIFY_IS_NOT_NULL(spAutomationDecadeViewSVElement);
+
+ // wrl.ComPtr spUIAutomationDatesCondition;
+ // Common.AutoVariant autoVarType;
+ // autoVarType.SetInt(UIA_ButtonControlTypeId);
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId,
+ // *(autoVarType.Storage()), spUIAutomationDatesCondition));
+ // wrl.ComPtr spDateItems;
+ // VERIFY_SUCCEEDED(spAutomationDecadeViewSVElement.FindAll(TreeScope.TreeScope_Children,
+ // spUIAutomationDatesCondition, spDateItems));
+ // int size = 0;
+ // size = VERIFY_SUCCEEDED(spDateItems.Length);
+ // WEX.Logging.Log.Comment("There are 2 years in the view between 2000-11-15 and 2001-1-15");
+ // VERIFY_ARE_EQUAL(size, 2);
+ // });
+ //}
+
+ //[TestMethod]
+ //public async Task VerifyElementPatterns()
+ //{
+ // TestCleanupWrapper cleanup;
+ // Grid rootPanel = null;
+
+ // CalendarHelper.CalendarViewHelper helper = new CalendarHelper.CalendarViewHelper();
+ // helper.PrepareLoadedEvent();
+ // CalendarView cv = ConfiguredTaskAwaitable<> helper.GetCalendarView();
+
+ // AutomationClient.AutomationPropertyChangedHandler <
+ // 3 >> spAutomationPropertyChangedEventHandler;
+ // Automation.AutomationClient.UIAElementInfo uiaInfo;
+ // uiaInfo.m_Name = "CalendarView";
+ // uiaInfo.m_AutomationID = "CalendarView";
+ // uiaInfo.m_cType = UIA_CalendarControlTypeId;
+
+ // wrl.ComPtr spUIAutomationElement;
+ // wrl.ComPtr spAutomation;
+ // wrl.ComPtr spAutomationHeaderElement;
+ // wrl.ComPtr spSelectionItemPattern;
+ // Windows.Foundation.DateTime testdate = ConvertToDateTime(1, 2000, 11, 15);
+ // CreateTestResources(rootPanel);
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.MinDate = ConvertToDateTime(1, 2000, 11, 15);
+ // cv.MaxDate = ConvertToDateTime(1, 2001, 1, 15);
+ // AutomationProperties.SetName(cv, uiaInfo.m_Name);
+ // rootPanel.Children.Add(cv);
+ // });
+
+ // helper.WaitForLoaded();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // var spAutomationClientManager =
+ // AutomationClient.AutomationClientManager.CreateAutomationClientManagerFromInfo(uiaInfo);
+ // spAutomationClientManager.GetCurrentUIAutomationElement(spUIAutomationElement);
+ // VERIFY_IS_NOT_NULL(spUIAutomationElement);
+
+ // spAutomationClientManager.GetAutomation(spAutomation);
+ // VERIFY_IS_NOT_NULL(spAutomation);
+ // });
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.SetDisplayDate(ConvertToDateTime(1, 2000, 11, 15));
+ // cv.UpdateLayout();
+ // });
+
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // wrl.ComPtr spUIAutomationMonthViewSVCondition;
+ // Common.AutoVariant autoMonthViewSV;
+ // autoMonthViewSV.SetString("MonthViewScrollViewer");
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoMonthViewSV.Storage()), spUIAutomationMonthViewSVCondition));
+
+ // wrl.ComPtr spAutomationMonthViewSVElement;
+ // VERIFY_SUCCEEDED(spUIAutomationElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationMonthViewSVCondition, spAutomationMonthViewSVElement));
+ // VERIFY_IS_NOT_NULL(spAutomationMonthViewSVElement);
+
+ // wrl.ComPtr spUIAutomationDatesCondition;
+ // Common.AutoVariant autoVarType;
+ // autoVarType.SetInt(UIA_DataItemControlTypeId);
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId,
+ // *(autoVarType.Storage()), spUIAutomationDatesCondition));
+ // wrl.ComPtr spAutomationFirstElement;
+ // VERIFY_SUCCEEDED(spAutomationMonthViewSVElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationDatesCondition, spAutomationFirstElement));
+
+ // wrl.ComPtr spGridItemPattern;
+ // VERIFY_SUCCEEDED(spAutomationFirstElement.GetCurrentPatternAs(UIA_GridItemPatternId,
+ // __uuidof(IUIAutomationGridItemPattern), spGridItemPattern));
+ // VERIFY_IS_NOT_NULL(spGridItemPattern);
+
+ // int col = 0, row = 0;
+ // col = VERIFY_SUCCEEDED(spGridItemPattern.CurrentColumn);
+ // WEX.Logging.Log.Comment("2000-11-15 is on Wednesday");
+ // VERIFY_ARE_EQUAL(col, 3);
+ // row = VERIFY_SUCCEEDED(spGridItemPattern.CurrentRow);
+ // VERIFY_ARE_EQUAL(row, 0);
+
+ // wrl.ComPtr spTableItemPattern;
+ // VERIFY_SUCCEEDED(spAutomationFirstElement.GetCurrentPatternAs(UIA_TableItemPatternId,
+ // __uuidof(IUIAutomationTableItemPattern), spTableItemPattern));
+ // VERIFY_IS_NOT_NULL(spTableItemPattern);
+
+ // wrl.ComPtr spRowHeaderItems;
+ // VERIFY_SUCCEEDED(spTableItemPattern.GetCurrentRowHeaderItems(spRowHeaderItems));
+
+ // int rowSize = 0;
+ // rowSize = VERIFY_SUCCEEDED(spRowHeaderItems.Length);
+ // VERIFY_ARE_EQUAL(rowSize, 1);
+
+ // wrl.ComPtr spRowHeaderAutomationElement;
+ // VERIFY_SUCCEEDED(spRowHeaderItems.GetElement(0, spRowHeaderAutomationElement));
+ // VERIFY_IS_NOT_NULL(spRowHeaderAutomationElement);
+
+ // AutoBSTR rowHeaderElementName;
+ // VERIFY_SUCCEEDED(spRowHeaderAutomationElement.get_CurrentName(rowHeaderElementName));
+ // Common.AutoBSTR.VerifyAreEqual("\u200eNovember\u200e \u200e2000", rowHeaderElementName);
+
+ // wrl.ComPtr spHeaderItems;
+ // VERIFY_SUCCEEDED(spTableItemPattern.GetCurrentColumnHeaderItems(spHeaderItems));
+
+ // int size = 0;
+ // size = VERIFY_SUCCEEDED(spHeaderItems.Length);
+ // VERIFY_ARE_EQUAL(size, 1);
+
+ // wrl.ComPtr spAutomationHeaderElement;
+ // VERIFY_SUCCEEDED(spHeaderItems.GetElement(0, spAutomationHeaderElement));
+ // VERIFY_IS_NOT_NULL(spAutomationHeaderElement);
+
+ // AutoBSTR elementName;
+ // VERIFY_SUCCEEDED(spAutomationHeaderElement.get_CurrentName(elementName));
+ // WEX.Logging.Log.Comment("2000-11-15 is on Wednesday");
+ // VERIFY_ARE_EQUAL(wcscmp(elementName, "Wednesday"), 0);
+
+ // VERIFY_SUCCEEDED(spAutomationFirstElement.GetCurrentPatternAs(UIA_SelectionItemPatternId,
+ // __uuidof(IUIAutomationSelectionItemPattern), spSelectionItemPattern));
+ // VERIFY_IS_NOT_NULL(spSelectionItemPattern);
+
+ // BOOL isSelected = true;
+ // isSelected = VERIFY_SUCCEEDED(spSelectionItemPattern.CurrentIsSelected);
+ // VERIFY_IS_false(!!isSelected);
+ // });
+
+ // helper.PrepareSelectedDatesChangedEvent();
+ // helper.ExpectAddedDate(testdate);
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // VERIFY_SUCCEEDED(spSelectionItemPattern.Select());
+ // });
+ // helper.WaitForSelectedDatesChanged();
+
+
+ // helper.PrepareSelectedDatesChangedEvent();
+ // helper.ExpectRemovedDate(testdate);
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // VERIFY_SUCCEEDED(spSelectionItemPattern.RemoveFromSelection());
+ // });
+ // helper.WaitForSelectedDatesChanged();
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.SelectedDates.Add(testdate);
+ // });
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // BOOL isSelected = true;
+ // isSelected = VERIFY_SUCCEEDED(spSelectionItemPattern.CurrentIsSelected);
+ // VERIFY_IS_true(!!isSelected);
+ // });
+ //}
+
+
+ //[TestMethod]
+ //public async Task VerifySelectionChangedEvent()
+ //{
+ // TestCleanupWrapper cleanup;
+ // Grid rootPanel = null;
+
+ // CalendarHelper.CalendarViewHelper helper = new CalendarViewHelper();
+ // helper.PrepareLoadedEvent();
+ // CalendarView cv = await helper.GetCalendarView();
+
+ // AutomationClient.AutomationPropertyChangedHandler <
+ // 3 >> spAutomationPropertyChangedEventHandler;
+ // Automation.AutomationClient.UIAElementInfo uiaInfo;
+ // uiaInfo.m_Name = "CalendarView";
+ // uiaInfo.m_AutomationID = "CalendarView";
+ // uiaInfo.m_cType = UIA_CalendarControlTypeId;
+
+ // wrl.ComPtr spUIAutomationElement;
+ // wrl.ComPtr spAutomation;
+ // wrl.ComPtr spAutomationHeaderElement;
+ // wrl.ComPtr spSelectionItemPattern;
+ // CreateTestResources(rootPanel);
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.MinDate = ConvertToDateTime(1, 2000, 11, 15);
+ // cv.MaxDate = ConvertToDateTime(1, 2001, 1, 15);
+ // AutomationProperties.SetName(cv, uiaInfo.m_Name);
+ // rootPanel.Children.Add(cv);
+ // });
+
+ // helper.WaitForLoaded();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.SetDisplayDate(ConvertToDateTime(1, 2000, 11, 15));
+ // cv.UpdateLayout();
+ // });
+
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // var spAutomationClientManager =
+ // AutomationClient.AutomationClientManager.CreateAutomationClientManagerFromInfo(uiaInfo);
+ // spAutomationClientManager.GetCurrentUIAutomationElement(spUIAutomationElement);
+ // VERIFY_IS_NOT_NULL(spUIAutomationElement);
+
+ // spAutomationClientManager.GetAutomation(spAutomation);
+ // VERIFY_IS_NOT_NULL(spAutomation);
+
+ // wrl.ComPtr spUIAutomationMonthViewSVCondition;
+ // Common.AutoVariant autoMonthViewSV;
+ // autoMonthViewSV.SetString("MonthViewScrollViewer");
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoMonthViewSV.Storage()), spUIAutomationMonthViewSVCondition));
+
+ // wrl.ComPtr spAutomationMonthViewSVElement;
+ // VERIFY_SUCCEEDED(spUIAutomationElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationMonthViewSVCondition, spAutomationMonthViewSVElement));
+ // VERIFY_IS_NOT_NULL(spAutomationMonthViewSVElement);
+
+ // wrl.ComPtr spUIAutomationDatesCondition;
+ // Common.AutoVariant autoVarType;
+ // autoVarType.SetInt(UIA_DataItemControlTypeId);
+ // VERIFY_SUCCEEDED(spAutomation.CreatePropertyCondition(UIA_ControlTypePropertyId,
+ // *(autoVarType.Storage()), spUIAutomationDatesCondition));
+ // wrl.ComPtr spAutomationFirstElement;
+ // VERIFY_SUCCEEDED(spAutomationMonthViewSVElement.FindFirst(TreeScope.TreeScope_Children,
+ // spUIAutomationDatesCondition, spAutomationFirstElement));
+
+ // wrl.ComPtr spSelectionItemPattern;
+ // VERIFY_SUCCEEDED(spAutomationFirstElement.GetCurrentPatternAs(UIA_SelectionItemPatternId,
+ // __uuidof(IUIAutomationSelectionItemPattern), spSelectionItemPattern));
+ // VERIFY_IS_NOT_NULL(spSelectionItemPattern);
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // cv.SelectionMode = CalendarViewSelectionMode.Multiple;
+ // });
+ // var spEvent = new Event();
+ // wrl.ComPtr spAutomationPatternHandler;
+
+ // spAutomationPatternHandler.Attach(new Patterns.SelectionItemPatternHandler(spAutomationClientManager,
+ // spEvent, TreeScope_Subtree, UIA_SelectionItem_ElementSelectedEventId));
+ // spAutomationPatternHandler.AttachEventHandler();
+ // Windows.Foundation.DateTime testdate = ConvertToDateTime(1, 2000, 11, 15);
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // WEX.Logging.Log.Comment("Selecting Date 2000-11-15");
+ // cv.SelectedDates.Add(testdate);
+ // });
+ // spAutomationPatternHandler.ConfirmAndUnregister();
+ // WEX.Logging.Log.Comment("UIA_SelectionItem_ElementSelectedEventId Recieved");
+
+ // spAutomationPatternHandler = null;
+
+ // spAutomationPatternHandler.Attach(new Patterns.SelectionItemPatternHandler(spAutomationClientManager,
+ // spEvent, TreeScope_Subtree, UIA_SelectionItem_ElementAddedToSelectionEventId));
+ // spAutomationPatternHandler.AttachEventHandler();
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // testdate = ConvertToDateTime(1, 2000, 11, 16);
+ // WEX.Logging.Log.Comment("Selecting Date 2000-11-16");
+ // cv.SelectedDates.Add(testdate);
+ // });
+ // spAutomationPatternHandler.ConfirmAndUnregister();
+ // WEX.Logging.Log.Comment("UIA_SelectionItem_ElementAddedToSelectionEventId Recieved");
+
+ // spAutomationPatternHandler = null;
+
+ // spAutomationPatternHandler.Attach(new Patterns.SelectionItemPatternHandler(spAutomationClientManager,
+ // spEvent, TreeScope_Subtree, UIA_SelectionItem_ElementRemovedFromSelectionEventId));
+ // spAutomationPatternHandler.AttachEventHandler();
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // WEX.Logging.Log.Comment("Removed one selection");
+ // cv.SelectedDates.RemoveAt(0);
+ // });
+ // spAutomationPatternHandler.ConfirmAndUnregister();
+ // WEX.Logging.Log.Comment("UIA_SelectionItem_ElementRemovedFromSelectionEventId Recieved");
+
+
+ // spAutomationPatternHandler = null;
+
+ // spAutomationPatternHandler.Attach(new Patterns.SelectionItemPatternHandler(spAutomationClientManager,
+ // spEvent, TreeScope_Subtree, UIA_SelectionItem_ElementRemovedFromSelectionEventId));
+ // spAutomationPatternHandler.AttachEventHandler();
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // WEX.Logging.Log.Comment("Removed last selection");
+ // cv.SelectedDates.RemoveAt(0);
+ // });
+ // spAutomationPatternHandler.ConfirmAndUnregister();
+ // WEX.Logging.Log.Comment("UIA_SelectionItem_ElementRemovedFromSelectionEventId Recieved");
+ // });
+ //}
+
+ [TestMethod]
+ public async Task VerifyDayItemRowHeaders()
+ {
+ TestCleanupWrapper cleanup;
+ CalendarView calendar = default;
+
+ await TestServices.RunOnUIThread(() =>
+ {
+ calendar = new CalendarView();
+ calendar.SetDisplayDate(ConvertToDateTime(1, 2016, 04, 01));
+ TestServices.WindowHelper.WindowContent = calendar;
+ });
+ await TestServices.WindowHelper.WaitForIdle();
+
+ await TestServices.RunOnUIThread(() =>
+ {
+ var dayItems = new List();
+ TreeHelper.GetVisualChildrenByType(calendar, ref dayItems);
+
+ // So since there is no mapping APIs on CalendarView, to get to the month in view, we divide by 2
+ // To get to the previous/next months, we remove/add 30 days.
+ var marchDay = dayItems.GetAt((int)(dayItems.Count * 0.5 - 30));
+ var aprilDay = dayItems.GetAt((int)(dayItems.Count * 0.5));
+ var mayDay = dayItems.GetAt((int)(dayItems.Count * 0.5 + 30));
+
+ // We want to validate we get different peers for day items that belong to different months.
+ var marchDayPeer = FrameworkElementAutomationPeer.CreatePeerForElement(marchDay);
+ var aprilDayPeer = FrameworkElementAutomationPeer.CreatePeerForElement(aprilDay);
+ var mayDayPeer = FrameworkElementAutomationPeer.CreatePeerForElement(mayDay);
+
+ var marchRowHeaders = ((ITableItemProvider )marchDayPeer.GetPattern(
+ PatternInterface.TableItem)).GetRowHeaderItems();
+ var aprilRowHeaders = ((ITableItemProvider )aprilDayPeer.GetPattern(
+ PatternInterface.TableItem)).GetRowHeaderItems();
+ var mayRowHeaders = ((ITableItemProvider )mayDayPeer.GetPattern(
+ PatternInterface.TableItem)).GetRowHeaderItems();
+
+ VERIFY_ARE_EQUAL(1, marchRowHeaders.Length);
+ VERIFY_ARE_EQUAL(1, aprilRowHeaders.Length);
+ VERIFY_ARE_EQUAL(1, mayRowHeaders.Length);
+
+ var bridge = new BridgeAutomationPeer();
+ VERIFY_ARE_EQUAL("\u200eMarch\u200e \u200e2016",
+ bridge.CallPeerFromProvider(marchRowHeaders[0]).GetName());
+ VERIFY_ARE_EQUAL("\u200eApril\u200e \u200e2016",
+ bridge.CallPeerFromProvider(aprilRowHeaders[0]).GetName());
+ VERIFY_ARE_EQUAL("\u200eMay\u200e \u200e2016",
+ bridge.CallPeerFromProvider(mayRowHeaders[0]).GetName());
+ });
+ }
+
+ //[TestMethod]
+ //public async Task VerifyOutOfViewItemsDoNotSupportGridItemPattern()
+ //{
+ // TestCleanupWrapper cleanup;
+
+ // Automation.AutomationClient.UIAElementInfo uiaInfo;
+ // uiaInfo.m_Name = "CalendarView";
+ // uiaInfo.m_AutomationID = "CalendarView";
+ // uiaInfo.m_cType = UIA_CalendarControlTypeId;
+
+ // CalendarView calendarView = null;
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // calendarView = new CalendarView();
+ // calendarView.SetDisplayDate(ConvertToDateTime(1, 2016, 04, 01));
+
+ // AutomationProperties.SetName(calendarView, uiaInfo.m_Name);
+
+ // TestServices.WindowHelper.WindowContent = calendarView;
+ // });
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // await TestServices.RunOnUIThread(() =>
+ // {
+ // calendarView.Focus(FocusState.Keyboard);
+ // });
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // LOG_OUTPUT("Get the automation element corresponding to the first visible day item.");
+ // wrl.ComPtr monthScrollViewerChildElement;
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // wrl.ComPtr automationElement;
+
+ // var automationClientManager =
+ // AutomationClient.AutomationClientManager.CreateAutomationClientManagerFromInfo(uiaInfo);
+ // automationClientManager.GetCurrentUIAutomationElement(automationElement);
+
+ // wrl.ComPtr automation;
+ // automationClientManager.GetAutomation(automation);
+
+ // wrl.ComPtr monthViewScrollViewerCondition;
+ // Common.AutoVariant autoMonthViewSV;
+ // autoMonthViewSV.SetString("MonthViewScrollViewer");
+ // LogThrow_IfFailed(automation.CreatePropertyCondition(UIA_AutomationIdPropertyId,
+ // *(autoMonthViewSV.Storage()), monthViewScrollViewerCondition));
+
+ // wrl.ComPtr monthViewScrollViewerElement;
+ // LogThrow_IfFailed(automationElement.FindFirst(TreeScope.TreeScope_Children,
+ // monthViewScrollViewerCondition, monthViewScrollViewerElement));
+
+ // wrl.ComPtr trueCondition;
+ // LogThrow_IfFailed(automation.CreateTrueCondition(trueCondition));
+ // LogThrow_IfFailed(monthViewScrollViewerElement.FindFirst(TreeScope.TreeScope_Children, trueCondition,
+ // &monthScrollViewerChildElement));
+ // });
+
+ // LOG_OUTPUT("Tab past the month header button.");
+ // TestServices.KeyboardHelper.Tab();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // LOG_OUTPUT("Tab past the previous month button.");
+ // TestServices.KeyboardHelper.Tab();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // LOG_OUTPUT("Tab past the next month button.");
+ // TestServices.KeyboardHelper.Tab();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // LOG_OUTPUT(
+ // "Focus is now on a day item. Press PageDown to change the month and move our previously queried automation element out of view.");
+ // TestServices.KeyboardHelper.PageDown();
+ // await TestServices.WindowHelper.WaitForIdle();
+
+ // LOG_OUTPUT(
+ // "Attempt to query for the grid item pattern on our previously queried automation element, which should return null.");
+ // UIAutomationHelper.RunOnCorrectThreadForUIA(() =>
+ // {
+ // wrl.ComPtr gridItemPattern;
+ // LogThrow_IfFailed(monthScrollViewerChildElement.GetCurrentPatternAs(UIA_GridItemPatternId,
+ // __uuidof(IUIAutomationGridItemPattern), gridItemPattern));
+
+ // VERIFY_IS_null(gridItemPattern);
+ // });
+ //}
+
+ //[TestMethod]
+ //public async Task VerifyAutomationNotificationEventAfterClickingNavigationButton()
+ //{
+ // TestCleanupWrapper cleanup;
+
+ // Automation.AutomationClient.UIAElementInfo uiaInfo;
+ // uiaInfo.m_Name = "CalendarView";
+ // uiaInfo.m_AutomationID = "CalendarView";
+ // uiaInfo.m_cType = UIA_CalendarControlTypeId;
+
+ // CalendarView calendarView = null;
+ // Button previousButton = null;
+ // Button nextButton = null;
+
+ // var loadedEvent = new Event();
+ // var previousButtonClickedEvent = new Event();
+ // var nextButtonClickedEvent = new Event();
+
+ // var loadedRegistration = CreateSafeEventRegistration("Loaded");
+ // var previousButtonClickRegistration = CreateSafeEventRegistration