mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
Merge branch 'feature/UITestAutomation' into dev/yaqingmi/ui-automation
This commit is contained in:
@@ -57,3 +57,4 @@ stages:
|
||||
platform: ${{ platform }}
|
||||
configuration: Release
|
||||
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
|
||||
isUIAutomationPipeline: true
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
using ABI.Windows.Foundation;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
@@ -199,6 +200,42 @@ namespace Microsoft.PowerToys.UITest
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates holding a key, clicking and dragging a UI element to the specified screen coordinates.
|
||||
/// </summary>
|
||||
/// <param name="key">The keyboard key to press and hold during the drag operation.</param>
|
||||
/// <param name="targetX">The target X-coordinate to drag the element to.</param>
|
||||
/// <param name="targetY">The target Y-coordinate to drag the element to.</param>
|
||||
public void KeyDownAndDrag(Key key, int targetX, int targetY)
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
KeyboardHelper.PressKey(key);
|
||||
|
||||
actions.MoveToElement(windowsElement)
|
||||
.ClickAndHold()
|
||||
.Perform();
|
||||
|
||||
int dx = targetX - windowElement.Rect.X;
|
||||
int dy = targetY - windowElement.Rect.Y;
|
||||
|
||||
int stepCount = 10;
|
||||
int stepX = dx / stepCount;
|
||||
int stepY = dy / stepCount;
|
||||
|
||||
for (int i = 0; i < stepCount; i++)
|
||||
{
|
||||
var stepAction = new Actions(driver);
|
||||
stepAction.MoveByOffset(stepX, stepY).Perform();
|
||||
}
|
||||
|
||||
var releaseAction = new Actions(driver);
|
||||
releaseAction.Release().Perform();
|
||||
|
||||
KeyboardHelper.ReleaseKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute value of the UI element.
|
||||
/// </summary>
|
||||
@@ -365,15 +402,10 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// Save UI Element to a PNG file.
|
||||
/// </summary>
|
||||
/// <param name="path">the full path</param>
|
||||
internal void SaveToPngFile(string path, bool eraseUserPreferenceColor)
|
||||
internal void SaveToPngFile(string path)
|
||||
{
|
||||
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method SaveToFile with parameter: path = {path}");
|
||||
this.windowsElement.GetScreenshot().SaveAsFile(path);
|
||||
|
||||
if (eraseUserPreferenceColor)
|
||||
{
|
||||
VisualHelper.EraseUserPreferenceColor(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,16 @@ namespace Microsoft.PowerToys.UITest
|
||||
SendWinKeyCombination(keysToSend);
|
||||
}
|
||||
|
||||
public static void PressKey(Key key)
|
||||
{
|
||||
PressVirtualKey(TranslateKeyHex(key));
|
||||
}
|
||||
|
||||
public static void ReleaseKey(Key key)
|
||||
{
|
||||
ReleaseVirtualKey(TranslateKeyHex(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates a key to its corresponding SendKeys representation.
|
||||
/// </summary>
|
||||
@@ -260,6 +270,26 @@ namespace Microsoft.PowerToys.UITest
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// map the virtual key codes to the corresponding keys.
|
||||
/// </summary>
|
||||
private static byte TranslateKeyHex(Key key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case Key.Win:
|
||||
return 0x5B; // Windows Key - 0x5B in hex
|
||||
case Key.Ctrl:
|
||||
return 0x11; // Ctrl Key - 0x11 in hex
|
||||
case Key.Alt:
|
||||
return 0x12; // Alt Key - 0x12 in hex
|
||||
case Key.Shift:
|
||||
return 0x10; // Shift Key - 0x10 in hex
|
||||
default:
|
||||
throw new ArgumentException($"Key {key} is not supported, Please add your key at TranslateKeyHex for translation to hex.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a combination of keys, including the Windows key, to the system.
|
||||
/// </summary>
|
||||
@@ -283,5 +313,21 @@ namespace Microsoft.PowerToys.UITest
|
||||
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just press the key.(no release)
|
||||
/// </summary>
|
||||
private static void PressVirtualKey(byte key)
|
||||
{
|
||||
keybd_event(key, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release only the button (if pressed first)
|
||||
/// </summary>
|
||||
private static void ReleaseVirtualKey(byte key)
|
||||
{
|
||||
keybd_event(key, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,6 +439,30 @@ namespace Microsoft.PowerToys.UITest
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// release the key (after the hold key and drag is completed.)
|
||||
/// </summary>
|
||||
/// <param name="key">The key release.</param>
|
||||
public void PressKey(Key key)
|
||||
{
|
||||
PerformAction(() =>
|
||||
{
|
||||
KeyboardHelper.PressKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// press and hold the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to press and hold .</param>
|
||||
public void ReleaseKey(Key key)
|
||||
{
|
||||
PerformAction(() =>
|
||||
{
|
||||
KeyboardHelper.ReleaseKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a sequence of keys.
|
||||
/// </summary>
|
||||
|
||||
@@ -538,7 +538,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("virtual monitor create faild");
|
||||
Console.WriteLine("virtual monitor create failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
@@ -20,7 +21,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <param name="element">Element object</param>
|
||||
/// <param name="scenarioSubname">additional scenario name if two or more scenarios in one test</param>
|
||||
[RequiresUnreferencedCode("This method uses reflection which may not be compatible with trimming.")]
|
||||
public static void AreEqual(TestContext? testContext, Element element, System.Reflection.Assembly callerAssembly, string scenarioSubname = "")
|
||||
public static void AreEqual(TestContext? testContext, Element element, string scenarioSubname = "")
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
@@ -52,8 +53,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
|
||||
var tempTestImagePath = GetTempFilePath(scenarioSubname, "test", ".png");
|
||||
|
||||
// Save the image with the user preference color erased
|
||||
element.SaveToPngFile(tempTestImagePath, true);
|
||||
element.SaveToPngFile(tempTestImagePath);
|
||||
|
||||
if (string.IsNullOrEmpty(baselineImageResourceName)
|
||||
|| !Path.GetFileNameWithoutExtension(baselineImageResourceName).EndsWith(scenarioSubname))
|
||||
@@ -65,21 +65,27 @@ namespace Microsoft.PowerToys.UITest
|
||||
|
||||
bool isSame = false;
|
||||
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
using (var baselineImage = new Bitmap(callerMethod!.DeclaringType!.Assembly.GetManifestResourceStream(baselineImageResourceName)))
|
||||
using (var stream = callerMethod!.DeclaringType!.Assembly.GetManifestResourceStream(baselineImageResourceName))
|
||||
{
|
||||
using (var testImage = new Bitmap(tempTestImagePath))
|
||||
if (stream == null)
|
||||
{
|
||||
isSame = VisualAssert.AreEqual(baselineImage, testImage);
|
||||
Assert.Fail($"Resource stream '{baselineImageResourceName}' is null.");
|
||||
}
|
||||
|
||||
if (!isSame)
|
||||
using (var baselineImage = new Bitmap(stream))
|
||||
{
|
||||
using (var testImage = new Bitmap(tempTestImagePath))
|
||||
{
|
||||
// Copy baseline image to temp folder as well
|
||||
baselineImage.Save(tempBaselineImagePath);
|
||||
isSame = VisualAssert.AreEqual(baselineImage, testImage);
|
||||
|
||||
if (!isSame)
|
||||
{
|
||||
// Copy baseline image to temp folder as well
|
||||
baselineImage.Save(tempBaselineImagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
|
||||
if (!isSame)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Hosts.UITests
|
||||
// 'Add an entry' button (only show-up when list is empty) should be visible
|
||||
Assert.IsTrue(this.HasOne<HyperlinkButton>("Add an entry"), "'Add an entry' button should be visible in the empty view");
|
||||
|
||||
// VisualAssert.AreEqual(this.Find("Entries"), "EmptyView");
|
||||
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "EmptyView");
|
||||
|
||||
// Click 'Add an entry' from empty-view for adding Host override rule
|
||||
this.Find<HyperlinkButton>("Add an entry").Click();
|
||||
@@ -51,7 +51,7 @@ namespace Hosts.UITests
|
||||
Assert.IsTrue(this.Has<Button>("Delete"), "Should have one row now");
|
||||
Assert.IsFalse(this.Has<HyperlinkButton>("Add an entry"), "'Add an entry' button should be invisible if not empty view");
|
||||
|
||||
// VisualAssert.AreEqual(this.Find("Entries"), "NonEmptyView");
|
||||
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "NonEmptyView");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,7 +74,7 @@ namespace Hosts.UITests
|
||||
|
||||
Assert.IsTrue(this.Has<Button>("Delete"), "Should have one row now");
|
||||
|
||||
// VisualAssert.AreEqual(this.TestContext, this.Find("Entries"));
|
||||
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user