From b71b5403f036655f8554712be84c6d609c1c47e9 Mon Sep 17 00:00:00 2001
From: michael-hawker <24302614+michael-hawker@users.noreply.github.com>
Date: Wed, 15 Jun 2022 19:43:47 -0700
Subject: [PATCH 01/13] Add initial touch injection API test
See comments in https://github.com/CommunityToolkit/Labs-Windows/issues/124
---
.../App.xaml.cs | 2 +
...mmunityToolkit.Labs.Tests.Shared.projitems | 1 +
.../SimulateInput.cs | 163 ++++++++++++++++++
.../ExampleSizerBaseTestClass.cs | 28 +++
.../SizerBase.Tests/SizerBase.Tests.projitems | 7 +
.../SizerBase.Tests/TouchInjectionTest.xaml | 18 ++
.../TouchInjectionTest.xaml.cs | 43 +++++
7 files changed, 262 insertions(+)
create mode 100644 common/CommunityToolkit.Labs.Tests.Shared/SimulateInput.cs
create mode 100644 labs/SizerBase/tests/SizerBase.Tests/TouchInjectionTest.xaml
create mode 100644 labs/SizerBase/tests/SizerBase.Tests/TouchInjectionTest.xaml.cs
diff --git a/common/CommunityToolkit.Labs.Tests.Shared/App.xaml.cs b/common/CommunityToolkit.Labs.Tests.Shared/App.xaml.cs
index 1b12ef5c4..a26f0aa0e 100644
--- a/common/CommunityToolkit.Labs.Tests.Shared/App.xaml.cs
+++ b/common/CommunityToolkit.Labs.Tests.Shared/App.xaml.cs
@@ -42,6 +42,8 @@ public sealed partial class App : Application
private static Windows.UI.Xaml.Window currentWindow = Windows.UI.Xaml.Window.Current;
#endif
+ public static Rect Bounds => currentWindow.Bounds;
+
// Holder for test content to abstract Window.Current.Content
public static FrameworkElement? ContentRoot
{
diff --git a/common/CommunityToolkit.Labs.Tests.Shared/CommunityToolkit.Labs.Tests.Shared.projitems b/common/CommunityToolkit.Labs.Tests.Shared/CommunityToolkit.Labs.Tests.Shared.projitems
index 9dad91eb3..bac707d91 100644
--- a/common/CommunityToolkit.Labs.Tests.Shared/CommunityToolkit.Labs.Tests.Shared.projitems
+++ b/common/CommunityToolkit.Labs.Tests.Shared/CommunityToolkit.Labs.Tests.Shared.projitems
@@ -19,6 +19,7 @@
App.xaml
+
\ No newline at end of file
diff --git a/common/CommunityToolkit.Labs.Tests.Shared/SimulateInput.cs b/common/CommunityToolkit.Labs.Tests.Shared/SimulateInput.cs
new file mode 100644
index 000000000..82e7c6d7e
--- /dev/null
+++ b/common/CommunityToolkit.Labs.Tests.Shared/SimulateInput.cs
@@ -0,0 +1,163 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Windows.UI.Input.Preview.Injection;
+using Windows.Foundation;
+
+#if !WINAPPSDK
+using Windows.UI.Input;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Media;
+#else
+using Microsoft.UI.Input;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Media;
+#endif
+
+namespace CommunityToolkit.Labs.UnitTests;
+
+public static class SimulateInput
+{
+ private static InputInjector _input = InputInjector.TryCreate();
+ private static uint _currentPointerId = 0;
+
+ public static void StartTouch()
+ {
+ Assert.IsNotNull(_input);
+
+ _input.InitializeTouchInjection(
+ InjectedInputVisualizationMode.Default);
+ }
+
+ ///
+ /// Simulates a touch press on screen at the coordinates provided, in app-local coordinates. For instance use App.ContentRoot.CoordinatesTo(element)
.
+ ///
+ ///
+ ///
+ public static uint TouchDown(Point point)
+ {
+ // Create a unique pointer ID for the injected touch pointer.
+ // Multiple input pointers would require more robust handling.
+ uint pointerId = _currentPointerId++;
+
+ var injectionPoint = TranslatePointForWindow(point);
+
+ // Create a touch data point for pointer down.
+ // Each element in the touch data list represents a single touch contact.
+ // For this example, we're mirroring a single mouse pointer.
+ List touchData = new()
+ {
+ new()
+ {
+ Contact = new InjectedInputRectangle
+ {
+ Left = 30, Top = 30, Bottom = 30, Right = 30
+ },
+ PointerInfo = new InjectedInputPointerInfo
+ {
+ PointerId = pointerId,
+ PointerOptions =
+ InjectedInputPointerOptions.PointerDown |
+ InjectedInputPointerOptions.InContact |
+ InjectedInputPointerOptions.New,
+ TimeOffsetInMilliseconds = 0,
+ PixelLocation = new InjectedInputPoint
+ {
+ PositionX = (int)injectionPoint.X ,
+ PositionY = (int)injectionPoint.Y
+ }
+ },
+ Pressure = 1.0,
+ TouchParameters =
+ InjectedInputTouchParameters.Pressure |
+ InjectedInputTouchParameters.Contact
+ }
+ };
+
+ // Inject the touch input.
+ _input.InjectTouchInput(touchData);
+
+ return pointerId;
+ }
+
+ public static void TouchMove(uint pointerId, int cX, int cY)
+ {
+ // Create a touch data point for pointer up.
+ List touchData = new()
+ {
+ new()
+ {
+ Contact = new InjectedInputRectangle
+ {
+ Left = 30, Top = 30, Bottom = 30, Right = 30
+ },
+ PointerInfo = new InjectedInputPointerInfo
+ {
+ PointerId = pointerId,
+ PointerOptions =
+ InjectedInputPointerOptions.InRange |
+ InjectedInputPointerOptions.InContact,
+ TimeOffsetInMilliseconds = 0,
+ PixelLocation = new InjectedInputPoint
+ {
+ PositionX = (int)cX ,
+ PositionY = (int)cY
+ }
+ },
+ Pressure = 1.0,
+ TouchParameters =
+ InjectedInputTouchParameters.Pressure |
+ InjectedInputTouchParameters.Contact
+ }
+ };
+
+ // Inject the touch input.
+ _input.InjectTouchInput(touchData);
+ }
+
+ public static void TouchUp(uint pointerId)
+ {
+ Assert.IsNotNull(_input);
+
+ // Create a touch data point for pointer up.
+ List touchData = new()
+ {
+ new()
+ {
+ PointerInfo = new InjectedInputPointerInfo
+ {
+ PointerId = pointerId,
+ PointerOptions = InjectedInputPointerOptions.PointerUp
+ }
+ }
+ };
+
+ // Inject the touch input.
+ _input.InjectTouchInput(touchData);
+ }
+
+ public static void StopTouch()
+ {
+ // Shut down the virtual input device.
+ _input.UninitializeTouchInjection();
+ }
+
+ private static Point TranslatePointForWindow(Point point)
+ {
+ // TODO: Do we want a ToPoint extension in the Toolkit? (is there an existing enum we can use to specify which corner/point of the rect? e.g. topleft, center, middleright, etc...?
+
+ // Get the top left screen coordinates of the app window rect.
+ Point appBoundsTopLeft = new Point(App.Bounds.Left, App.Bounds.Top);
+
+ // Create the point for input injection and calculate its screen location.
+ return new Point(
+ appBoundsTopLeft.X + point.X,
+ appBoundsTopLeft.Y + point.Y);
+
+ }
+}
diff --git a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs
index 28d0237ca..b7cd30c66 100644
--- a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs
+++ b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs
@@ -57,4 +57,32 @@ public void PropertySizer_TestChangeBinding(PropertySizerTestInitialBinding test
// Set in XAML Page LINK: PropertySizerTestInitialBinding.xaml#L14
Assert.AreEqual(200, propertySizer.Binding, "Property Sizer not at expected changed value.");
}
+
+ [LabsUITestMethod]
+ public async Task InputInjection_TestClickButton(TouchInjectionTest testControl)
+ {
+ var button = testControl.FindDescendant