Skip to content

Commit

Permalink
add ui initialization tests and add exit scope exe function
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhaopeng Wang committed Mar 4, 2025
1 parent b96bcf1 commit 3fa0a74
Showing 4 changed files with 270 additions and 3 deletions.
43 changes: 43 additions & 0 deletions src/common/UITestAutomation/Element/Element.cs
Original file line number Diff line number Diff line change
@@ -184,6 +184,26 @@ public T Find<T>(string name, int timeoutMS = 3000)
return this.Find<T>(By.Name(name), timeoutMS);
}

/// <summary>
/// Shortcut for this.FindAllByAccessibilityId<T>(accessibilityId, timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="accessibilityId">The accessibilityId of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public T FindByAccessibilityId<T>(string accessibilityId, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: accessibilityId = {accessibilityId}, timeoutMS = {timeoutMS}");

// leverage findAll to filter out mismatched elements
var collection = this.FindAllByAccessibilityId<T>(accessibilityId, timeoutMS);

Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {accessibilityId}");

return collection[0];
}

/// <summary>
/// Finds an element by the selector.
/// Shortcut for this.Find<Element>(by, timeoutMS)
@@ -231,6 +251,29 @@ public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
return foundElements ?? new ReadOnlyCollection<T>([]);
}

/// <summary>
/// Finds all elements by accessibilityId.
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="accessibilityId">The accessibilityId to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAllByAccessibilityId<T>(string accessibilityId, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: accessibilityId = {accessibilityId}, timeoutMS = {timeoutMS}");
var foundElements = FindHelper.FindAll<T, AppiumWebElement>(
() =>
{
var elements = this.windowsElement.FindElementsByAccessibilityId(accessibilityId);
return elements;
},
this.driver,
timeoutMS);

return foundElements ?? new ReadOnlyCollection<T>([]);
}

/// <summary>
/// Finds all elements by the selector.
/// Shortcut for this.FindAll<T>(By.Name(name), timeoutMS)
22 changes: 19 additions & 3 deletions src/common/UITestAutomation/SessionHelper.cs
Original file line number Diff line number Diff line change
@@ -95,12 +95,13 @@ public void StartExe(string appPath)
}

/// <summary>
/// Restarts now exe and takes control of it.
/// Exit a exe.
/// </summary>
public void RestartScopeExe()
/// <param name="path">The path to the application executable.</param>
public void ExitExe(string path)
{
// Exit Exe
string exeName = Path.GetFileNameWithoutExtension(sessionPath);
string exeName = Path.GetFileNameWithoutExtension(path);

// PowerToys.FancyZonesEditor
Process[] processes = Process.GetProcessesByName(exeName);
@@ -116,7 +117,22 @@ public void RestartScopeExe()
Assert.Fail($"Failed to terminate process {process.ProcessName} (ID: {process.Id}): {ex.Message}");
}
}
}

/// <summary>
/// Exit now exe.
/// </summary>
public void ExitScopeExe()
{
ExitExe(sessionPath);
}

/// <summary>
/// Restarts now exe and takes control of it.
/// </summary>
public void RestartScopeExe()
{
ExitExe(sessionPath);
StartExe(locationPath + sessionPath);
}

10 changes: 10 additions & 0 deletions src/common/UITestAutomation/UITestBase.cs
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings)

~UITestBase()
{
this.ExitScopeExe();
this.sessionHelper.Cleanup();
}

@@ -163,5 +164,14 @@ public void RestartScopeExe()
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver());
return;
}

/// <summary>
/// Restart scope exe.
/// </summary>
public void ExitScopeExe()
{
sessionHelper.ExitScopeExe();
return;
}
}
}
198 changes: 198 additions & 0 deletions src/modules/fancyzones/UITests-FancyZonesEditor/UIInitializaionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

using FancyZonesEditorCommon.Data;
using Microsoft.FancyZonesEditor.UITests;
using Microsoft.FancyZonesEditor.UITests.Utils;
using Microsoft.FancyZonesEditor.UnitTests.Utils;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.UI;
using static FancyZonesEditorCommon.Data.EditorParameters;
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;

namespace UITests_FancyZonesEditor
{
[TestClass]
public class UIInitializaionTest : UITestBase
{
public UIInitializaionTest()
: base(PowerToysModule.FancyZone)
{
}

[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.ParamsIOHelper.RestoreData();
}

[TestCleanup]
public void TestCleanup()
{
this.ExitScopeExe();
}

[TestMethod]
public void EditorParams_VerifySelectedMonitor()
{
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
ProcessId = 1,
SpanZonesAcrossMonitors = false,
Monitors = new List<NativeMonitorDataWrapper>
{
new NativeMonitorDataWrapper
{
Monitor = "monitor-1",
MonitorInstanceId = "instance-id-1",
MonitorSerialNumber = "serial-number-1",
MonitorNumber = 1,
VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}",
Dpi = 96,
LeftCoordinate = 0,
TopCoordinate = 0,
WorkAreaHeight = 1040,
WorkAreaWidth = 1920,
MonitorHeight = 1080,
MonitorWidth = 1920,
IsSelected = false,
},
new NativeMonitorDataWrapper
{
Monitor = "monitor-2",
MonitorInstanceId = "instance-id-2",
MonitorSerialNumber = "serial-number-2",
MonitorNumber = 2,
VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}",
Dpi = 96,
LeftCoordinate = 1920,
TopCoordinate = 0,
WorkAreaHeight = 1040,
WorkAreaWidth = 1920,
MonitorHeight = 1080,
MonitorWidth = 1920,
IsSelected = true,
},
},
};
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
this.RestartScopeExe();

Assert.IsFalse(Session.Find<Element>("Monitor 1").Selected);
Assert.IsTrue(Session.Find<Element>("Monitor 2").Selected);
}

[TestMethod]
public void EditorParams_VerifyMonitorScaling()
{
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
ProcessId = 1,
SpanZonesAcrossMonitors = false,
Monitors = new List<NativeMonitorDataWrapper>
{
new NativeMonitorDataWrapper
{
Monitor = "monitor-1",
MonitorInstanceId = "instance-id-1",
MonitorSerialNumber = "serial-number-1",
MonitorNumber = 1,
VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}",
Dpi = 192, // 200% scaling
LeftCoordinate = 0,
TopCoordinate = 0,
WorkAreaHeight = 1040,
WorkAreaWidth = 1920,
MonitorHeight = 1080,
MonitorWidth = 1920,
IsSelected = true,
},
},
};
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
this.RestartScopeExe();

var monitor = Session.Find<Element>("Monitor 1");
var scaling = monitor.FindByAccessibilityId<Element>("ScalingText");
Assert.AreEqual("200%", scaling.Text);
}

[TestMethod]
public void EditorParams_VerifyMonitorResolution()
{
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
ProcessId = 1,
SpanZonesAcrossMonitors = false,
Monitors = new List<NativeMonitorDataWrapper>
{
new NativeMonitorDataWrapper
{
Monitor = "monitor-1",
MonitorInstanceId = "instance-id-1",
MonitorSerialNumber = "serial-number-1",
MonitorNumber = 1,
VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}",
Dpi = 192,
LeftCoordinate = 0,
TopCoordinate = 0,
WorkAreaHeight = 1040,
WorkAreaWidth = 1920,
MonitorHeight = 1080,
MonitorWidth = 1920,
IsSelected = true,
},
},
};
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
this.RestartScopeExe();

var monitor = Session.Find<Element>("Monitor 1");
var resolution = monitor.FindByAccessibilityId<Element>("ResolutionText");
Assert.AreEqual("1920 × 1080", resolution.Text);
}

[TestMethod]
public void EditorParams_SpanAcrossMonitors()
{
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
ProcessId = 1,
SpanZonesAcrossMonitors = true,
Monitors = new List<NativeMonitorDataWrapper>
{
new NativeMonitorDataWrapper
{
Monitor = "monitor-1",
MonitorInstanceId = "instance-id-1",
MonitorSerialNumber = "serial-number-1",
MonitorNumber = 1,
VirtualDesktop = "{FF34D993-73F3-4B8C-AA03-73730A01D6A8}",
Dpi = 192,
LeftCoordinate = 0,
TopCoordinate = 0,
WorkAreaHeight = 1040,
WorkAreaWidth = 1920,
MonitorHeight = 1080,
MonitorWidth = 1920,
IsSelected = true,
},
},
};
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
this.RestartScopeExe();

var monitor = Session.Find<Element>("Monitor 1");
Assert.IsNotNull(monitor);
Assert.IsTrue(monitor.Selected);
}
}
}

1 comment on commit 3fa0a74

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (1)

Initializaion

These words are not needed and should be removed ahk AMPROPERTY AMPROPSETID Breadcrumb CDEF comdef ddf devenum DEVMON DEVSOURCE DGR DIIRFLAG dshow DVH DVHD DVSD DVSL EData ERole fdw FILEINFOSIG Filtergraph Filterx HCERTSTORE IKs iljxck IYUV KSPROPERTY lcb ldx lld LONGLONG LTRB majortype makecab MEDIASUBTYPE mediatype mfplat mic mjpg Msimg msiquery ORAW outpin overlaywindow PAUDIO PINDIR Pnp ppmt previouscamera PROPBAG propvarutil reencoded REFGUID REGFILTER REGFILTERPINS REGPINTYPES regsvr shmem sizeread stl strsafe strutil subquery SYNCMFT TMPVAR vcdl vdi vid VIDCAP VIDEOINFOHEADER vih webcam wistd WVC

To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the git@github.com:microsoft/PowerToys.git repository
on the dev/zhaopengwang/test/37733-ui-test-fancyzones-editor branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.24/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/13649835332/attempts/1'
Errors (1)

See the 📜action log or 📝 job summary for details.

❌ Errors Count
❌ check-file-path 1

See ❌ Event descriptions for more information.

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.