Compare commits

..

1 Commits

Author SHA1 Message Date
Gordon Lam (SH)
d868a85b87 Add Version 2025-06-13 18:53:02 +08:00
106 changed files with 684 additions and 8191 deletions

View File

@@ -191,7 +191,6 @@ CImage
cla
CLASSDC
CLASSNOTAVAILABLE
cleanmgr
clickable
clickonce
CLIENTEDGE
@@ -276,7 +275,6 @@ currentculture
CURRENTDIR
CURSORINFO
cursorpos
CURSORSHOWING
customaction
CUSTOMACTIONTEST
CUSTOMFORMATPLACEHOLDER
@@ -344,9 +342,7 @@ DESELECTOTHERS
DESIGNINFO
DESKTOPABSOLUTEEDITING
DESKTOPABSOLUTEPARSING
DESKTOPHORZRES
desktopshorcutinstalled
DESKTOPVERTRES
devblogs
devdocs
devmgmt
@@ -357,7 +353,6 @@ DFX
DIALOGEX
digicert
dimm
DINORMAL
DISABLEASACTIONKEY
DISABLENOSCROLL
diskmgmt
@@ -401,7 +396,6 @@ DVASPECTINFO
DVD
dvr
DVTARGETDEVICE
dwflags
dwl
dwm
dwmapi
@@ -490,7 +484,6 @@ FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
FARPROC
fff
FFFF
FILEEXPLORER
FILEFLAGS
FILEFLAGSMASK
@@ -609,7 +602,6 @@ helptext
HGFE
hglobal
hhk
HHmmssfff
hhx
Hiber
Hiberboot
@@ -618,7 +610,6 @@ hicon
HIDEREADONLY
HIDEWINDOW
Hif
hightlight
HIMAGELIST
himl
hinst
@@ -753,7 +744,6 @@ isocpp
iss
issecret
ISSEPARATOR
istep
ith
ITHUMBNAIL
IUI
@@ -1284,7 +1274,6 @@ prvpane
psapi
pscid
PSECURITY
psexec
psfgao
psfi
PSMODULEPATH
@@ -1542,7 +1531,6 @@ SLGP
sln
SMALLICON
smartphone
smileys
SMTO
SNAPPROCESS
snk
@@ -1675,7 +1663,6 @@ TDefault
TDevice
telephon
templatenamespace
TESTONLY
testprocess
TEXCOORD
TEXTBOXNEWLINE
@@ -1886,7 +1873,6 @@ WINDOWPOSCHANGING
WINDOWSBUILDNUMBER
windowssearch
windowssettings
windowsterminal
WINDOWSTYLES
WINDOWSTYLESICON
winerror
@@ -1996,7 +1982,6 @@ Zoneszonabletester
Zoomin
zoomit
ZOOMITX
Zorder
ZXk
ZXNs
zzz

View File

@@ -53,9 +53,6 @@ parameters:
- name: runTests
type: boolean
default: true
- name: buildTests
type: boolean
default: true
- name: useVSPreview
type: boolean
default: false
@@ -113,7 +110,7 @@ jobs:
JobOutputArtifactName: build-$(BuildPlatform)-$(BuildConfiguration)${{ parameters.artifactStem }}
NUGET_RESTORE_MSBUILD_ARGS: /p:Platform=$(BuildPlatform) # Required for nuget to work due to self contained
NODE_OPTIONS: --max_old_space_size=16384
${{ if or(eq(parameters.runTests, true), eq(parameters.buildTests, true)) }}:
${{ if eq(parameters.runTests, true) }}:
MSBuildMainBuildTargets: Build;Test
${{ else }}:
MSBuildMainBuildTargets: Build
@@ -397,7 +394,10 @@ jobs:
testAssemblyVer2: |
**\KeyboardManagerEngineTest.dll
**\KeyboardManagerEditorTest.dll
**\*UnitTest*.dll
**\UnitTests-CommonLib.dll
**\PowerRenameUnitTests.dll
**\UnitTests-FancyZones.dll
**\\WorkspacesLibUnitTests.dll
!**\obj\**
- pwsh: |-
@@ -531,7 +531,7 @@ jobs:
displayName: Stage GPO files
# Running the tests may result in future jobs consuming artifacts out of this build
- ${{ if or(eq(parameters.runTests, true), eq(parameters.buildTests, true)) }}:
- ${{ if eq(parameters.runTests, true) }}:
- task: CopyFiles@2
displayName: Stage entire build output
inputs:

View File

@@ -15,29 +15,20 @@ parameters:
jobs:
- job: Test${{ parameters.platform }}${{ parameters.configuration }}
displayName: Test ${{ parameters.platform }} ${{ parameters.configuration }}
timeoutInMinutes: 300
variables:
${{ if or(eq(parameters.platform, 'x64Win10'), eq(parameters.platform, 'x64Win11')) }}:
BuildPlatform: x64
${{ else }}:
BuildPlatform: ${{ parameters.platform }}
TestPlatform: ${{ parameters.platform }}
BuildPlatform: ${{ parameters.platform }}
BuildConfiguration: ${{ parameters.configuration }}
SrcPath: $(Build.Repository.LocalPath)
TestArtifactsName: build-${{ variables.BuildPlatform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}
TestArtifactsName: build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}
pool:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
${{ if ne(parameters.platform, 'ARM64') }}:
name: SHINE-INT-Testing-x64
${{ if eq(parameters.platform, 'x64Win11') }}:
demands: ImageOverride -equals SHINE-W11-Testing
${{ else }}:
name: SHINE-INT-Testing-arm64
${{ else }}:
${{ if ne(parameters.platform, 'ARM64') }}:
name: SHINE-OSS-Testing-x64
${{ if eq(parameters.platform, 'x64Win11') }}:
demands: ImageOverride -equals SHINE-W11-Testing
${{ else }}:
name: SHINE-OSS-Testing-arm64
steps:
@@ -110,13 +101,8 @@ jobs:
vsTestVersion: 'toolsInstaller'
uiTests: true
rerunFailedTests: true
# Since UITests-FancyZonesEditor.dll is generated in both UITests-FancyZonesEditor and UITests-FancyZones, removed one to avoid duplicate test runs
testAssemblyVer2: |
**\*UITest*.dll
**\UITests-FancyZones.dll
**\UITests-FancyZonesEditor.dll
!**\obj\**
!**\ref\**
!**\UITests-FancyZones\**\UITests-FancyZonesEditor.dll
env:
platform: '$(TestPlatform)'
!**\ref\**

View File

@@ -60,4 +60,16 @@ stages:
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
- ${{ if and(eq(parameters.runTests, true), not(and(eq(platform, 'arm64'), eq(variables['System.PullRequest.IsFork'], true)))) }}:
- stage: Test_${{ platform }}
displayName: Test ${{ platform }}
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: ${{ platform }}
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}

View File

@@ -1,84 +0,0 @@
variables:
- name: runCodesignValidationInjectionBG
value: false
- name: EnablePipelineCache
value: true
- ${{ if eq(parameters.enableMsBuildCaching, true) }}:
- name: EnablePipelineCache
value: true
parameters:
- name: buildPlatforms
type: object
default:
- x64
- arm64
- name: enableMsBuildCaching
type: boolean
default: false
- name: useVSPreview
type: boolean
default: false
- name: useLatestWebView2
type: boolean
default: false
stages:
- ${{ each platform in parameters.buildPlatforms }}:
- stage: Build_${{ platform }}
displayName: Build ${{ platform }}
dependsOn: []
jobs:
- template: job-build-project.yml
parameters:
pool:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ platform }}
buildConfigurations: [Release]
enablePackageCaching: true
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: false
buildTests: true
useVSPreview: ${{ parameters.useVSPreview }}
- ${{ if eq(platform, 'x64') }}:
- stage: Test_x64Win10
displayName: Test x64Win10
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: x64Win10
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
- ${{ if eq(platform, 'x64') }}:
- stage: Test_x64Win11
displayName: Test x64Win11
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: x64Win11
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
- ${{ if ne(platform, 'x64') }}:
- stage: Test_${{ platform }}
displayName: Test ${{ platform }}
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: ${{ platform }}
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}

View File

@@ -92,3 +92,4 @@ if ($totalFailures -gt 0) {
Write-Host -ForegroundColor Green "All " $referencedFileVersionsPerDll.keys.Count " libraries are mentioned with the same version across the dependencies.`r`n"
exit 0

View File

@@ -28,8 +28,6 @@ $versionExceptions = @(
"ObjectModelCsProjection.dll",
"RendererCsProjection.dll") -join '|';
$nullVersionExceptions = @(
"SkiaSharp.Views.WinUI.Native.dll",
"libSkiaSharp.dll",
"codicon.ttf",
"e_sqlite3.dll",
"getfilesiginforedist.dll",

View File

@@ -181,6 +181,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
## PowerToys core team
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
- [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
@@ -212,7 +213,6 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
- [@plante-msft](https://github.com/plante-msft) - Connor Plante - Product Manager
- [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie - Product Manager
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
- [@enricogior](https://github.com/enricogior) - Enrico Giordani - Dev Lead
- [@bzoz](https://github.com/bzoz) - Bartosz Sosnowski - Dev
- [@ivan100sic](https://github.com/ivan100sic) - Ivan Stošić - Dev

View File

@@ -717,10 +717,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "sr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MouseUtils.UITests", "src\modules\MouseUtils\MouseUtils.UITests\MouseUtils.UITests.csproj", "{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesEditorUITest", "src\modules\Workspaces\WorkspacesEditorUITest\WorkspacesEditorUITest.csproj", "{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorEngineCommon", "src\common\CalculatorEngineCommon\CalculatorEngineCommon.vcxproj", "{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}"
EndProject
Global
@@ -2603,22 +2599,6 @@ Global
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64
@@ -2788,7 +2768,7 @@ Global
{25C91A4E-BA4E-467A-85CD-8B62545BF674} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
{6AB6A2D6-F859-4A82-9184-0BD29C9F07D1} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
{212AD910-8488-4036-BE20-326931B75FB2} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{7AC943C9-52E8-44CF-9083-744D8049667B} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{7AC943C9-52E8-44CF-9083-744D8049667B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{54A93AF7-60C7-4F6C-99D2-FBB1F75F853A} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
@@ -2920,8 +2900,6 @@ Global
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
{64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}

View File

@@ -21,74 +21,67 @@
- Create a new project and add the following references to the project file. Change the OutputPath to your own module's path.
```
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ProjectGuid>{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}</ProjectGuid>
<RootNamespace>PowerToys.Hosts.UITests</RootNamespace>
<AssemblyName>PowerToys.Hosts.UITests</AssemblyName>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\Hosts.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>
<PropertyGroup>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\KeyboardManagerUITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<Folder Include="Properties\" />
</ItemGroup>
```
- Inherit your test class from UITestBase.
>Set Scope: The default scope starts from the PowerToys settings UI. If you want to start from your own module, set the constructor as shown below:
>Specify Scope:
```
[TestClass]
public class HostModuleTests : UITestBase
{
public HostModuleTests()
: base(PowerToysModule.Hosts, WindowSize.Small_Vertical)
{
}
}
[TestClass]
public class RunFancyZonesTest : UITestBase
{
public RunFancyZonesTest()
: base(PowerToysModule.FancyZone)
{
}
}
```
- Then you can start performing the UI operations.
- Then you can start using session to perform the UI operations.
**Example**
```
[TestMethod("Hosts.Basic.EmptyViewShouldWork")]
[TestCategory("Hosts File Editor #4")]
public void TestEmptyView()
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_KeyboardManager
{
this.CloseWarningDialog();
this.RemoveAllEntries();
[TestClass]
public class RunKeyboardManagerUITests : UITestBase
{
[TestMethod]
public void OpenKeyboardManagerEditor()
{
// Open KeyboardManagerEditor
this.Session.Find<Button>(By.Name("Remap a key")).Click();
this.Session.Attach("Remap keys");
// '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");
// Maximize window
var window = Session.Find<Window>(By.Name("Remap keys")).Maximize();
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "EmptyView");
// Add Key Remapping
this.Session.Find<Button>(By.Name("Add key remapping")).Click();
window.Close();
// Click 'Add an entry' from empty-view for adding Host override rule
this.Find<HyperlinkButton>("Add an entry").Click();
this.AddEntry("192.168.0.1", "localhost", false, false);
// Should have one row now and not more empty view
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.TestContext, this.Find("Entries"), "NonEmptyView");
// Back to Settings
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
}
}
```

View File

@@ -1,36 +0,0 @@
#include <windows.h>
#include "resource.h"
#include "../version/version.h"
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

View File

@@ -98,11 +98,6 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ExprtkEvaluator.h" />
<ClInclude Include="pch.h" />
@@ -110,7 +105,6 @@
<ClInclude Include="Calculator.h">
<DependentUpon>Calculator.idl</DependentUpon>
</ClInclude>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ExprtkEvaluator.cpp">
@@ -134,14 +128,16 @@
<ItemGroup>
<None Include="PropertySheet.props" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="CalculatorEngineCommon.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<ItemGroup>
<ProjectReference Include="..\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
</ItemGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>

View File

@@ -1,13 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by CalculatorEngineCommon.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "CalculatorEngineCommon"
#define INTERNAL_NAME "CalculatorEngineCommon"
#define ORIGINAL_FILENAME "CalculatorEngineCommon.dll"
// Non-localizable
//////////////////////////////

View File

@@ -1,53 +0,0 @@
// 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.
namespace Microsoft.PowerToys.UITest
{
public class CheckBox : Element
{
private static readonly string ExpectedControlType = "ControlType.CheckBox";
/// <summary>
/// Initializes a new instance of the <see cref="CheckBox"/> class.
/// </summary>
public CheckBox()
{
this.TargetControlType = CheckBox.ExpectedControlType;
}
/// <summary>
/// Select the item of the ComboBox.
/// </summary>
/// <param name="value">The text to select from the list view.</param>
public void Select(string value)
{
this.Find<NavigationViewItem>(value).Click();
}
/// <summary>
/// Gets a value indicating whether the CheckBox is checked.
/// </summary>
public bool IsChecked => this.Selected;
public CheckBox SetCheck(bool value = true, int msPreAction = 500, int msPostAction = 500)
{
if (this.IsChecked != value)
{
if (msPreAction > 0)
{
Task.Delay(msPreAction).Wait();
}
// Toggle the switch
this.Click();
if (msPostAction > 0)
{
Task.Delay(msPostAction).Wait();
}
}
return this;
}
}
}

View File

@@ -1,28 +0,0 @@
// 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.
namespace Microsoft.PowerToys.UITest
{
public class ComboBox : Element
{
private static readonly string ExpectedControlType = "ControlType.ComboBox";
/// <summary>
/// Initializes a new instance of the <see cref="ComboBox"/> class.
/// </summary>
public ComboBox()
{
this.TargetControlType = ComboBox.ExpectedControlType;
}
/// <summary>
/// Select the item of the ComboBox.
/// </summary>
/// <param name="value">The text to select from the list view.</param>
public void Select(string value)
{
this.Find<NavigationViewItem>(value).Click();
}
}
}

View File

@@ -1,74 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
public class Custom : Element
{
private static readonly string ExpectedControlType = "ControlType.Custom";
/// <summary>
/// Initializes a new instance of the <see cref="Custom"/> class.
/// </summary>
public Custom()
{
this.TargetControlType = Custom.ExpectedControlType;
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
PerformAction((actions, windowElement) =>
{
KeyboardHelper.SendKeys(keys);
});
}
/// <summary>
/// Drag element move offset.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
/// <summary>
/// Drag element move to other element.
/// </summary>
/// <param name="element">Move to this element.</param>
public void Drag(Element element)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).ClickAndHold();
Assert.IsNotNull(element.WindowsElement, "element is null");
int dx = (element.WindowsElement.Rect.X - windowElement.Rect.X) / 10;
int dy = (element.WindowsElement.Rect.Y - windowElement.Rect.Y) / 10;
for (int i = 0; i < 10; i++)
{
actions.MoveByOffset(dx, dy);
}
actions.Release();
actions.Build().Perform();
});
}
}
}

View File

@@ -7,7 +7,6 @@ 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;
@@ -26,20 +25,8 @@ namespace Microsoft.PowerToys.UITest
{
private WindowsElement? windowsElement;
protected internal WindowsElement? WindowsElement
{
get => windowsElement;
set => windowsElement = value;
}
private WindowsDriver<WindowsElement>? driver;
protected internal WindowsDriver<WindowsElement>? Driver
{
get => driver;
set => driver = value;
}
protected string? TargetControlType { get; set; }
internal bool IsMatchingTarget()
@@ -125,12 +112,9 @@ namespace Microsoft.PowerToys.UITest
/// Click the UI element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
/// <param name="msPreAction">Delay in milliseconds before performing the click action. Default is 500 ms.</param>
/// <param name="msPostAction">Delay in milliseconds after performing the click action. Default is 500 ms.</param>
public virtual void Click(bool rightClick = false, int msPreAction = 500, int msPostAction = 500)
public virtual void Click(bool rightClick = false)
{
PerformAction(
(actions, windowElement) =>
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement);
@@ -147,9 +131,7 @@ namespace Microsoft.PowerToys.UITest
}
actions.Build().Perform();
},
msPreAction,
msPostAction);
});
}
/// <summary>
@@ -170,20 +152,51 @@ namespace Microsoft.PowerToys.UITest
}
/// <summary>
/// Release action
/// Drag element move offset.
/// </summary>
public void ReleaseAction()
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
var releaseAction = new Actions(driver);
releaseAction.Release().Perform();
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
/// <summary>
/// Release key
/// Drag element move to other element.
/// </summary>
public void ReleaseKey(Key key)
/// <param name="element">Move to this element.</param>
public void Drag(Element element)
{
KeyboardHelper.ReleaseKey(key);
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).ClickAndHold();
Assert.IsNotNull(element.windowsElement, "element is null");
int dx = (element.windowsElement.Rect.X - windowElement.Rect.X) / 10;
int dy = (element.windowsElement.Rect.Y - windowElement.Rect.Y) / 10;
for (int i = 0; i < 10; i++)
{
actions.MoveByOffset(dx, dy);
}
actions.Release();
actions.Build().Perform();
});
}
/// <summary>
/// Send Key of the element.
/// </summary>
/// <param name="key">The Key to Send.</param>
public void SendKeys(string key)
{
PerformAction((actions, windowElement) =>
{
windowElement.SendKeys(key);
});
}
/// <summary>
@@ -205,7 +218,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public T Find<T>(By by, int timeoutMS = 5000)
public T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
@@ -213,7 +226,7 @@ namespace Microsoft.PowerToys.UITest
// leverage findAll to filter out mismatched elements
var collection = this.FindAll<T>(by, timeoutMS);
Assert.IsTrue(collection.Count > 0, $"UI-Element({typeof(T).Name}) not found using selector: {by}");
Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {by}");
return collection[0];
}
@@ -226,7 +239,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public T Find<T>(string name, int timeoutMS = 5000)
public T Find<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Find<T>(By.Name(name), timeoutMS);
@@ -239,7 +252,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public Element Find(By by, int timeoutMS = 5000)
public Element Find(By by, int timeoutMS = 3000)
{
return this.Find<Element>(by, timeoutMS);
}
@@ -251,7 +264,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public Element Find(string name, int timeoutMS = 5000)
public Element Find(string name, int timeoutMS = 3000)
{
return this.Find<Element>(By.Name(name), timeoutMS);
}
@@ -263,7 +276,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 5000)
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
@@ -295,7 +308,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 5000)
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.FindAll<T>(By.Name(name), timeoutMS);
@@ -308,7 +321,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="by">The selector to use for finding the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 5000)
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 3000)
{
return this.FindAll<Element>(by, timeoutMS);
}
@@ -320,23 +333,11 @@ namespace Microsoft.PowerToys.UITest
/// <param name="name">The name for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 5000)
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 3000)
{
return this.FindAll<Element>(By.Name(name), timeoutMS);
}
/// <summary>
/// Send Key of the element.
/// </summary>
/// <param name="key">The Key to Send.</param>
protected void SendKeys(string key)
{
PerformAction((actions, windowElement) =>
{
windowElement.SendKeys(key);
});
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
@@ -359,15 +360,5 @@ namespace Microsoft.PowerToys.UITest
Task.Delay(msPostAction).Wait();
}
}
/// <summary>
/// Save UI Element to a PNG file.
/// </summary>
/// <param name="path">the full path</param>
internal void SaveToPngFile(string path)
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method SaveToPngFile with parameter: path = {path}");
this.windowsElement.GetScreenshot().SaveAsFile(path);
}
}
}

View File

@@ -1,25 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
public class Group : Element
{
private static readonly string ExpectedControlType = "ControlType.Group";
/// <summary>
/// Initializes a new instance of the <see cref="Group"/> class.
/// </summary>
public Group()
{
this.TargetControlType = Group.ExpectedControlType;
}
}
}

View File

@@ -24,12 +24,9 @@ namespace Microsoft.PowerToys.UITest
/// Click the ListItem element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
/// <param name="msPreAction">Pre action delay in milliseconds. Default value is 500</param>
/// <param name="msPostAction">Post action delay in milliseconds. Default value is 500</param>
public override void Click(bool rightClick = false, int msPreAction = 500, int msPostAction = 500)
public override void Click(bool rightClick = false)
{
PerformAction(
(actions, windowElement) =>
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement, 10, 10);
@@ -43,36 +40,7 @@ namespace Microsoft.PowerToys.UITest
}
actions.Build().Perform();
},
msPreAction,
msPostAction);
}
/// <summary>
/// Click the center of the ListItem element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
/// <param name="msPreAction">Pre action delay in milliseconds. Default value is 500</param>
/// <param name="msPostAction">Post action delay in milliseconds. Default value is 500</param>
public void ClickCenter(bool rightClick = false, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
(actions, windowElement) =>
{
actions.MoveToElement(windowElement);
if (rightClick)
{
actions.ContextClick();
}
else
{
actions.Click();
}
actions.Build().Perform();
},
msPreAction,
msPostAction);
});
}
/// <summary>

View File

@@ -1,101 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
public class Pane : Element
{
private static readonly string ExpectedControlType = "ControlType.Pane";
/// <summary>
/// Initializes a new instance of the <see cref="Pane"/> class.
/// </summary>
public Pane()
{
this.TargetControlType = Pane.ExpectedControlType;
}
/// <summary>
/// Drag element move offset.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
/// <summary>
/// Simulates holding when dragging to target position.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void DragAndHold(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY);
actions.Build().Perform();
});
}
public void ReleaseDrag()
{
var releaseAction = new Actions(this.Driver);
releaseAction.Release().Perform();
}
/// <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)
{
HoldShiftToDrag(key, targetX, targetY);
ReleaseAction();
ReleaseKey(key);
}
/// <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 HoldShiftToDrag(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();
}
});
}
}
}

View File

@@ -1,131 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
namespace Microsoft.PowerToys.UITest
{
public class Slider : Element
{
private static readonly string ExpectedControlType = "ControlType.Slider";
/// <summary>
/// Initializes a new instance of the <see cref="Slider"/> class.
/// </summary>
public Slider()
{
this.TargetControlType = Slider.ExpectedControlType;
}
/// <summary>
/// Gets the value of a Slider (WindowsElement)
/// </summary>
/// <returns>The integer value of the slider</returns>
public int GetValue()
{
return this.Text == string.Empty ? 0 : int.Parse(this.Text);
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
PerformAction((actions, windowElement) =>
{
KeyboardHelper.SendKeys(keys);
});
}
/// <summary>
/// Sets the value of a Slider (WindowsElement) to the specified integer value.
/// Throws an exception if the value is out of the slider's valid range.
/// </summary>
/// <param name="targetValue">The target integer value to set</param>
public void SetValue(int targetValue)
{
// Read range and current value
int min = int.Parse(this.GetAttribute("RangeValue.Minimum"));
int max = int.Parse(this.GetAttribute("RangeValue.Maximum"));
int current = int.Parse(this.Text);
// Use Assert to check if the target value is within the valid range
Assert.IsTrue(
targetValue >= min && targetValue <= max,
$"Target value {targetValue} is out of range (min: {min}, max: {max}).");
// Compute difference
int diff = targetValue - current;
if (diff == 0)
{
return;
}
string key = diff > 0 ? OpenQA.Selenium.Keys.Right : OpenQA.Selenium.Keys.Left;
int steps = Math.Abs(diff);
for (int i = 0; i < steps; i++)
{
this.SendKeys(key);
// Thread.Sleep(2);
}
// Final check
int finalValue = int.Parse(this.Text);
Assert.AreEqual(
targetValue, finalValue, $"Slider value mismatch: expected {targetValue}, but got {finalValue}.");
}
/// <summary>
/// Sets the value of a Slider (WindowsElement) to the specified integer value.
/// Throws an exception if the value is out of the slider's valid range.
/// </summary>
/// <param name="targetValue">The target integer value to set</param>
public void QuickSetValue(int targetValue)
{
// Read range and current value
int min = int.Parse(this.GetAttribute("RangeValue.Minimum"));
int max = int.Parse(this.GetAttribute("RangeValue.Maximum"));
int current = int.Parse(this.Text);
// Use Assert to check if the target value is within the valid range
Assert.IsTrue(
targetValue >= min && targetValue <= max,
$"Target value {targetValue} is out of range (min: {min}, max: {max}).");
// Compute difference
int diff = targetValue - current;
if (diff == 0)
{
return;
}
string key = diff > 0 ? OpenQA.Selenium.Keys.Right : OpenQA.Selenium.Keys.Left;
int steps = Math.Abs(diff);
int maxKeysPerSend = 50;
int fullChunks = steps / maxKeysPerSend;
int remainder = steps % maxKeysPerSend;
for (int i = 0; i < fullChunks; i++)
{
SendKeys(new string(key[0], maxKeysPerSend));
Thread.Sleep(2);
}
if (remainder > 0)
{
SendKeys(new string(key[0], remainder));
Thread.Sleep(2);
}
// Final check
int finalValue = int.Parse(this.Text);
Assert.AreEqual(
targetValue, finalValue, $"Slider value mismatch: expected {targetValue}, but got {finalValue}.");
}
}
}

View File

@@ -1,67 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
public class Tab : Element
{
private static readonly string ExpectedControlType = "ControlType.Tab";
/// <summary>
/// Initializes a new instance of the <see cref="Tab"/> class.
/// </summary>
public Tab()
{
this.TargetControlType = Tab.ExpectedControlType;
}
/// <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)
{
HoldShiftToDrag(key, targetX, targetY);
ReleaseAction();
ReleaseKey(key);
}
/// <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 HoldShiftToDrag(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();
}
});
}
}
}

View File

@@ -2,6 +2,8 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using OpenQA.Selenium;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
@@ -33,10 +35,9 @@ namespace Microsoft.PowerToys.UITest
PerformAction((actions, windowElement) =>
{
// select all text and delete it
windowElement.SendKeys(OpenQA.Selenium.Keys.Control + "a");
windowElement.SendKeys(OpenQA.Selenium.Keys.Delete);
windowElement.SendKeys(Keys.Control + "a");
windowElement.SendKeys(Keys.Delete);
});
Task.Delay(500).Wait();
}
PerformAction((actions, windowElement) =>

View File

@@ -1,36 +0,0 @@
// 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
namespace Microsoft.PowerToys.UITest
{
public class Thumb : Element
{
private static readonly string ExpectedControlType = "ControlType.Thumb";
/// <summary>
/// Initializes a new instance of the <see cref="Thumb"/> class.
/// </summary>
public Thumb()
{
this.TargetControlType = Thumb.ExpectedControlType;
}
/// <summary>
/// Drag element move offset.
/// </summary>
/// <param name="offsetX">The offsetX to move.</param>
/// <param name="offsetY">The offsetY to move.</param>
public void Drag(int offsetX, int offsetY)
{
PerformAction((actions, windowElement) =>
{
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
actions.Build().Perform();
});
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Microsoft.PowerToys.UITest
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<IReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
var items = FindElementsWithRetry(findElementsFunc, timeoutMS);
var items = findElementsFunc();
var res = items.Select(item =>
{
var element = item as WindowsElement;
@@ -30,30 +30,17 @@ namespace Microsoft.PowerToys.UITest
return new ReadOnlyCollection<T>(res);
}
private static ReadOnlyCollection<TW> FindElementsWithRetry<TW>(Func<IReadOnlyCollection<TW>> findElementsFunc, int timeoutMS)
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<ReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
var timeout = TimeSpan.FromMilliseconds(timeoutMS);
var retryIntervalMS = TimeSpan.FromMilliseconds(500);
DateTime startTime = DateTime.Now;
while (DateTime.Now - startTime < timeout)
var items = findElementsFunc();
var res = items.Select(item =>
{
try
{
var items = findElementsFunc();
if (items.Count > 0)
{
return new ReadOnlyCollection<TW>((IList<TW>)items);
}
var element = item as WindowsElement;
return NewElement<T>(element, driver, timeoutMS);
}).Where(item => item.IsMatchingTarget()).ToList();
Task.Delay(retryIntervalMS).Wait();
}
catch (Exception)
{
}
}
return new ReadOnlyCollection<TW>(new List<TW>());
return new ReadOnlyCollection<T>(res);
}
public static T NewElement<T>(WindowsElement? element, WindowsDriver<WindowsElement>? driver, int timeoutMS)
@@ -63,6 +50,11 @@ namespace Microsoft.PowerToys.UITest
Assert.IsNotNull(element, $"New Element {typeof(T).Name} error: element is null.");
T newElement = new T();
if (timeoutMS > 0)
{
// Only set timeout if it is positive value
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(timeoutMS);
}
newElement.SetSession(driver);
newElement.SetWindowsElement(element);

View File

@@ -1,475 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Represents keyboard keys.
/// </summary>
public enum Key
{
Ctrl,
LCtrl,
RCtrl,
Alt,
Shift,
Tab,
Esc,
Enter,
Win,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Num0,
Num1,
Num2,
Num3,
Num4,
Num5,
Num6,
Num7,
Num8,
Num9,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Space,
Backspace,
Delete,
Insert,
Home,
End,
PageUp,
PageDown,
Up,
Down,
Left,
Right,
Other,
}
/// <summary>
    /// Provides methods for simulating keyboard input.
    /// </summary>
internal static class KeyboardHelper
{
[DllImport("user32.dll")]
#pragma warning disable SA1300 // Element should begin with upper-case letter
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
#pragma warning restore SA1300 // Element should begin with upper-case letter
#pragma warning disable SA1310 // Field names should not contain underscore
private const byte VK_LWIN = 0x5B;
private const uint KEYEVENTF_KEYDOWN = 0x0000;
private const uint KEYEVENTF_KEYUP = 0x0002;
#pragma warning restore SA1310 // Field names should not contain underscore
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public static void SendKeys(params Key[] keys)
{
string keysToSend = string.Join(string.Empty, keys.Select(TranslateKey));
SendWinKeyCombination(keysToSend);
}
public static void PressKey(Key key)
{
PressVirtualKey(TranslateKeyHex(key));
}
public static void ReleaseKey(Key key)
{
ReleaseVirtualKey(TranslateKeyHex(key));
}
public static void SendKey(Key key)
{
PressVirtualKey(TranslateKeyHex(key));
ReleaseVirtualKey(TranslateKeyHex(key));
}
/// <summary>
        /// Translates a key to its corresponding SendKeys representation.
        /// </summary>
        /// <param name="key">The key to translate.</param>
        /// <returns>The SendKeys representation of the key.</returns>
private static string TranslateKey(Key key)
{
switch (key)
{
case Key.Ctrl:
return "^";
case Key.LCtrl:
return "^";
case Key.RCtrl:
return "^";
case Key.Alt:
return "%";
case Key.Shift:
return "+";
case Key.Tab:
return "{TAB}";
case Key.Esc:
return "{ESC}";
case Key.Enter:
return "{ENTER}";
case Key.Win:
return "{WIN}";
case Key.Space:
return " ";
case Key.Backspace:
return "{BACKSPACE}";
case Key.Delete:
return "{DELETE}";
case Key.Insert:
return "{INSERT}";
case Key.Home:
return "{HOME}";
case Key.End:
return "{END}";
case Key.PageUp:
return "{PGUP}";
case Key.PageDown:
return "{PGDN}";
case Key.Up:
return "{UP}";
case Key.Down:
return "{DOWN}";
case Key.Left:
return "{LEFT}";
case Key.Right:
return "{RIGHT}";
case Key.F1:
return "{F1}";
case Key.F2:
return "{F2}";
case Key.F3:
return "{F3}";
case Key.F4:
return "{F4}";
case Key.F5:
return "{F5}";
case Key.F6:
return "{F6}";
case Key.F7:
return "{F7}";
case Key.F8:
return "{F8}";
case Key.F9:
return "{F9}";
case Key.F10:
return "{F10}";
case Key.F11:
return "{F11}";
case Key.F12:
return "{F12}";
case Key.A:
return "a";
case Key.B:
return "b";
case Key.C:
return "c";
case Key.D:
return "d";
case Key.E:
return "e";
case Key.F:
return "f";
case Key.G:
return "g";
case Key.H:
return "h";
case Key.I:
return "i";
case Key.J:
return "j";
case Key.K:
return "k";
case Key.L:
return "l";
case Key.M:
return "m";
case Key.N:
return "n";
case Key.O:
return "o";
case Key.P:
return "p";
case Key.Q:
return "q";
case Key.R:
return "r";
case Key.S:
return "s";
case Key.T:
return "t";
case Key.U:
return "u";
case Key.V:
return "v";
case Key.W:
return "w";
case Key.X:
return "x";
case Key.Y:
return "y";
case Key.Z:
return "z";
case Key.Num0:
return "0";
case Key.Num1:
return "1";
case Key.Num2:
return "2";
case Key.Num3:
return "3";
case Key.Num4:
return "4";
case Key.Num5:
return "5";
case Key.Num6:
return "6";
case Key.Num7:
return "7";
case Key.Num8:
return "8";
case Key.Num9:
return "9";
default:
return string.Empty;
}
}
/// <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
case Key.LCtrl:
return 0xA2; // Left Ctrl Key - 0xA2 in hex
case Key.RCtrl:
return 0xA3; // Right Ctrl Key - 0xA3 in hex
case Key.A:
return 0x41; // A Key - 0x41 in hex
case Key.B:
return 0x42; // B Key - 0x42 in hex
case Key.C:
return 0x43; // C Key - 0x43 in hex
case Key.D:
return 0x44; // D Key - 0x44 in hex
case Key.E:
return 0x45; // E Key - 0x45 in hex
case Key.F:
return 0x46; // F Key - 0x46 in hex
case Key.G:
return 0x47; // G Key - 0x47 in hex
case Key.H:
return 0x48; // H Key - 0x48 in hex
case Key.I:
return 0x49; // I Key - 0x49 in hex
case Key.J:
return 0x4A; // J Key - 0x4A in hex
case Key.K:
return 0x4B; // K Key - 0x4B in hex
case Key.L:
return 0x4C; // L Key - 0x4C in hex
case Key.M:
return 0x4D; // M Key - 0x4D in hex
case Key.N:
return 0x4E; // N Key - 0x4E in hex
case Key.O:
return 0x4F; // O Key - 0x4F in hex
case Key.P:
return 0x50; // P Key - 0x50 in hex
case Key.Q:
return 0x51; // Q Key - 0x51 in hex
case Key.R:
return 0x52; // R Key - 0x52 in hex
case Key.S:
return 0x53; // S Key - 0x53 in hex
case Key.T:
return 0x54; // T Key - 0x54 in hex
case Key.U:
return 0x55; // U Key - 0x55 in hex
case Key.V:
return 0x56; // V Key - 0x56 in hex
case Key.W:
return 0x57; // W Key - 0x57 in hex
case Key.X:
return 0x58; // X Key - 0x58 in hex
case Key.Y:
return 0x59; // Y Key - 0x59 in hex
case Key.Z:
return 0x5A; // Z Key - 0x5A in hex
case Key.Num0:
return 0x30; // 0 Key - 0x30 in hex
case Key.Num1:
return 0x31; // 1 Key - 0x31 in hex
case Key.Num2:
return 0x32; // 2 Key - 0x32 in hex
case Key.Num3:
return 0x33; // 3 Key - 0x33 in hex
case Key.Num4:
return 0x34; // 4 Key - 0x34 in hex
case Key.Num5:
return 0x35; // 5 Key - 0x35 in hex
case Key.Num6:
return 0x36; // 6 Key - 0x36 in hex
case Key.Num7:
return 0x37; // 7 Key - 0x37 in hex
case Key.Num8:
return 0x38; // 8 Key - 0x38 in hex
case Key.Num9:
return 0x39; // 9 Key - 0x39 in hex
case Key.F1:
return 0x70; // F1 Key - 0x70 in hex
case Key.F2:
return 0x71; // F2 Key - 0x71 in hex
case Key.F3:
return 0x72; // F3 Key - 0x72 in hex
case Key.F4:
return 0x73; // F4 Key - 0x73 in hex
case Key.F5:
return 0x74; // F5 Key - 0x74 in hex
case Key.F6:
return 0x75; // F6 Key - 0x75 in hex
case Key.F7:
return 0x76; // F7 Key - 0x76 in hex
case Key.F8:
return 0x77; // F8 Key - 0x77 in hex
case Key.F9:
return 0x78; // F9 Key - 0x78 in hex
case Key.F10:
return 0x79; // F10 Key - 0x79 in hex
case Key.F11:
return 0x7A; // F11 Key - 0x7A in hex
case Key.F12:
return 0x7B; // F12 Key - 0x7B in hex
case Key.Up:
return 0x26; // Up Arrow Key - 0x26 in hex
case Key.Down:
return 0x28; // Down Arrow Key - 0x28 in hex
case Key.Left:
return 0x25; // Left Arrow Key - 0x25 in hex
case Key.Right:
return 0x27; // Right Arrow Key - 0x27 in hex
case Key.Home:
return 0x24; // Home Key - 0x24 in hex
case Key.End:
return 0x23; // End Key - 0x23 in hex
case Key.PageUp:
return 0x21; // Page Up Key - 0x21 in hex
case Key.PageDown:
return 0x22; // Page Down Key - 0x22 in hex
case Key.Space:
return 0x20; // Space Key - 0x20 in hex
case Key.Enter:
return 0x0D; // Enter Key - 0x0D in hex
case Key.Backspace:
return 0x08; // Backspace Key - 0x08 in hex
case Key.Tab:
return 0x09; // Tab Key - 0x09 in hex
case Key.Esc:
return 0x1B; // Escape Key - 0x1B in hex
case Key.Insert:
return 0x2D; // Insert Key - 0x2D in hex
case Key.Delete:
return 0x2E; // Delete Key - 0x2E 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>
/// <param name="keys">The keys to send.</param>
private static void SendWinKeyCombination(string keys)
{
bool winKeyDown = false;
if (keys.Contains("{WIN}"))
{
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);
winKeyDown = true;
keys = keys.Replace("{WIN}", string.Empty); // Remove {WIN} from the string
        }
System.Windows.Forms.SendKeys.SendWait(keys);
        // Release Windows key
if (winKeyDown)
{
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);
}
}
}

View File

@@ -29,49 +29,6 @@ namespace Microsoft.PowerToys.UITest
PowerToysSettings,
FancyZone,
Hosts,
Runner,
Workspaces,
}
/// <summary>
/// Represents the window size for the UI test.
/// </summary>
public enum WindowSize
{
/// <summary>
/// Unspecified window size, won't make any size change
/// </summary>
UnSpecified,
/// <summary>
/// Small window size, 640 * 480
/// </summary>
Small,
/// <summary>
/// Small window size, 480 * 640
/// </summary>
Small_Vertical,
/// <summary>
/// Medium window size, 1024 * 768
/// </summary>
Medium,
/// <summary>
/// Medium window size, 768 * 1024
/// </summary>
Medium_Vertical,
/// <summary>
/// Large window size, 1920 * 1080
/// </summary>
Large,
/// <summary>
/// Large window size, 1080 * 1920
/// </summary>
Large_Vertical,
}
internal class ModuleConfigData
@@ -95,8 +52,6 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.PowerToysSettings] = "PowerToys Settings",
[PowerToysModule.FancyZone] = "FancyZones Layout",
[PowerToysModule.Hosts] = "Hosts File Editor",
[PowerToysModule.Runner] = "PowerToys",
[PowerToysModule.Workspaces] = "Workspaces Editor",
};
// Exe start path for the module if it exists.
@@ -105,8 +60,6 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.PowerToysSettings] = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe",
[PowerToysModule.FancyZone] = @"\..\..\..\PowerToys.FancyZonesEditor.exe",
[PowerToysModule.Hosts] = @"\..\..\..\WinUI3Apps\PowerToys.Hosts.exe",
[PowerToysModule.Runner] = @"\..\..\..\PowerToys.exe",
[PowerToysModule.Workspaces] = @"\..\..\..\PowerToys.WorkspacesEditor.exe",
};
}

View File

@@ -1,37 +0,0 @@
// 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;
namespace Microsoft.PowerToys.UITest
{
public class MonitorInfoData
{
public MonitorInfoData()
{
}
public struct MonitorInfoDataWrapper
{
public string DeviceName { get; set; }
public string DeviceString { get; set; }
public string DeviceID { get; set; }
public string DeviceKey { get; set; }
public int PelsWidth { get; set; }
public int PelsHeight { get; set; }
public int DisplayFrequency { get; set; }
}
public struct ParamsWrapper
{
public List<MonitorInfoDataWrapper> Monitors { get; set; }
}
}
}

View File

@@ -1,217 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
public enum MouseActionType
{
LeftClick,
RightClick,
MiddleClick,
LeftDoubleClick,
RightDoubleClick,
LeftDown,
LeftUp,
RightDown,
RightUp,
MiddleDown,
MiddleUp,
ScrollUp,
ScrollDown,
}
internal static class MouseHelper
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[Flags]
internal enum MouseEvent
{
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
Wheel = 0x0800,
}
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int x, int y);
[DllImport("user32.dll")]
#pragma warning disable SA1300 // Element should begin with upper-case letter
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
#pragma warning restore SA1300 // Element should begin with upper-case letter
        /// <summary>
        /// Gets the current position of the mouse cursor as a tuple.
        /// </summary>
        /// <returns>A tuple containing the X and Y coordinates of the cursor.</returns>
public static Tuple<int, int> GetMousePosition()
{
GetCursorPos(out POINT point);
return Tuple.Create(point.X, point.Y);
}
/// <summary>
    /// Moves the mouse cursor to the specified screen coordinates.
    /// </summary>
    /// <param name="x">The new x-coordinate of the cursor.</param>
    /// <param name="y">The new y-coordinate of the cursor.</param
public static void MoveMouseTo(int x, int y)
{
SetCursorPos(x, y);
}
/// <summary>
/// The delay in milliseconds between mouse down and up events to simulate a click.
/// </summary>
private const int ClickDelay = 100;
/// <summary>
/// The amount of scroll units to simulate a single mouse wheel tick.
/// </summary>
private const int ScrollAmount = 120;
/// <summary>
/// Simulates a left mouse click (press and release).
/// </summary>
public static void LeftClick()
{
LeftDown();
Thread.Sleep(ClickDelay);
LeftUp();
}
/// <summary>
/// Simulates a right mouse click (press and release).
/// </summary>
public static void RightClick()
{
RightDown();
Thread.Sleep(ClickDelay);
RightUp();
}
/// <summary>
/// Simulates a middle mouse click (press and release).
/// </summary>
public static void MiddleClick()
{
MiddleDown();
Thread.Sleep(ClickDelay);
MiddleUp();
}
/// <summary>
/// Simulates a left mouse double-click.
/// </summary>
public static void LeftDoubleClick()
{
LeftClick();
Thread.Sleep(ClickDelay);
LeftClick();
}
/// <summary>
/// Simulates a right mouse double-click.
/// </summary>
public static void RightDoubleClick()
{
RightClick();
Thread.Sleep(ClickDelay);
RightClick();
}
/// <summary>
/// Simulates pressing the left mouse button down.
/// </summary>
public static void LeftDown()
{
mouse_event((uint)MouseEvent.LeftDown, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates pressing the right mouse button down.
/// </summary>
public static void RightDown()
{
mouse_event((uint)MouseEvent.RightDown, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates pressing the middle mouse button down.
/// </summary>
public static void MiddleDown()
{
mouse_event((uint)MouseEvent.MiddleDown, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates releasing the left mouse button.
/// </summary>
public static void LeftUp()
{
mouse_event((uint)MouseEvent.LeftUp, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates releasing the right mouse button.
/// </summary>
public static void RightUp()
{
mouse_event((uint)MouseEvent.RightUp, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates releasing the middle mouse button.
/// </summary>
public static void MiddleUp()
{
mouse_event((uint)MouseEvent.MiddleUp, 0, 0, 0, UIntPtr.Zero);
}
/// <summary>
/// Simulates a mouse scroll wheel action by a specified amount.
/// Positive values scroll up, negative values scroll down.
/// </summary>
/// <param name="amount">The scroll amount. Typically 120 or -120 per tick.</param>
public static void ScrollWheel(int amount)
{
mouse_event((uint)MouseEvent.Wheel, 0, 0, (uint)amount, UIntPtr.Zero);
}
/// <summary>
/// Simulates scrolling the mouse wheel up by one tick.
/// </summary>
public static void ScrollUp()
{
ScrollWheel(ScrollAmount);
}
/// <summary>
/// Simulates scrolling the mouse wheel down by one tick.
/// </summary>
public static void ScrollDown()
{
ScrollWheel(-ScrollAmount);
}
}
}

View File

@@ -1,133 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Provides methods for capturing the screen with the mouse cursor.
/// </summary>
internal static class ScreenCapture
{
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
private static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll")]
private static extern bool DrawIconEx(IntPtr hdc, int x, int y, IntPtr hIcon, int cx, int cy, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, int diFlags);
/// <summary>
/// Represents a point with X and Y coordinates.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
/// <summary>
/// Contains information about the cursor.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
/// <summary>
/// Gets or sets the size of the structure.
/// </summary>
public int CbSize;
/// <summary>
/// Gets or sets the cursor state.
/// </summary>
public int Flags;
/// <summary>
/// Gets or sets the handle to the cursor.
/// </summary>
public IntPtr HCursor;
/// <summary>
/// Gets or sets the screen position of the cursor.
/// </summary>
public POINT PTScreenPos;
}
private const int CURSORSHOWING = 0x00000001;
private const int DESKTOPHORZRES = 118;
private const int DESKTOPVERTRES = 117;
private const int DINORMAL = 0x0003;
/// <summary>
/// Captures the screen with the mouse cursor and saves it to the specified file path.
/// </summary>
/// <param name="filePath">The file path to save the captured image.</param>
private static void CaptureScreenWithMouse(string filePath)
{
IntPtr hdc = GetDC(IntPtr.Zero);
int screenWidth = GetDeviceCaps(hdc, DESKTOPHORZRES);
int screenHeight = GetDeviceCaps(hdc, DESKTOPVERTRES);
ReleaseDC(IntPtr.Zero, hdc);
Rectangle bounds = new Rectangle(0, 0, screenWidth, screenHeight);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap))
{
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
CURSORINFO cursorInfo;
cursorInfo.CbSize = Marshal.SizeOf<CURSORINFO>();
if (GetCursorInfo(out cursorInfo) && cursorInfo.Flags == CURSORSHOWING)
{
using (System.Drawing.Graphics gIcon = System.Drawing.Graphics.FromImage(bitmap))
{
IntPtr hdcDest = gIcon.GetHdc();
DrawIconEx(hdcDest, cursorInfo.PTScreenPos.X, cursorInfo.PTScreenPos.Y, cursorInfo.HCursor, 0, 0, 0, IntPtr.Zero, DINORMAL);
gIcon.ReleaseHdc(hdcDest);
}
}
}
bitmap.Save(filePath, ImageFormat.Png);
}
}
/// <summary>
/// Captures a screenshot and saves it to the specified directory.
/// </summary>
/// <param name="directory">The directory to save the screenshot.</param>
private static void CaptureScreenshot(string directory)
{
string filePath = Path.Combine(directory, $"screenshot_{DateTime.Now:yyyyMMdd_HHmmssfff}.png");
CaptureScreenWithMouse(filePath);
}
/// <summary>
/// Timer callback method to capture a screenshot.
/// </summary>
/// <param name="state">The state object passed to the callback method.</param>
public static void TimerCallback(object? state)
{
string directory = (string)state!;
CaptureScreenshot(directory);
}
}
}

View File

@@ -1,17 +1,14 @@
// Copyright (c) Microsoft Corporation
// 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.ObjectModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -20,70 +17,35 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public class Session
{
public WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement> WindowsDriver { get; set; }
private List<IntPtr> windowHandlers = new List<IntPtr>();
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(nint hWnd);
private Window? MainWindow { get; set; }
/// <summary>
/// Gets Main Window Handler
/// </summary>
public IntPtr MainWindowHandler { get; private set; }
/// <summary>
/// Gets Init Scope
/// </summary>
public PowerToysModule InitScope { get; private set; }
/// <summary>
/// Gets the RunAsAdmin flag.
/// If true, the session is running as admin.
/// If false, the session is not running as admin.
/// If null, no information is available.
/// </summary>
public bool? IsElevated { get; private set; }
public Session(WindowsDriver<WindowsElement> pRoot, WindowsDriver<WindowsElement> pDriver, PowerToysModule scope, WindowSize size)
public Session(WindowsDriver<WindowsElement> root, WindowsDriver<WindowsElement> windowsDriver)
{
this.MainWindowHandler = IntPtr.Zero;
this.Root = pRoot;
this.WindowsDriver = pDriver;
this.InitScope = scope;
if (size != WindowSize.UnSpecified)
{
// Attach to the scope & reset MainWindowHandler
this.Attach(scope, size);
}
this.Root = root;
this.WindowsDriver = windowsDriver;
}
/// <summary>
/// Cleans up the Session Exe.
/// </summary>
public void Cleanup()
{
windowHandlers.Clear();
}
/// <summary>
/// Finds an Element or its derived class by selector.
/// Finds an element by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public T Find<T>(By by, int timeoutMS = 5000, bool global = false)
public T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
// leverage findAll to filter out mismatched elements
var collection = this.FindAll<T>(by, timeoutMS, global);
var collection = this.FindAll<T>(by, timeoutMS);
Assert.IsTrue(collection.Count > 0, $"UI-Element({typeof(T).Name}) not found using selector: {by}");
Assert.IsTrue(collection.Count > 0, $"Element not found using selector: {by}");
return collection[0];
}
@@ -93,159 +55,62 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public T Find<T>(string name, int timeoutMS = 5000, bool global = false)
public T Find<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Find<T>(By.Name(name), timeoutMS, global);
return this.Find<T>(By.Name(name), timeoutMS);
}
/// <summary>
/// Shortcut for this.Find<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public Element Find(By by, int timeoutMS = 5000, bool global = false)
public Element Find(By by, int timeoutMS = 3000)
{
return this.Find<Element>(by, timeoutMS, global);
return this.Find<Element>(by, timeoutMS);
}
/// <summary>
/// Shortcut for this.Find<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public Element Find(string name, int timeoutMS = 5000, bool global = false)
public Element Find(string name, int timeoutMS = 3000)
{
return this.Find<Element>(By.Name(name), timeoutMS, global);
return this.Find<Element>(By.Name(name), timeoutMS);
}
/// <summary>
/// Has only one Element or its derived class by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.FindAll<T>(by, timeoutMS, global).Count == 1;
}
/// <summary>
/// Shortcut for this.HasOne<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(By by, int timeoutMS = 5000, bool global = false)
{
return this.HasOne<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.HasOne<T>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.HasOne<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.HasOne<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(string name, int timeoutMS = 5000, bool global = false)
{
return this.HasOne<Element>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Has one or more Element or its derived class by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.FindAll<T>(by, timeoutMS, global).Count >= 1;
}
/// <summary>
/// Shortcut for this.Has<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(By by, int timeoutMS = 5000, bool global = false)
{
return this.Has<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Has<T>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Has<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Has<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(string name, int timeoutMS = 5000, bool global = false)
{
return this.Has<Element>(name, timeoutMS, global);
}
/// <summary>
/// Finds all Element or its derived class by selector.
/// Finds all elements by selector.
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</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> FindAll<T>(By by, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
var driver = global ? this.Root : this.WindowsDriver;
Assert.IsNotNull(driver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElements = FindHelper.FindAll<T, WindowsElement>(
() =>
{
if (by.GetIsAccessibilityId())
{
var elements = driver.FindElementsByAccessibilityId(by.GetAccessibilityId());
var elements = this.WindowsDriver.FindElementsByAccessibilityId(by.GetAccessibilityId());
return elements;
}
else
{
var elements = driver.FindElements(by.ToSeleniumBy());
var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy());
return elements;
}
},
driver,
this.WindowsDriver,
timeoutMS);
return foundElements ?? new ReadOnlyCollection<T>([]);
@@ -257,12 +122,12 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="name">The name to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</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> FindAll<T>(string name, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.FindAll<T>(By.Name(name), timeoutMS, global);
return this.FindAll<T>(By.Name(name), timeoutMS);
}
/// <summary>
@@ -270,11 +135,11 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.FindAll<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 3000)
{
return this.FindAll<Element>(by, timeoutMS, global);
return this.FindAll<Element>(by, timeoutMS);
}
/// <summary>
@@ -282,186 +147,55 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.FindAll<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 5000, bool global = false)
public ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 3000)
{
return this.FindAll<Element>(By.Name(name), timeoutMS, global);
return this.FindAll<Element>(By.Name(name), timeoutMS);
}
/// <summary>
/// Close the main window.
/// Keyboard Action key.
/// </summary>
public void CloseMainWindow()
/// <param name="key1">The Keys1 to click.</param>
/// <param name="key2">The Keys2 to click.</param>
/// <param name="key3">The Keys3 to click.</param>
/// <param name="key4">The Keys4 to click.</param>
public void KeyboardAction(string key1, string key2 = "", string key3 = "", string key4 = "")
{
if (MainWindow != null)
PerformAction((actions, windowElement) =>
{
MainWindow.Close();
MainWindow = null;
}
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
PerformAction(() =>
{
KeyboardHelper.SendKeys(keys);
});
}
/// <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>
/// press and hold the specified key.
/// </summary>
/// <param name="key">The key to press and release .</param>
public void SendKey(Key key, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
() =>
if (string.IsNullOrEmpty(key2))
{
KeyboardHelper.SendKey(key);
},
msPreAction,
msPostAction);
}
/// <summary>
/// Sends a sequence of keys.
/// </summary>
/// <param name="keys">An array of keys to send.</param>
public void SendKeySequence(params Key[] keys)
{
PerformAction(() =>
{
foreach (var key in keys)
{
KeyboardHelper.SendKeys(key);
actions.SendKeys(key1);
}
else if (string.IsNullOrEmpty(key3))
{
actions.SendKeys(key1).SendKeys(key2);
}
else if (string.IsNullOrEmpty(key4))
{
actions.SendKeys(key1).SendKeys(key2).SendKeys(key3);
}
else
{
actions.SendKeys(key1).SendKeys(key2).SendKeys(key3).SendKeys(key4);
}
actions.Release();
actions.Build().Perform();
});
}
        /// <summary>
        /// Gets the current position of the mouse cursor as a tuple.
        /// </summary>
        /// <returns>A tuple containing the X and Y coordinates of the cursor.</returns>
public Tuple<int, int> GetMousePosition()
{
return MouseHelper.GetMousePosition();
}
/// <summary>
    /// Moves the mouse cursor to the specified screen coordinates.
    /// </summary>
    /// <param name="x">The new x-coordinate of the cursor.</param>
    /// <param name="y">The new y-coordinate of the cursor.</param
public void MoveMouseTo(int x, int y, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
() =>
{
MouseHelper.MoveMouseTo(x, y);
},
msPreAction,
msPostAction);
}
/// <summary>
/// Performs a mouse action based on the specified action type.
/// </summary>
/// <param name="action">The mouse action to perform.</param>
/// <param name="msPreAction">Pre-action delay in milliseconds.</param>
/// <param name="msPostAction">Post-action delay in milliseconds.</param>
public void PerformMouseAction(MouseActionType action, int msPreAction = 500, int msPostAction = 500)
{
PerformAction(
() =>
{
switch (action)
{
case MouseActionType.LeftClick:
MouseHelper.LeftClick();
break;
case MouseActionType.RightClick:
MouseHelper.RightClick();
break;
case MouseActionType.MiddleClick:
MouseHelper.MiddleClick();
break;
case MouseActionType.LeftDoubleClick:
MouseHelper.LeftDoubleClick();
break;
case MouseActionType.RightDoubleClick:
MouseHelper.RightDoubleClick();
break;
case MouseActionType.LeftDown:
MouseHelper.LeftDown();
break;
case MouseActionType.LeftUp:
MouseHelper.LeftUp();
break;
case MouseActionType.RightDown:
MouseHelper.RightDown();
break;
case MouseActionType.RightUp:
MouseHelper.RightUp();
break;
case MouseActionType.MiddleDown:
MouseHelper.MiddleDown();
break;
case MouseActionType.MiddleUp:
MouseHelper.MiddleUp();
break;
case MouseActionType.ScrollUp:
MouseHelper.ScrollUp();
break;
case MouseActionType.ScrollDown:
MouseHelper.ScrollDown();
break;
default:
throw new ArgumentException("Unsupported mouse action.", nameof(action));
}
},
msPreAction,
msPostAction);
}
/// <summary>
/// Attaches to an existing PowerToys module.
/// </summary>
/// <param name="module">The PowerToys module to attach to.</param>
/// <param name="size">The window size to set. Default is no change to window size</param>
/// <returns>The attached session.</returns>
public Session Attach(PowerToysModule module, WindowSize size = WindowSize.UnSpecified)
public Session Attach(PowerToysModule module)
{
string windowName = ModuleConfigData.Instance.GetModuleWindowName(module);
return this.Attach(windowName, size);
return this.Attach(windowName);
}
/// <summary>
@@ -469,148 +203,35 @@ namespace Microsoft.PowerToys.UITest
/// The session should be attached when a new app is started.
/// </summary>
/// <param name="windowName">The window name to attach to.</param>
/// <param name="size">The window size to set. Default is no change to window size</param>
/// <returns>The attached session.</returns>
public Session Attach(string windowName, WindowSize size = WindowSize.UnSpecified)
public Session Attach(string windowName)
{
this.IsElevated = null;
this.MainWindowHandler = IntPtr.Zero;
if (this.Root != null)
{
// search window handler by window title (admin and non-admin titles)
var timeout = TimeSpan.FromMinutes(2);
var retryInterval = TimeSpan.FromSeconds(5);
DateTime startTime = DateTime.Now;
List<(IntPtr HWnd, string Title)>? matchingWindows = null;
while (DateTime.Now - startTime < timeout)
{
matchingWindows = WindowHelper.ApiHelper.FindDesktopWindowHandler(
new[] { windowName, WindowHelper.AdministratorPrefix + windowName });
if (matchingWindows.Count > 0 && matchingWindows[0].HWnd != IntPtr.Zero)
{
break;
}
Task.Delay(retryInterval).Wait();
}
if (matchingWindows == null || matchingWindows.Count == 0 || matchingWindows[0].HWnd == IntPtr.Zero)
{
Assert.Fail($"Failed to attach. Window '{windowName}' not found after {timeout.TotalSeconds} seconds.");
}
// pick one from matching windows
this.MainWindowHandler = matchingWindows[0].HWnd;
this.IsElevated = matchingWindows[0].Title.StartsWith(WindowHelper.AdministratorPrefix);
ApiHelper.SetForegroundWindow(this.MainWindowHandler);
var hexWindowHandle = this.MainWindowHandler.ToInt64().ToString("x");
var window = this.Root.FindElementByName(windowName);
Assert.IsNotNull(window, $"Failed to attach. Window '{windowName}' not found");
var windowHandle = new nint(int.Parse(window.GetAttribute("NativeWindowHandle")));
SetForegroundWindow(windowHandle);
var hexWindowHandle = windowHandle.ToString("x");
var appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("appTopLevelWindow", hexWindowHandle);
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
this.WindowsDriver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), appCapabilities);
Assert.IsNotNull(this.WindowsDriver, "Attach WindowsDriver is null");
this.windowHandlers.Add(this.MainWindowHandler);
if (size != WindowSize.UnSpecified)
{
WindowHelper.SetWindowSize(this.MainWindowHandler, size);
}
// Set MainWindow
MainWindow = Find<Window>(matchingWindows[0].Title);
// Set implicit timeout to make element search retry every 500 ms
this.WindowsDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3);
}
else
{
Assert.IsNotNull(this.Root, $"Failed to attach to the window '{windowName}'. Root driver is null");
}
Task.Delay(3000).Wait();
return this;
}
/// <summary>
/// Sets the main window size.
/// </summary>
/// <param name="size">WindowSize enum</param>
public void SetMainWindowSize(WindowSize size)
{
if (this.MainWindowHandler == IntPtr.Zero)
{
// Attach to the scope & reset MainWindowHandler
this.Attach(this.InitScope);
}
WindowHelper.SetWindowSize(this.MainWindowHandler, size);
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public (int CenterX, int CenterY) GetMainWindowCenter()
{
return WindowHelper.GetWindowCenter(this.MainWindowHandler);
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(int Left, int Top, int Right, int Bottom)</returns>
public (int Left, int Top, int Right, int Bottom) GetMainWindowRect()
{
return WindowHelper.GetWindowRect(this.MainWindowHandler);
}
/// <summary>
/// Launches the specified executable with optional arguments and simulates a delay before and after execution.
/// </summary>
/// <param name="executablePath">The full path to the executable to launch.</param>
/// <param name="arguments">Optional command-line arguments to pass to the executable.</param>
/// <param name="msPreAction">The number of milliseconds to wait before launching the executable. Default is 0 ms.</param>
/// <param name="msPostAction">The number of milliseconds to wait after launching the executable. Default is 2000 ms.</param>
public void StartExe(string executablePath, string arguments = "", int msPreAction = 0, int msPostAction = 2000)
{
PerformAction(
() =>
{
StartExeInternal(executablePath, arguments);
},
msPreAction,
msPostAction);
}
private void StartExeInternal(string executablePath, string arguments = "")
{
var processInfo = new ProcessStartInfo
{
FileName = executablePath,
Arguments = arguments,
UseShellExecute = true,
};
Process.Start(processInfo);
}
/// <summary>
/// Terminates all running processes that match the specified process name.
/// Waits for each process to exit after sending the kill signal.
/// </summary>
/// <param name="processName">The name of the process to terminate (without extension, e.g., "notepad").</param>
public void KillAllProcessesByName(string processName)
{
foreach (var process in Process.GetProcessesByName(processName))
{
process.Kill();
process.WaitForExit();
}
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
@@ -633,26 +254,5 @@ namespace Microsoft.PowerToys.UITest
Task.Delay(msPostAction).Wait();
}
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
/// <param name="action">The action to perform on the element.</param>
/// <param name="msPreAction">The number of milliseconds to wait before the action. Default value is 500 ms</param>
/// <param name="msPostAction">The number of milliseconds to wait after the action. Default value is 500 ms</param>
protected void PerformAction(Action action, int msPreAction = 500, int msPostAction = 500)
{
if (msPreAction > 0)
{
Task.Delay(msPreAction).Wait();
}
action();
if (msPostAction > 0)
{
Task.Delay(msPostAction).Wait();
}
}
}
}

View File

@@ -15,58 +15,39 @@ namespace Microsoft.PowerToys.UITest
/// <summary>
/// Nested class for test initialization.
/// </summary>
public class SessionHelper
internal class SessionHelper
{
// Default session path is PowerToys settings dashboard
private readonly string sessionPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.PowerToysSettings);
private readonly string runnerPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.Runner);
private string? locationPath;
private static WindowsDriver<WindowsElement>? root;
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement>? Driver { get; set; }
private static Process? appDriver;
private Process? runner;
private PowerToysModule scope;
private Process? appDriver;
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public SessionHelper(PowerToysModule scope)
{
this.scope = scope;
this.sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
CheckWinAppDriverAndRoot();
var runnerProcessInfo = new ProcessStartInfo
var winAppDriverProcessInfo = new ProcessStartInfo
{
FileName = locationPath + this.runnerPath,
FileName = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe",
Verb = "runas",
};
if (scope == PowerToysModule.PowerToysSettings)
{
this.ExitExe(runnerProcessInfo.FileName);
this.runner = Process.Start(runnerProcessInfo);
}
}
this.appDriver = Process.Start(winAppDriverProcessInfo);
/// <summary>
/// Initializes WinAppDriver And Root.
/// </summary>
public void CheckWinAppDriverAndRoot()
{
if (SessionHelper.root == null || SessionHelper.appDriver?.SessionId == null || SessionHelper.appDriver == null || SessionHelper.appDriver.HasExited)
{
this.StartWindowsAppDriverApp();
var desktopCapabilities = new AppiumOptions();
desktopCapabilities.AddAdditionalCapability("app", "Root");
SessionHelper.root = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), desktopCapabilities);
}
var desktopCapabilities = new AppiumOptions();
desktopCapabilities.AddAdditionalCapability("app", "Root");
this.Root = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), desktopCapabilities);
// Set default timeout to 5 seconds
this.Root.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
@@ -75,8 +56,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="scope">The PowerToys module to start.</param>
public SessionHelper Init()
{
this.ExitExe(this.locationPath + this.sessionPath);
this.StartExe(this.locationPath + this.sessionPath);
this.StartExe(locationPath + this.sessionPath);
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
@@ -91,11 +71,8 @@ namespace Microsoft.PowerToys.UITest
ExitScopeExe();
try
{
if (this.scope == PowerToysModule.PowerToysSettings)
{
runner?.Kill();
runner?.WaitForExit(); // Optional: Wait for the process to exit
}
appDriver?.Kill();
appDriver?.WaitForExit(); // Optional: Wait for the process to exit
}
catch (Exception ex)
{
@@ -104,30 +81,6 @@ namespace Microsoft.PowerToys.UITest
}
}
/// <summary>
/// Exit a exe.
/// </summary>
/// <param name="appPath">The path to the application executable.</param>
public void ExitExe(string appPath)
{
// Exit Exe
string exeName = Path.GetFileNameWithoutExtension(appPath);
Process[] processes = Process.GetProcessesByName(exeName);
foreach (Process process in processes)
{
try
{
process.Kill();
process.WaitForExit(); // Optional: Wait for the process to exit
}
catch (Exception ex)
{
Assert.Fail($"Failed to terminate process {process.ProcessName} (ID: {process.Id}): {ex.Message}");
}
}
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
@@ -136,36 +89,34 @@ namespace Microsoft.PowerToys.UITest
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
this.Driver = NewWindowsDriver(opts);
Console.WriteLine($"appPath: {appPath}");
this.Driver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), opts);
// Set default timeout to 5 seconds
this.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
/// Starts a new exe and takes control of it.
/// Exit a exe.
/// </summary>
/// <param name="info">The path to the application executable.</param>
private WindowsDriver<WindowsElement> NewWindowsDriver(AppiumOptions info)
/// <param name="path">The path to the application executable.</param>
public void ExitExe(string path)
{
// Create driver with retry
var timeout = TimeSpan.FromMinutes(2);
var retryInterval = TimeSpan.FromSeconds(5);
DateTime startTime = DateTime.Now;
// Exit Exe
string exeName = Path.GetFileNameWithoutExtension(path);
while (true)
// PowerToys.FancyZonesEditor
Process[] processes = Process.GetProcessesByName(exeName);
foreach (Process process in processes)
{
try
{
var res = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), info);
return res;
process.Kill();
process.WaitForExit(); // Optional: Wait for the process to exit
}
catch (Exception)
catch (Exception ex)
{
if (DateTime.Now - startTime > timeout)
{
throw;
}
Task.Delay(retryInterval).Wait();
CheckWinAppDriverAndRoot();
Assert.Fail($"Failed to terminate process {process.ProcessName} (ID: {process.Id}): {ex.Message}");
}
}
}
@@ -183,31 +134,16 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public void RestartScopeExe()
{
ExitScopeExe();
ExitExe(sessionPath);
StartExe(locationPath + sessionPath);
}
public WindowsDriver<WindowsElement> GetRoot()
{
return SessionHelper.root!;
}
public WindowsDriver<WindowsElement> GetRoot() => this.Root;
public WindowsDriver<WindowsElement> GetDriver()
{
Assert.IsNotNull(this.Driver, $"Failed to get driver. Driver is null.");
return this.Driver;
}
private void StartWindowsAppDriverApp()
{
var winAppDriverProcessInfo = new ProcessStartInfo
{
FileName = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe",
Verb = "runas",
};
this.ExitExe(winAppDriverProcessInfo.FileName);
SessionHelper.appDriver = Process.Start(winAppDriverProcessInfo);
}
}
}

View File

@@ -8,9 +8,6 @@
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<ItemGroup>

View File

@@ -6,16 +6,10 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using Windows.Devices.Display.Core;
using Windows.Foundation.Metadata;
using static Microsoft.PowerToys.UITest.UITestBase.NativeMethods;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -23,37 +17,19 @@ namespace Microsoft.PowerToys.UITest
/// Base class that should be inherited by all Test Classes.
/// </summary>
[TestClass]
public class UITestBase : IDisposable
public class UITestBase
{
public required TestContext TestContext { get; set; }
public Session Session { get; set; }
public required Session Session { get; set; }
public bool IsInPipeline { get; }
public static MonitorInfoData.ParamsWrapper MonitorInfoData { get; set; } = new MonitorInfoData.ParamsWrapper() { Monitors = new List<MonitorInfoData.MonitorInfoDataWrapper>() };
private readonly SessionHelper sessionHelper;
private readonly PowerToysModule scope;
private readonly WindowSize size;
private SessionHelper? sessionHelper;
private System.Threading.Timer? screenshotTimer;
private string? screenshotDirectory;
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified)
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings)
{
this.IsInPipeline = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("platform"));
Console.WriteLine($"Running tests on platform: {Environment.GetEnvironmentVariable("platform")}");
if (IsInPipeline)
{
NativeMethods.ChangeDisplayResolution(1920, 1080);
NativeMethods.GetMonitorInfo();
// Escape Popups before starting
System.Windows.Forms.SendKeys.SendWait("{ESC}");
}
this.scope = scope;
this.size = size;
this.sessionHelper = new SessionHelper(scope).Init();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver());
}
/// <summary>
@@ -62,22 +38,6 @@ namespace Microsoft.PowerToys.UITest
[TestInitialize]
public void TestInit()
{
CloseOtherApplications();
if (IsInPipeline)
{
screenshotDirectory = Path.Combine(this.TestContext.TestResultsDirectory ?? string.Empty, "UITestScreenshots_" + Guid.NewGuid().ToString());
Directory.CreateDirectory(screenshotDirectory);
// Take screenshot every 1 second
screenshotTimer = new System.Threading.Timer(ScreenCapture.TimerCallback, screenshotDirectory, TimeSpan.Zero, TimeSpan.FromMilliseconds(1000));
// Escape Popups before starting
System.Windows.Forms.SendKeys.SendWait("{ESC}");
}
this.sessionHelper = new SessionHelper(scope).Init();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), scope, size);
if (this.scope == PowerToysModule.PowerToysSettings)
{
// close Debug warning dialog if any
@@ -90,32 +50,12 @@ namespace Microsoft.PowerToys.UITest
}
/// <summary>
/// Cleanups the test.
/// UnInitializes the test.
/// </summary>
[TestCleanup]
public void TestCleanup()
public void TestClean()
{
if (IsInPipeline)
{
screenshotTimer?.Change(Timeout.Infinite, Timeout.Infinite);
Dispose();
if (TestContext.CurrentTestOutcome is UnitTestOutcome.Failed
or UnitTestOutcome.Error
or UnitTestOutcome.Unknown)
{
Task.Delay(1000).Wait();
AddScreenShotsToTestResultsDirectory();
}
}
this.Session.Cleanup();
this.sessionHelper!.Cleanup();
}
public void Dispose()
{
screenshotTimer?.Dispose();
GC.SuppressFinalize(this);
this.sessionHelper.Cleanup();
}
/// <summary>
@@ -124,143 +64,47 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected T Find<T>(By by, int timeoutMS = 5000, bool global = false)
protected T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.Find<T>(by, timeoutMS, global);
return this.Session.Find<T>(by, timeoutMS);
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(name, timeoutMS)
/// Shortcut for this.Session.Find<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected T Find<T>(string name, int timeoutMS = 5000, bool global = false)
protected T Find<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.Find<T>(By.Name(name), timeoutMS, global);
return this.Session.Find<T>(By.Name(name), timeoutMS);
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected Element Find(By by, int timeoutMS = 5000, bool global = false)
protected Element Find(By by, int timeoutMS = 3000)
{
return this.Session.Find(by, timeoutMS, global);
return this.Session.Find(by, timeoutMS);
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(name, timeoutMS)
/// Shortcut for this.Session.Find<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
protected Element Find(string name, int timeoutMS = 5000, bool global = false)
protected Element Find(string name, int timeoutMS = 3000)
{
return this.Session.Find(name, timeoutMS, global);
}
/// <summary>
/// Has only one Element or its derived class by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.FindAll<T>(by, timeoutMS, global).Count == 1;
}
/// <summary>
/// Shortcut for this.Session.HasOne<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(By by, int timeoutMS = 5000, bool global = false)
{
return this.Session.HasOne<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.HasOne<T>(name, timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Session.HasOne<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.HasOne<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
public bool HasOne(string name, int timeoutMS = 5000, bool global = false)
{
return this.Session.HasOne<Element>(name, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.Has<T>(by, timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Session.FindAll<T>(by, timeoutMS, global).Count >= 1;
}
/// <summary>
/// Shortcut for this.Session.Has<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(By by, int timeoutMS = 5000, bool global = false)
{
return this.Session.Has<Element>(by, timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.Has<T>(By.Name(name), timeoutMS)
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
return this.Session.Has<T>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Shortcut for this.Session.Has<Element>(name, timeoutMS)
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
public bool Has(string name, int timeoutMS = 5000, bool global = false)
{
return this.Session.Has<Element>(name, timeoutMS, global);
return this.Session.Find(name, timeoutMS);
}
/// <summary>
@@ -269,12 +113,12 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.FindAll<T>(by, timeoutMS, global);
return this.Session.FindAll<T>(by, timeoutMS);
}
/// <summary>
@@ -283,12 +127,12 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<T> FindAll<T>(string name, int timeoutMS = 3000)
where T : Element, new()
{
return this.Session.FindAll<T>(By.Name(name), timeoutMS, global);
return this.Session.FindAll<T>(By.Name(name), timeoutMS);
}
/// <summary>
@@ -296,11 +140,11 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.Session.FindAll<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<Element> FindAll(By by, int timeoutMS = 3000)
{
return this.Session.FindAll<Element>(by, timeoutMS, global);
return this.Session.FindAll<Element>(by, timeoutMS);
}
/// <summary>
@@ -308,137 +152,11 @@ namespace Microsoft.PowerToys.UITest
/// Shortcut for this.Session.FindAll<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
protected ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 5000, bool global = false)
protected ReadOnlyCollection<Element> FindAll(string name, int timeoutMS = 3000)
{
return this.Session.FindAll<Element>(By.Name(name), timeoutMS, global);
}
/// <summary>
/// Scrolls the page
/// </summary>
/// <param name="scrollCount">The number of scroll attempts.</param>
/// <param name="direction">The direction to scroll.</param>
/// <param name="msPreAction">Pre-action delay in milliseconds.</param>
/// <param name="msPostAction">Post-action delay in milliseconds.</param>
public void Scroll(int scrollCount = 5, string direction = "Up", int msPreAction = 500, int msPostAction = 500)
{
MouseActionType mouseAction = direction == "Up" ? MouseActionType.ScrollUp : MouseActionType.ScrollDown;
for (int i = 0; i < scrollCount; i++)
{
Session.PerformMouseAction(mouseAction, msPreAction, msPostAction); // Ensure settings are visible
}
}
/// <summary>
/// Captures the last screenshot when the test fails.
/// </summary>
protected void CaptureLastScreenshot()
{
// Implement your screenshot capture logic here
// For example, save a screenshot to a file and return the file path
string screenshotPath = Path.Combine(this.TestContext.TestResultsDirectory ?? string.Empty, "last_screenshot.png");
this.Session.Root.GetScreenshot().SaveAsFile(screenshotPath, ScreenshotImageFormat.Png);
// Save screenshot to screenshotPath & upload to test attachment
this.TestContext.AddResultFile(screenshotPath);
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public Color GetPixelColor(int x, int y)
{
return WindowHelper.GetPixelColor(x, y);
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates as a string.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public string GetPixelColorString(int x, int y)
{
return WindowHelper.GetPixelColorString(x, y);
}
/// <summary>
/// Gets the size of the display.
/// </summary>
/// <returns>
/// A tuple containing the width and height of the display.
/// </returns
public Tuple<int, int> GetDisplaySize()
{
return WindowHelper.GetDisplaySize();
}
/// <summary>
/// Sends a combination of keys.
/// </summary>
/// <param name="keys">The keys to send.</param>
public void SendKeys(params Key[] keys)
{
this.Session.SendKeys(keys);
}
/// <summary>
/// Sends a sequence of keys.
/// </summary>
/// <param name="keys">An array of keys to send.</param>
public void SendKeySequence(params Key[] keys)
{
this.Session.SendKeySequence(keys);
}
/// <summary>
        /// Gets the current position of the mouse cursor as a tuple.
        /// </summary>
        /// <returns>A tuple containing the X and Y coordinates of the cursor.</returns>
public Tuple<int, int> GetMousePosition()
{
return this.Session.GetMousePosition();
}
/// <summary>
/// Gets the screen center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public (int CenterX, int CenterY) GetScreenCenter()
{
return WindowHelper.GetScreenCenter();
}
public bool IsWindowOpen(string windowName)
{
return WindowHelper.IsWindowOpen(windowName);
}
/// <summary>
    /// Moves the mouse cursor to the specified screen coordinates.
    /// </summary>
    /// <param name="x">The new x-coordinate of the cursor.</param>
    /// <param name="y">The new y-coordinate of the cursor.</param
public void MoveMouseTo(int x, int y)
{
        this.Session.MoveMouseTo(x, y);
}
protected void AddScreenShotsToTestResultsDirectory()
{
if (screenshotDirectory != null)
{
foreach (string file in Directory.GetFiles(screenshotDirectory))
{
this.TestContext.AddResultFile(file);
}
}
return this.Session.FindAll<Element>(By.Name(name), timeoutMS);
}
/// <summary>
@@ -446,8 +164,8 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public void RestartScopeExe()
{
this.sessionHelper!.RestartScopeExe();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), this.scope, this.size);
this.sessionHelper.RestartScopeExe();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver());
return;
}
@@ -456,194 +174,8 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
public void ExitScopeExe()
{
this.sessionHelper!.ExitScopeExe();
this.sessionHelper.ExitScopeExe();
return;
}
private void CloseOtherApplications()
{
// Close other applications
var processNamesToClose = new List<string>
{
"PowerToys",
"PowerToys.Settings",
"PowerToys.FancyZonesEditor",
};
foreach (var processName in processNamesToClose)
{
foreach (var process in Process.GetProcessesByName(processName))
{
process.Kill();
process.WaitForExit();
}
}
}
public class NativeMethods
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public int StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
}
[DllImport("user32.dll")]
private static extern int EnumDisplaySettings(IntPtr deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll")]
private static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
private static extern bool EnumDisplayDevices(IntPtr lpDevice, int iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
[DllImport("user32.dll")]
private static extern int ChangeDisplaySettings(ref DEVMODE devMode, int flags);
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
private static extern int ChangeDisplaySettingsEx(IntPtr lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, uint dwflags, IntPtr lParam);
private const int DM_PELSWIDTH = 0x80000;
private const int DM_PELSHEIGHT = 0x100000;
public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_TEST = 0x00000002;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int DISP_CHANGE_SUCCESSFUL = 0;
public const int DISP_CHANGE_RESTART = 1;
public const int DISP_CHANGE_FAILED = -1;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DmDeviceName;
public short DmSpecVersion;
public short DmDriverVersion;
public short DmSize;
public short DmDriverExtra;
public int DmFields;
public int DmPositionX;
public int DmPositionY;
public int DmDisplayOrientation;
public int DmDisplayFixedOutput;
public short DmColor;
public short DmDuplex;
public short DmYResolution;
public short DmTTOption;
public short DmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DmFormName;
public short DmLogPixels;
public int DmBitsPerPel;
public int DmPelsWidth;
public int DmPelsHeight;
public int DmDisplayFlags;
public int DmDisplayFrequency;
public int DmICMMethod;
public int DmICMIntent;
public int DmMediaType;
public int DmDitherType;
public int DmReserved1;
public int DmReserved2;
public int DmPanningWidth;
public int DmPanningHeight;
}
public static void GetMonitorInfo()
{
int deviceIndex = 0;
DISPLAY_DEVICE d = default(DISPLAY_DEVICE);
d.cb = Marshal.SizeOf(d);
Console.WriteLine("monitor list :");
while (EnumDisplayDevices(IntPtr.Zero, deviceIndex, ref d, 0))
{
Console.WriteLine($"monitor {deviceIndex + 1}:");
Console.WriteLine($" name: {d.DeviceName}");
Console.WriteLine($"  string: {d.DeviceString}");
Console.WriteLine($"  ID: {d.DeviceID}");
Console.WriteLine($"  key: {d.DeviceKey}");
Console.WriteLine();
DEVMODE dm = default(DEVMODE);
dm.DmSize = (short)Marshal.SizeOf<DEVMODE>();
int modeNum = 0;
while (EnumDisplaySettings(d.DeviceName, modeNum, ref dm) > 0)
{
MonitorInfoData.Monitors.Add(new MonitorInfoData.MonitorInfoDataWrapper()
{
DeviceName = d.DeviceName,
DeviceString = d.DeviceString,
DeviceID = d.DeviceID,
DeviceKey = d.DeviceKey,
PelsWidth = dm.DmPelsWidth,
PelsHeight = dm.DmPelsHeight,
DisplayFrequency = dm.DmDisplayFrequency,
});
Console.WriteLine($"  mode {modeNum}: {dm.DmPelsWidth}x{dm.DmPelsHeight} @ {dm.DmDisplayFrequency}Hz");
modeNum++;
}
deviceIndex++;
d.cb = Marshal.SizeOf(d); // Reset the size for the next device
}
}
public static void ChangeDisplayResolution(int PelsWidth, int PelsHeight)
{
Screen screen = Screen.PrimaryScreen!;
if (screen.Bounds.Width == PelsWidth && screen.Bounds.Height == PelsHeight)
{
return;
}
DEVMODE devMode = default(DEVMODE);
devMode.DmDeviceName = new string(new char[32]);
devMode.DmFormName = new string(new char[32]);
devMode.DmSize = (short)Marshal.SizeOf<DEVMODE>();
int modeNum = 0;
while (EnumDisplaySettings(IntPtr.Zero, modeNum, ref devMode) > 0)
{
Console.WriteLine($"Mode {modeNum}: {devMode.DmPelsWidth}x{devMode.DmPelsHeight} @ {devMode.DmDisplayFrequency}Hz");
modeNum++;
}
devMode.DmPelsWidth = PelsWidth;
devMode.DmPelsHeight = PelsHeight;
int result = NativeMethods.ChangeDisplaySettings(ref devMode, NativeMethods.CDS_TEST);
if (result == DISP_CHANGE_SUCCESSFUL)
{
result = ChangeDisplaySettings(ref devMode, CDS_UPDATEREGISTRY);
if (result == DISP_CHANGE_SUCCESSFUL)
{
Console.WriteLine($"Changing display resolution to {devMode.DmPelsWidth}x{devMode.DmPelsHeight}");
}
else
{
Console.WriteLine($"Failed to change display resolution. Error code: {result}");
}
}
else if (result == DISP_CHANGE_RESTART)
{
Console.WriteLine($"Changing display resolution to {devMode.DmPelsWidth}x{devMode.DmPelsHeight} requires a restart");
}
else
{
Console.WriteLine($"Failed to change display resolution. Error code: {result}");
}
}
}
}
}

View File

@@ -1,160 +0,0 @@
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
public static class VisualAssert
{
/// <summary>
/// Asserts current visual state of the element is equal with base line image.
/// To use this VisualAssert, you need to set Window Theme to Light-Mode to avoid Theme color difference in baseline image.
/// Such limitation could be removed either Auto-generate baseline image for both Light & Dark mode
/// </summary>
/// <param name="testContext">TestContext object</param>
/// <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, string scenarioSubname = "")
{
var pipelinePlatform = Environment.GetEnvironmentVariable("platform");
// Perform visual validation only in the pipeline
if (string.IsNullOrEmpty(pipelinePlatform))
{
Console.WriteLine("Skip visual validation in the local run.");
return;
}
if (element == null)
{
Assert.Fail("Element object is null or invalid");
}
var stackTrace = new StackTrace();
var callerFrame = stackTrace.GetFrame(1);
var callerMethod = callerFrame?.GetMethod();
var callerName = callerMethod?.Name;
var callerClassName = callerMethod?.DeclaringType?.Name;
if (string.IsNullOrEmpty(callerName) || string.IsNullOrEmpty(callerClassName))
{
Assert.Fail("Unable to determine the caller method and class name.");
}
if (string.IsNullOrWhiteSpace(scenarioSubname))
{
scenarioSubname = string.Join("_", callerClassName, callerName, pipelinePlatform);
}
else
{
scenarioSubname = string.Join("_", callerClassName, callerName, scenarioSubname.Trim(), pipelinePlatform);
}
var baselineImageResourceName = callerMethod!.DeclaringType!.Assembly.GetManifestResourceNames().Where(name => name.Contains(scenarioSubname)).FirstOrDefault();
var tempTestImagePath = GetTempFilePath(scenarioSubname, "test", ".png");
element.SaveToPngFile(tempTestImagePath);
if (string.IsNullOrEmpty(baselineImageResourceName)
|| !Path.GetFileNameWithoutExtension(baselineImageResourceName).EndsWith(scenarioSubname))
{
testContext?.AddResultFile(tempTestImagePath);
Assert.Fail($"Baseline image for scenario {scenarioSubname} can't be found, test image saved in file://{tempTestImagePath.Replace('\\', '/')}");
}
var tempBaselineImagePath = GetTempFilePath(scenarioSubname, "baseline", Path.GetExtension(baselineImageResourceName));
bool isSame = false;
using (var stream = callerMethod!.DeclaringType!.Assembly.GetManifestResourceStream(baselineImageResourceName))
{
if (stream == null)
{
Assert.Fail($"Resource stream '{baselineImageResourceName}' is null.");
}
using (var baselineImage = new Bitmap(stream))
{
using (var testImage = new Bitmap(tempTestImagePath))
{
isSame = VisualAssert.AreEqual(baselineImage, testImage);
if (!isSame)
{
// Copy baseline image to temp folder as well
baselineImage.Save(tempBaselineImagePath);
}
}
}
}
if (!isSame)
{
if (testContext != null)
{
testContext.AddResultFile(tempBaselineImagePath);
testContext.AddResultFile(tempTestImagePath);
}
Assert.Fail($"Fail to validate visual result for scenario {scenarioSubname}, baseline image can be found file://{tempBaselineImagePath.Replace('\\', '/')}, and test image can be found file://{tempTestImagePath.Replace('\\', '/')}");
}
}
/// <summary>
/// Get temp file path
/// </summary>
/// <param name="scenario">scenario name</param>
/// <param name="imageType">baseline or test image</param>
/// <param name="extension">image file extension</param>
/// <returns>full temp file path</returns>
private static string GetTempFilePath(string scenario, string imageType, string extension)
{
var tempFileFullName = $"{scenario}_{imageType}{extension}";
// Remove invalid filename character if any
Path.GetInvalidFileNameChars().ToList().ForEach(c => tempFileFullName = tempFileFullName.Replace(c, '-'));
return Path.Combine(Path.GetTempPath(), tempFileFullName);
}
/// <summary>
/// Test if two images are equal bit-by-bit
/// </summary>
/// <param name="baselineImage">baseline image</param>
/// <param name="testImage">test image</param>
/// <returns>true if are equal,otherwise false</returns>
private static bool AreEqual(Bitmap baselineImage, Bitmap testImage)
{
if (baselineImage.Width != testImage.Width || baselineImage.Height != testImage.Height)
{
return false;
}
// WinAppDriver sometimes adds a border to the screenshot (around 2 pix width), and it is not always consistent.
// So we exclude the border when comparing the images, and usually it is the edge of the windows, won't affect the comparison.
int excludeBorderWidth = 5, excludeBorderHeight = 5;
for (int x = excludeBorderWidth; x < baselineImage.Width - excludeBorderWidth; x++)
{
for (int y = excludeBorderHeight; y < baselineImage.Height - excludeBorderHeight; y++)
{
if (!VisualHelper.PixIsSame(baselineImage.GetPixel(x, y), testImage.GetPixel(x, y)))
{
return false;
}
}
}
return true;
}
}
}

View File

@@ -1,33 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.UITest
{
internal static class VisualHelper
{
/// <summary>
/// Compare two pixels with a fuzz factor
/// </summary>
/// <param name="c1">base color</param>
/// <param name="c2">test color</param>
/// <param name="fuzz">fuzz factor, default is 10</param>
/// <returns>true if same, otherwise is false</returns>
public static bool PixIsSame(Color c1, Color c2, int fuzz = 10)
{
return Math.Abs(c1.A - c2.A) <= fuzz && Math.Abs(c1.R - c2.R) <= fuzz && Math.Abs(c1.G - c2.G) <= fuzz && Math.Abs(c1.B - c2.B) <= fuzz;
}
}
}

View File

@@ -1,331 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
internal static class WindowHelper
{
internal const string AdministratorPrefix = "Administrator: ";
/// <summary>
/// Sets the main window size.
/// </summary>
/// <param name="size">WindowSize enum</param>
public static void SetWindowSize(IntPtr windowHandler, WindowSize size)
{
if (size == WindowSize.UnSpecified)
{
return;
}
int width = 0, height = 0;
switch (size)
{
case WindowSize.Small:
width = 640;
height = 480;
break;
case WindowSize.Small_Vertical:
width = 480;
height = 640;
break;
case WindowSize.Medium:
width = 1024;
height = 768;
break;
case WindowSize.Medium_Vertical:
width = 768;
height = 1024;
break;
case WindowSize.Large:
width = 1920;
height = 1080;
break;
case WindowSize.Large_Vertical:
width = 1080;
height = 1920;
break;
}
if (width > 0 && height > 0)
{
WindowHelper.SetMainWindowSize(windowHandler, width, height);
}
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public static (int CenterX, int CenterY) GetWindowCenter(IntPtr windowHandler)
{
if (windowHandler == IntPtr.Zero)
{
return (0, 0);
}
else
{
var rect = ApiHelper.GetWindowCenter(windowHandler);
return (rect.CenterX, rect.CenterY);
}
}
/// <summary>
/// Gets the main window center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public static (int Left, int Top, int Right, int Bottom) GetWindowRect(IntPtr windowHandler)
{
if (windowHandler == IntPtr.Zero)
{
return (0, 0, 0, 0);
}
else
{
var rect = ApiHelper.GetWindowRect(windowHandler);
return (rect.Left, rect.Top, rect.Right, rect.Bottom);
}
}
/// <summary>
/// Gets the screen center coordinates.
/// </summary>
/// <returns>(x, y)</returns>
public static (int CenterX, int CenterY) GetScreenCenter()
{
return ApiHelper.GetScreenCenter();
}
/// <summary>
/// Sets the main window size based on Width and Height.
/// </summary>
/// <param name="width">the width in pixel</param>
/// <param name="height">the height in pixel</param>
public static void SetMainWindowSize(IntPtr windowHandler, int width, int height)
{
if (windowHandler == IntPtr.Zero
|| width <= 0
|| height <= 0)
{
return;
}
ApiHelper.SetWindowPos(windowHandler, IntPtr.Zero, 0, 0, width, height, ApiHelper.SetWindowPosNoZorder | ApiHelper.SetWindowPosShowWindow);
// Wait for 1000ms after resize
Task.Delay(1000).Wait();
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public static Color GetPixelColor(int x, int y)
{
IntPtr hdc = ApiHelper.GetDC(IntPtr.Zero);
uint pixel = ApiHelper.GetPixel(hdc, x, y);
_ = ApiHelper.ReleaseDC(IntPtr.Zero, hdc);
int r = (int)(pixel & 0x000000FF);
int g = (int)((pixel & 0x0000FF00) >> 8);
int b = (int)((pixel & 0x00FF0000) >> 16);
return Color.FromArgb(r, g, b);
}
/// <summary>
/// Retrieves the color of the pixel at the specified screen coordinates as a string.
/// </summary>
/// <param name="x">The X coordinate on the screen.</param>
/// <param name="y">The Y coordinate on the screen.</param>
/// <returns>The color of the pixel at the specified coordinates.</returns>
public static string GetPixelColorString(int x, int y)
{
Color color = WindowHelper.GetPixelColor(x, y);
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
}
/// <summary>
/// Gets the size of the display.
/// </summary>
/// <returns>
/// A tuple containing the width and height of the display.
/// </returns
public static Tuple<int, int> GetDisplaySize()
{
IntPtr hdc = ApiHelper.GetDC(IntPtr.Zero);
int screenWidth = ApiHelper.GetDeviceCaps(hdc, ApiHelper.DESKTOPHORZRES);
int screenHeight = ApiHelper.GetDeviceCaps(hdc, ApiHelper.DESKTOPVERTRES);
_ = ApiHelper.ReleaseDC(IntPtr.Zero, hdc);
return Tuple.Create(screenWidth, screenHeight);
}
public static bool IsWindowOpen(string windowName)
{
var matchingWindows = ApiHelper.FindDesktopWindowHandler([windowName, AdministratorPrefix + windowName]);
return matchingWindows.Count > 0;
}
internal static class ApiHelper
{
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public const uint SetWindowPosNoMove = 0x0002;
public const uint SetWindowPosNoZorder = 0x0004;
public const uint SetWindowPosShowWindow = 0x0040;
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
// Delegate for the EnumWindows callback function
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
// P/Invoke declaration for EnumWindows
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
// P/Invoke declaration for GetWindowTextLength
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetWindowTextLength(IntPtr hWnd);
// P/Invoke declaration for GetWindowText
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
public static extern uint GetPixel(IntPtr hdc, int x, int y);
[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public const int DESKTOPHORZRES = 118;
public const int DESKTOPVERTRES = 117;
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
// Define the Win32 RECT structure
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // X coordinate of the left edge of the window
public int Top; // Y coordinate of the top edge of the window
public int Right; // X coordinate of the right edge of the window
public int Bottom; // Y coordinate of the bottom edge of the window
}
// Import GetWindowRect API to retrieve window's screen coordinates
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
public static List<(IntPtr HWnd, string Title)> FindDesktopWindowHandler(string[] matchingWindowsTitles)
{
var windows = new List<(IntPtr HWnd, string Title)>();
_ = EnumWindows(
(hWnd, lParam) =>
{
int length = GetWindowTextLength(hWnd);
if (length > 0)
{
var builder = new StringBuilder(length + 1);
_ = GetWindowText(hWnd, builder, builder.Capacity);
var title = builder.ToString();
if (matchingWindowsTitles.Contains(title))
{
windows.Add((hWnd, title));
}
}
return true; // Continue enumeration
},
IntPtr.Zero);
return windows;
}
/// <summary>
/// Get the center point coordinates of a specified window (in screen coordinates)
/// </summary>
/// <param name="hWnd">The window handle</param>
/// <returns>The center point (x, y)</returns>
public static (int CenterX, int CenterY) GetWindowCenter(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
throw new ArgumentException("Invalid window handle");
}
if (GetWindowRect(hWnd, out RECT rect))
{
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
int centerX = rect.Left + (width / 2);
int centerY = rect.Top + (height / 2);
return (centerX, centerY);
}
else
{
throw new InvalidOperationException("Failed to retrieve window coordinates");
}
}
public static (int Left, int Top, int Right, int Bottom) GetWindowRect(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
throw new ArgumentException("Invalid window handle");
}
if (GetWindowRect(hWnd, out RECT rect))
{
return (rect.Left, rect.Top, rect.Right, rect.Bottom);
}
else
{
throw new InvalidOperationException("Failed to retrieve window coordinates");
}
}
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
public enum SystemMetric
{
ScreenWidth = 0, // Width of the primary screen in pixels (SM_CXSCREEN)
ScreenHeight = 1, // Height of the primary screen in pixels (SM_CYSCREEN)
VirtualScreenWidth = 78, // Width of the virtual screen that includes all monitors (SM_CXVIRTUALSCREEN)
VirtualScreenHeight = 79, // Height of the virtual screen that includes all monitors (SM_CYVIRTUALSCREEN)
MonitorCount = 80, // Number of display monitors (SM_CMONITORS, available on Windows XP+)
}
public static (int CenterX, int CenterY) GetScreenCenter()
{
int width = GetSystemMetrics((int)SystemMetric.ScreenWidth);
int height = GetSystemMetrics((int)SystemMetric.ScreenHeight);
return (width / 2, height / 2);
}
}
}
}

View File

@@ -6,7 +6,6 @@
<PropertyGroup>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<DefineConstants>TESTONLY</DefineConstants>
</PropertyGroup>
<PropertyGroup>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -13,7 +13,7 @@ namespace Hosts.UITests
public class HostModuleTests : UITestBase
{
public HostModuleTests()
: base(PowerToysModule.Hosts, WindowSize.Small_Vertical)
: base(PowerToysModule.Hosts)
{
}
@@ -31,17 +31,14 @@ namespace Hosts.UITests
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Basic.EmptyViewShouldWork")]
[TestCategory("Hosts File Editor #4")]
[TestMethod]
public void TestEmptyView()
{
this.CloseWarningDialog();
this.RemoveAllEntries();
// '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.TestContext, this.Find("Entries"), "EmptyView");
Assert.IsTrue(this.FindAll<HyperlinkButton>("Add an entry").Count == 1, "'Add an entry' button should be visible in the empty view");
// Click 'Add an entry' from empty-view for adding Host override rule
this.Find<HyperlinkButton>("Add an entry").Click();
@@ -49,10 +46,8 @@ namespace Hosts.UITests
this.AddEntry("192.168.0.1", "localhost", false, false);
// Should have one row now and not more empty view
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.TestContext, this.Find("Entries"), "NonEmptyView");
Assert.IsTrue(this.FindAll<Button>("Delete").Count == 1, "Should have one row now");
Assert.IsTrue(this.FindAll<HyperlinkButton>("Add an entry").Count == 0, "'Add an entry' button should be invisible if not empty view");
}
/// <summary>
@@ -63,20 +58,17 @@ namespace Hosts.UITests
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Basic.AddEntryButtonShouldWork")]
[TestCategory("Hosts File Editor #4")]
[TestMethod]
public void TestAddingEntry()
{
this.CloseWarningDialog();
this.RemoveAllEntries();
Assert.IsFalse(this.Has<Button>("Delete"), "Should have no row after removing all");
Assert.IsTrue(this.FindAll<Button>("Delete").Count == 0, "Should have no row after removing all");
this.AddEntry("192.168.0.1", "localhost", true);
Assert.IsTrue(this.Has<Button>("Delete"), "Should have one row now");
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"));
Assert.IsTrue(this.FindAll<Button>("Delete").Count == 1, "Should have one row now");
}
/// <summary>
@@ -90,8 +82,7 @@ namespace Hosts.UITests
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Basic.CanNotAddMoreThenNighHosts")]
[TestCategory("Hosts File Editor #5")]
[TestMethod]
public void TestTooManyHosts()
{
this.CloseWarningDialog();
@@ -125,48 +116,18 @@ namespace Hosts.UITests
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Basic.ErrorMessageShowupIfNotRunAsAdmin")]
[TestCategory("Hosts File Editor #8")]
[TestMethod]
public void TestErrorMessageWithNonAdminPermission()
{
if (this.Session.IsElevated == false)
{
this.CloseWarningDialog();
this.RemoveAllEntries();
this.CloseWarningDialog();
this.RemoveAllEntries();
// Add new URL override and a warning tip should be shown
this.AddEntry("192.168.0.1", "localhost", true);
// Add new URL override and a warning tip should be shown
this.AddEntry("192.168.0.1", "localhost", true);
Assert.IsTrue(
this.FindAll<TextBlock>("The hosts file cannot be saved because the program isn't running as administrator.").Count == 1,
"Should display host-file saving error if not run as administrator");
}
}
/// <summary>
/// Test No Error-message in the Hosts-File-Editor
/// <list type="bullet">
/// <item>
/// <description>Validating error message should be shown if not run as admin.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Basic.NoErrorMessageShowupIfRunAsAdmin")]
[TestCategory("Hosts File Editor #8")]
public void TestNoErrorMessageWithNonAdminPermission()
{
if (this.Session.IsElevated == true)
{
this.CloseWarningDialog();
this.RemoveAllEntries();
// Add new URL override and a warning tip should be shown
this.AddEntry("192.168.0.1", "localhost", true);
Assert.IsFalse(
this.FindAll<TextBlock>("The hosts file cannot be saved because the program isn't running as administrator.").Count > 0,
"Should display host-file saving error if not run as administrator");
}
Assert.IsTrue(
this.FindAll<TextBlock>("The hosts file cannot be saved because the program isn't running as administrator.").Count == 1,
"Should display host-file saving error if not run as administrator");
}
/// <summary>
@@ -183,8 +144,7 @@ namespace Hosts.UITests
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Basic.FiltersControlShouldWork")]
[TestCategory("Hosts File Editor #6")]
[TestMethod]
public void TestFilterControl()
{
this.CloseWarningDialog();
@@ -252,7 +212,7 @@ namespace Hosts.UITests
// Close-filter-panel
this.Find<Button>("Filters").Click();
Assert.IsFalse(this.FindAll<Button>("Clear filters").Count == 1, "Filter panel should be closed after clicking Filter Button");
Assert.IsFalse(this.FindAll<Button>("Clear filters").Count == 1, "Filter panel should be closed afer click Filter Button");
}
private void AddEntry(string ip, string host, bool active = true, bool clickAddEntryButton = true)
@@ -283,25 +243,25 @@ namespace Hosts.UITests
private void CloseWarningDialog()
{
// Find 'Accept' button which come in 'Warning' dialog
if (this.FindAll("Warning", 1000).Count > 0 &&
this.FindAll<Button>("Accept", 1000).Count > 0)
if (this.FindAll("Warning").Count > 0 &&
this.FindAll<Button>("Accept").Count > 0)
{
// Hide Warning dialog if any
this.Find<Button>("Accept", 1000).Click();
this.Find<Button>("Accept").Click();
}
}
private void RemoveAllEntries()
{
// Delete all existing host-override rules
foreach (var deleteBtn in this.FindAll<Button>("Delete", 1000))
foreach (var deleteBtn in this.FindAll<Button>("Delete"))
{
deleteBtn.Click();
this.Find<Button>("Yes", 1000).Click();
this.Find<Button>("Yes").Click();
}
// Should have no row left, and no more delete button
Assert.IsTrue(this.FindAll<Button>("Delete", 1000).Count == 0);
Assert.IsTrue(this.FindAll<Button>("Delete").Count == 0);
}
}
}

View File

@@ -17,17 +17,6 @@
<PropertyGroup>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\Hosts.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Baseline\HostModuleTests_TestAddingEntry_arm64.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestEmptyView_EmptyView_arm64.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestEmptyView_NonEmptyView_arm64.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestAddingEntry_x64Win10.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestEmptyView_EmptyView_x64Win10.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestEmptyView_NonEmptyView_x64Win10.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestAddingEntry_x64Win11.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestEmptyView_EmptyView_x64Win11.png" />
<EmbeddedResource Include="Baseline\HostModuleTests_TestEmptyView_NonEmptyView_x64Win11.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MSTest" />

View File

@@ -12,11 +12,6 @@ namespace Hosts.UITests
[TestClass]
public class HostsSettingTests : UITestBase
{
public HostsSettingTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Medium)
{
}
/// <summary>
/// Test Warning Dialog at startup
/// <list type="bullet">
@@ -34,9 +29,7 @@ namespace Hosts.UITests
/// </item>
/// </list>
/// </summary>
[TestMethod("Hosts.Settings.ShowWarningDialogIfRunAsAdmin")]
[TestCategory("Hosts File Editor #1")]
[TestCategory("Hosts File Editor #9")]
[TestMethod]
public void TestWarningDialog()
{
this.LaunchFromSetting(showWarning: true);
@@ -58,7 +51,10 @@ namespace Hosts.UITests
this.Find<Button>("Launch Hosts File Editor").Click();
this.Session.Attach(PowerToysModule.Hosts, WindowSize.Small_Vertical);
// wait for 500 ms to make sure Hosts File Editor is launched
Task.Delay(500).Wait();
this.Session.Attach(PowerToysModule.Hosts);
// Should show warning dialog
Assert.IsTrue(this.FindAll("Warning").Count > 0, "Should show warning dialog");
@@ -72,7 +68,7 @@ namespace Hosts.UITests
Assert.IsFalse(this.IsHostsFileEditorClosed(), "Hosts File Editor should NOT be closed after click Accept button in Warning Dialog");
// Close Hosts File Editor window
this.Session.CloseMainWindow();
this.Session.Find<Window>("Hosts File Editor").Close();
// Restore back to PowerToysSettings Session
this.Session.Attach(PowerToysModule.PowerToysSettings);
@@ -86,7 +82,7 @@ namespace Hosts.UITests
Assert.IsFalse(this.IsHostsFileEditorClosed(), "Hosts File Editor should NOT be closed");
// Close Hosts File Editor window
this.Session.CloseMainWindow();
this.Session.Find<Window>("Hosts File Editor").Close();
// Restore back to PowerToysSettings Session
this.Session.Attach(PowerToysModule.PowerToysSettings);
@@ -94,9 +90,14 @@ namespace Hosts.UITests
private bool IsHostsFileEditorClosed()
{
if (this.Session.FindAll<Window>("Hosts File Editor").Count == 0 && this.Session.FindAll<Window>("Administrator: Hosts File Editor").Count == 0)
try
{
return true;
this.Session.FindAll<Window>("Hosts File Editor");
}
catch (Exception ex)
{
// Validate if editor window closed by checking exception.Message
return ex.Message.Contains("Currently selected window has been closed");
}
return false;
@@ -120,7 +121,7 @@ namespace Hosts.UITests
// launch Hosts File Editor
this.Find<Button>("Launch Hosts File Editor").Click();
Task.Delay(2000).Wait();
Task.Delay(500).Wait();
this.Session.Attach(PowerToysModule.Hosts);
}

View File

@@ -611,7 +611,6 @@
<ContentDialog
x:Name="EntryDialog"
x:Uid="EntryDialog"
x:DataType="models:Entry"
IsPrimaryButtonEnabled="{Binding Valid, Mode=OneWay}"
Loaded="ContentDialog_Loaded_ApplyMargin"
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">

View File

@@ -139,23 +139,10 @@ namespace HostsUILib.Views
dialog.XamlRoot = XamlRoot;
dialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style;
dialog.Title = resourceLoader.GetString("WarningDialog_Title");
dialog.Content = new StackPanel
dialog.Content = new TextBlock
{
Children =
{
new TextBlock
{
Text = resourceLoader.GetString("WarningDialog_Text"),
TextWrapping = TextWrapping.Wrap,
},
new HyperlinkButton
{
Content = resourceLoader.GetString("WarningDialog_LearnMore"),
NavigateUri = new Uri("https://aka.ms/PowerToysOverview_HostsFileEditor"),
Padding = new Thickness(0),
Margin = new Thickness(0, 5, 0, 5),
},
},
Text = resourceLoader.GetString("WarningDialog_Text"),
TextWrapping = TextWrapping.Wrap,
};
dialog.PrimaryButtonText = resourceLoader.GetString("WarningDialog_AcceptBtn");
dialog.PrimaryButtonStyle = Application.Current.Resources["AccentButtonStyle"] as Style;

View File

@@ -11,9 +11,6 @@ using HostsUILib.Helpers;
namespace HostsUILib.Models
{
#if !TESTONLY
[Microsoft.UI.Xaml.Data.Bindable]
#endif
public partial class Entry : ObservableObject
{
private static readonly char[] _spaceCharacters = new char[] { ' ', '\t' };

View File

@@ -331,9 +331,6 @@
<data name="WarningDialog_Title" xml:space="preserve">
<value>Warning</value>
</data>
<data name="WarningDialog_LearnMore" xml:space="preserve">
<value>Learn more</value>
</data>
<data name="WindowAdminTitle" xml:space="preserve">
<value>Administrator: Hosts File Editor</value>
<comment>Title of the window when running as administrator. "Hosts File Editor" is the name of the utility. "Hosts" refers to the system hosts file, do not loc</comment>

View File

@@ -1,640 +0,0 @@
// 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;
using System.Globalization;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.Devices.Printers;
namespace MouseUtils.UITests
{
[TestClass]
public class FindMyMouseTests : UITestBase
{
/// <summary>
/// Test Warning Dialog at startup
/// <list type="bullet">
/// <item>
/// <description>Validating Warning-Dialog will be shown if 'Show a warning at startup' toggle is On.</description>
/// </item>
/// <item>
/// <description>Validating Warning-Dialog will NOT be shown if 'Show a warning at startup' toggle is Off.</description>
/// </item>
/// <item>
/// <description>Validating click 'Quit' button in Warning-Dialog, the Hosts File Editor window would be closed.</description>
/// </item>
/// <item>
/// <description>Validating click 'Accept' button in Warning-Dialog, the Hosts File Editor window would NOT be closed.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("MouseUtils.FindMyMouse.EnableFindMyMouse")]
[TestCategory("Mouse Utils #1")]
[TestCategory("Mouse Utils #2")]
[TestCategory("Mouse Utils #3")]
[TestCategory("Mouse Utils #4")]
public void TestEnableFindMyMouse()
{
LaunchFromSetting();
var settings = new FindMyMouseSettings();
settings.OverlayOpacity = "100";
settings.Radius = "50";
settings.InitialZoom = "1";
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
excludedApps.Click();
}
else
{
Assert.Fail("Activation method group not found.");
}
}
else
{
Assert.Fail("Find My Mouse group not found.");
}
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press Left Ctrl twice and verify the overlay appears.
VerifySpotlightSettings(ref settings);
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press any other key and verify the overlay disappears.
Session.SendKeys(Key.A);
VerifySpotlightDisappears(ref settings);
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press Left Ctrl twice and verify the overlay appears.
VerifySpotlightSettings(ref settings);
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press a mouse button and verify the overlay disappears.
Task.Delay(1000).Wait();
Session.PerformMouseAction(MouseActionType.LeftClick, 500, 1000);
VerifySpotlightDisappears(ref settings);
}
[TestMethod("MouseUtils.FindMyMouse.FindMyMouseDifferentSettings")]
[TestCategory("Mouse Utils #10")]
[TestCategory("Mouse Utils #11")]
[TestCategory("Mouse Utils #12")]
public void TestFindMyMouseDifferentSettings()
{
LaunchFromSetting();
var settings = new FindMyMouseSettings();
settings.OverlayOpacity = "100";
settings.Radius = "80";
settings.InitialZoom = "1";
settings.AnimationDuration = "0";
settings.BackgroundColor = "FF0000";
settings.SpotlightColor = "0000FF";
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
excludedApps.Click();
}
else
{
Assert.Fail("Excluded apps group not found.");
}
}
else
{
Assert.Fail("Find My Mouse group not found.");
}
// [Test Case]Test the different settings and verify they apply, Background color
// [Test Case]Test the different settings and verify they apply, Spotlight color
// [Test Case]Test the different settings and verify they apply, Spotlight radius
VerifySpotlightSettings(ref settings);
Session.SendKeys(Key.A);
VerifySpotlightDisappears(ref settings);
}
[TestMethod("MouseUtils.FindMyMouse.DisableFindMyMouse")]
[TestCategory("Mouse Utils #5")]
[TestCategory("Mouse Utils #6")]
public void TestDisableFindMyMouse()
{
LaunchFromSetting();
var settings = new FindMyMouseSettings();
settings.OverlayOpacity = "100";
settings.Radius = "50";
settings.InitialZoom = "1";
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom);
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
excludedApps.Click();
}
else
{
Assert.Fail("Activation method group not found.");
}
}
else
{
Assert.Fail("Find My Mouse group not found.");
}
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press Left Ctrl twice and verify the overlay appears.
// VerifySpotlightSettings(ref settings);
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
// [Test Case] Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
Task.Delay(1000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightDisappears(ref settings);
// [Test Case] Press Left Ctrl twice and verify the overlay appears
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
Task.Delay(2000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
Session.PerformMouseAction(MouseActionType.LeftClick);
}
[TestMethod("MouseUtils.FindMyMouse.DisableFindMyMouse3")]
[TestCategory("Mouse Utils #6")]
public void TestDisableFindMyMouse3()
{
LaunchFromSetting();
var settings = new FindMyMouseSettings();
settings.OverlayOpacity = "100";
settings.Radius = "50";
settings.InitialZoom = "1";
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom);
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
excludedApps.Click();
}
else
{
Assert.Fail("Activation method group not found.");
}
}
else
{
Assert.Fail("Find My Mouse group not found.");
}
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press Left Ctrl twice and verify the overlay appears.
// VerifySpotlightSettings(ref settings);
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
// [Test Case] Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
Task.Delay(1000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightDisappears(ref settings);
// [Test Case] Press Left Ctrl twice and verify the overlay appears
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
Task.Delay(2000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
Session.PerformMouseAction(MouseActionType.LeftClick);
}
[TestMethod("MouseUtils.FindMyMouse.DisableFindMyMouse2")]
[TestCategory("Mouse Utils #5")]
public void TestDisableFindMyMouse2()
{
LaunchFromSetting();
var settings = new FindMyMouseSettings();
settings.OverlayOpacity = "100";
settings.Radius = "50";
settings.InitialZoom = "1";
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>("Find My Mouse");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
// foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
// SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
excludedApps.Click();
}
else
{
Assert.Fail("Activation method group not found.");
}
}
else
{
Assert.Fail("Find My Mouse group not found.");
}
// [Test Case]Enable FindMyMouse. Then, without moving your mouse: Press Left Ctrl twice and verify the overlay appears.
// VerifySpotlightSettings(ref settings);
// [Test Case] Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
Task.Delay(2000).Wait();
Session.SendKey(Key.LCtrl, 0, 0);
Task.Delay(100).Wait();
Session.SendKey(Key.LCtrl, 0, 0);
VerifySpotlightDisappears(ref settings);
}
private void VerifySpotlightDisappears(ref FindMyMouseSettings settings)
{
Task.Delay(2000).Wait();
var location = Session.GetMousePosition();
int radius = int.Parse(settings.Radius, CultureInfo.InvariantCulture);
var colorSpotlight = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreNotEqual("#" + settings.SpotlightColor, colorSpotlight);
var colorBackground = this.GetPixelColorString(location.Item1 + radius + 50, location.Item2 + radius + 50);
Assert.AreNotEqual("#" + settings.BackgroundColor, colorBackground);
var colorBackground2 = this.GetPixelColorString(location.Item1 + radius + 100, location.Item2 + radius + 100);
Assert.AreNotEqual("#" + settings.BackgroundColor, colorBackground2);
}
private void VerifySpotlightAppears(ref FindMyMouseSettings settings)
{
Task.Delay(2000).Wait();
var location = Session.GetMousePosition();
int radius = int.Parse(settings.Radius, CultureInfo.InvariantCulture);
var colorSpotlight = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreEqual("#" + settings.SpotlightColor, colorSpotlight);
var colorSpotlight2 = this.GetPixelColorString(location.Item1 + radius - 1, location.Item2);
// Session.MoveMouseTo(location.Item1 + radius - 10, location.Item2);
Assert.AreEqual("#" + settings.SpotlightColor, colorSpotlight2);
Task.Delay(100).Wait();
var colorBackground = this.GetPixelColorString(location.Item1 + radius + 50, location.Item2 + radius + 50);
Assert.AreEqual("#" + settings.BackgroundColor, colorBackground);
var colorBackground2 = this.GetPixelColorString(location.Item1 + radius + 100, location.Item2 + radius + 100);
Assert.AreEqual("#" + settings.BackgroundColor, colorBackground2);
}
private void ActivateSpotlight(ref FindMyMouseSettings settings)
{
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1 - 200, xy.Item2 - 100);
Task.Delay(1000).Wait();
Session.PerformMouseAction(MouseActionType.LeftClick);
Task.Delay(1000).Wait();
if (settings.SelectedActivationMethod == FindMyMouseSettings.ActivationMethod.PressLeftControlTwice)
{
Session.SendKey(Key.LCtrl, 0, 0);
Task.Delay(200).Wait();
Session.SendKey(Key.LCtrl, 0, 0);
}
else if (settings.SelectedActivationMethod == FindMyMouseSettings.ActivationMethod.PressRightControlTwice)
{
Session.SendKey(Key.RCtrl, 0, 0);
Task.Delay(200).Wait();
Session.SendKey(Key.RCtrl, 0, 0);
}
else if (settings.SelectedActivationMethod == FindMyMouseSettings.ActivationMethod.ShakeMouse)
{
// Simulate shake mouse;
}
else if (settings.SelectedActivationMethod == FindMyMouseSettings.ActivationMethod.CustomShortcut)
{
// Simulate custom shortcut
}
}
private void VerifySpotlightSettings(ref FindMyMouseSettings settings, bool equal = true)
{
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
}
private void SetFindMyMouseActivationMethod(ref Custom? foundCustom, string method)
{
Assert.IsNotNull(foundCustom);
var groupActivation = foundCustom.Find<TextBlock>("Activation method");
if (groupActivation != null)
{
groupActivation.Click();
string findMyMouseComboBoxKey = "Activation method";
var foundElements = foundCustom.FindAll<ComboBox>(findMyMouseComboBoxKey);
if (foundElements.Count != 0)
{
var myMouseComboBox = foundCustom.Find<ComboBox>(findMyMouseComboBoxKey);
Assert.IsNotNull(myMouseComboBox);
myMouseComboBox.Click();
var selectedItem = myMouseComboBox.Find<NavigationViewItem>(method);
Assert.IsNotNull(selectedItem);
selectedItem.Click();
}
else
{
Assert.IsTrue(false, "ComboBox is not found in the setting page.");
}
}
else
{
Assert.Fail("Activation method group not found.");
}
}
private void SetFindMyMouseAppearanceBehavior(ref Custom foundCustom, ref FindMyMouseSettings settings)
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// groupAppearanceBehavior.Click();
if (foundCustom.FindAll<Slider>("Overlay opacity (%)").Count == 0)
{
groupAppearanceBehavior.Click();
}
// Set the BackGround color
var backgroundColor = foundCustom.Find<Group>("Background color");
Assert.IsNotNull(backgroundColor);
var button = backgroundColor.Find<Button>(By.XPath(".//Button"));
Assert.IsNotNull(button);
button.Click();
var popupWindow = this.Find<Window>("Popup");
Assert.IsNotNull(popupWindow);
Task.Delay(1000).Wait();
var colorModelComboBox = this.Find<ComboBox>("Color model");
Assert.IsNotNull(colorModelComboBox);
colorModelComboBox.Click();
var selectedItem = colorModelComboBox.Find<NavigationViewItem>("RGB");
selectedItem.Click();
Task.Delay(500).Wait();
var rgbHexEdit = this.Find<TextBox>("RGB hex");
Assert.IsNotNull(rgbHexEdit);
Task.Delay(500).Wait();
int retry = 5;
while (retry > 0)
{
Task.Delay(500).Wait();
rgbHexEdit.SetText(settings.BackgroundColor);
Task.Delay(500).Wait();
string rgbHex = rgbHexEdit.Text;
bool isValid = rgbHex.StartsWith('#') && rgbHex.Length == 7 && rgbHex.Substring(1) == settings.BackgroundColor;
Task.Delay(500).Wait();
if (isValid)
{
break;
}
retry--;
}
button.Click();
// Set the Spotlight color
var spotlightColor = foundCustom.Find<Group>("Spotlight color");
Assert.IsNotNull(spotlightColor);
var spotlightColorButton = spotlightColor.Find<Button>(By.XPath(".//Button"));
Assert.IsNotNull(spotlightColorButton);
spotlightColorButton.Click();
var spotlightColorPopupWindow = Session.Find<Window>("Popup");
Assert.IsNotNull(spotlightColorPopupWindow);
var spotlightColorModelComboBox = this.Find<ComboBox>("Color model");
Assert.IsNotNull(spotlightColorModelComboBox);
spotlightColorModelComboBox.Click();
var selectedItem2 = spotlightColorModelComboBox.Find<NavigationViewItem>("RGB");
Assert.IsNotNull(selectedItem2);
selectedItem2.Click();
Task.Delay(500).Wait();
var rgbHexEdit2 = this.Find<TextBox>("RGB hex");
Assert.IsNotNull(rgbHexEdit2);
Task.Delay(500).Wait();
retry = 5;
while (retry > 0)
{
Task.Delay(500).Wait();
rgbHexEdit2.SetText(settings.SpotlightColor);
Task.Delay(500).Wait();
string rgbHex = rgbHexEdit2.Text;
bool isValid = rgbHex.StartsWith('#') && rgbHex.Length == 7 && rgbHex.Substring(1) == settings.SpotlightColor;
Task.Delay(500).Wait();
if (isValid)
{
break;
}
retry--;
}
Task.Delay(500).Wait();
spotlightColorButton.Click(false, 500, 1500);
// Set the overlay opacity to overlayOpacity%
var overlayOpacitySlider = foundCustom.Find<Slider>("Overlay opacity (%)");
Assert.IsNotNull(overlayOpacitySlider);
Assert.IsNotNull(settings.OverlayOpacity);
int overlayOpacityValue = int.Parse(settings.OverlayOpacity, CultureInfo.InvariantCulture);
overlayOpacitySlider.QuickSetValue(overlayOpacityValue);
Assert.AreEqual(settings.OverlayOpacity, overlayOpacitySlider.Text);
Task.Delay(1000).Wait();
// Set the Fade Initial zoom to 0
var spotlightInitialZoomSlider = foundCustom.Find<Slider>("Spotlight initial zoom");
Assert.IsNotNull(spotlightInitialZoomSlider);
Task.Delay(1000).Wait();
spotlightInitialZoomSlider.QuickSetValue(int.Parse(settings.InitialZoom, CultureInfo.InvariantCulture));
Assert.AreEqual(settings.InitialZoom, spotlightInitialZoomSlider.Text);
Task.Delay(1000).Wait();
//// Change the edit value
var spotlightRadiusEdit = foundCustom.Find<TextBox>("Spotlight radius (px) Minimum5");
Assert.IsNotNull(spotlightRadiusEdit);
Task.Delay(1000).Wait();
spotlightRadiusEdit.SetText(settings.Radius);
Assert.AreEqual(settings.Radius, spotlightRadiusEdit.Text);
Task.Delay(1000).Wait();
// Set the duration to 0 ms
var spotlightAnimationDuration = foundCustom.Find<TextBox>("Animation duration (ms) Minimum0");
Assert.IsNotNull(spotlightAnimationDuration);
Task.Delay(1000).Wait();
spotlightAnimationDuration.SetText(settings.AnimationDuration);
Assert.AreEqual(settings.AnimationDuration, spotlightAnimationDuration.Text);
Task.Delay(1000).Wait();
// groupAppearanceBehavior.Click();
}
else
{
Assert.Fail("Appearance & behavior group not found.");
}
}
private bool CheckAnimationEnable(ref Custom? foundCustom)
{
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
var foundElements = foundCustom.FindAll<TextBlock>("Animations are disabled in your system settings.");
// Assert.IsNull(animationDisabledWarning);
if (foundElements.Count != 0)
{
var openSettingsLink = foundCustom.Find<Element>("Open settings");
Assert.IsNotNull(openSettingsLink);
openSettingsLink.Click(false, 500, 3000);
string settingsWindow = "Settings";
this.Session.Attach(settingsWindow);
var animationEffects = this.Find<ToggleSwitch>("Animation effects");
Assert.IsNotNull(animationEffects);
animationEffects.Toggle(true);
Task.Delay(2000).Wait();
Session.SendKeys(Key.Alt, Key.F4);
this.Session.Attach(PowerToysModule.PowerToysSettings);
this.LaunchFromSetting(reload: true);
}
else
{
return false;
}
return true;
}
private void LaunchFromSetting(bool reload = false, bool launchAsAdmin = false)
{
// this.Session.Attach(PowerToysModule.PowerToysSettings);
this.Session.SetMainWindowSize(WindowSize.Large);
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities", 10000).Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").Click();
}
if (reload)
{
this.Find<NavigationViewItem>("Keyboard Manager").Click();
}
Task.Delay(1000).Wait();
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -1,498 +0,0 @@
// 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;
using System.Globalization;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.Devices.Printers;
namespace MouseUtils.UITests
{
[TestClass]
public class MouseHighlighterTests : UITestBase
{
[TestMethod("MouseUtils.MouseHighlighter.EnableMouseHighlighter")]
[TestCategory("Mouse Utils #17")]
[TestCategory("Mouse Utils #18")]
[TestCategory("Mouse Utils #19")]
[TestCategory("Mouse Utils #20")]
[TestCategory("Mouse Utils #21")]
public void TestEnableMouseHighlighter()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
Assert.Fail("Find My Mouse custom not found.");
}
var settings = new MouseHighlighterSettings();
settings.PrimaryButtonHighlightColor = "FFFF0000";
settings.SecondaryButtonHighlightColor = "FF00FF00";
settings.AlwaysHighlightColor = "004cFF71";
settings.Radius = "50";
settings.FadeDelay = "0";
settings.FadeDuration = "90";
var foundCustom = this.Find<Custom>("Mouse Highlighter");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(false);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
// Change the shortcut key for MouseHighlighter
// [TestCase]Change activation shortcut and test it
var activationShortcutButton = foundCustom.Find<Button>("Activation shortcut");
Assert.IsNotNull(activationShortcutButton);
activationShortcutButton.Click();
Task.Delay(500).Wait();
var activationShortcutWindow = Session.Find<Window>("Activation shortcut");
Assert.IsNotNull(activationShortcutWindow);
// Invalid shortcut key
Session.SendKeySequence(Key.H);
// IOUtil.SimulateKeyPress(0x41);
var invalidShortcutText = activationShortcutWindow.Find<TextBlock>("Invalid shortcut");
Assert.IsNotNull(invalidShortcutText);
// IOUtil.SimulateShortcut(0x5B, 0x10, 0x45);
Session.SendKeys(Key.Win, Key.Shift, Key.H);
// Assert.IsNull(activationShortcutWindow.Find<TextBlock>("Invalid shortcut"));
var saveButton = activationShortcutWindow.Find<Button>("Save");
Assert.IsNotNull(saveButton);
saveButton.Click(false, 500, 1000);
SetMouseHighlighterAppearanceBehavior(ref foundCustom, ref settings);
var xy0 = Session.GetMousePosition();
Session.MoveMouseTo(xy0.Item1 - 100, xy0.Item2);
Session.PerformMouseAction(MouseActionType.LeftClick);
// Check the mouse highlighter is enabled
Session.SendKeys(Key.Win, Key.Shift, Key.H);
// IOUtil.SimulateShortcut(0x5B, 0x10, 0x45);
Task.Delay(1000).Wait();
// MouseSimulator.LeftClick();
// [Test Case] Press the activation shortcut and press left and right click somewhere, verifying the highlights are applied.
// [Test Case] Press the activation shortcut again and verify no highlights appear when the mouse buttons are clicked.
VerifyMouseHighlighterAppears(ref settings, "leftClick");
VerifyMouseHighlighterAppears(ref settings, "rightClick");
// Disable mouse highlighter
Session.SendKeys(Key.Win, Key.Shift, Key.H);
Task.Delay(1000).Wait();
VerifyMouseHighlighterNotAppears(ref settings, "leftClick");
VerifyMouseHighlighterNotAppears(ref settings, "rightClick");
// [Test Case] Disable Mouse Highlighter and verify that the module is not activated when you press the activation shortcut.
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(false);
xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1 - 100, xy.Item2);
Session.SendKeys(Key.Win, Key.Shift, Key.H);
Task.Delay(1000).Wait();
VerifyMouseHighlighterNotAppears(ref settings, "leftClick");
VerifyMouseHighlighterNotAppears(ref settings, "rightClick");
// [Test Case] With left mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
// [Test Case] With right mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1 - 100, xy.Item2);
Session.SendKeys(Key.Win, Key.Shift, Key.H);
Task.Delay(1000).Wait();
VerifyMouseHighlighterDrag(ref settings, "leftClick");
VerifyMouseHighlighterDrag(ref settings, "rightClick");
}
else
{
Assert.Fail("Mouse Highlighter Custom not found.");
}
Task.Delay(500).Wait();
}
[TestMethod("MouseUtils.MouseHighlighter.MouseHighlighterDifferentSettings")]
[TestCategory("Mouse Utils #22")]
[TestCategory("Mouse Utils #23")]
[TestCategory("Mouse Utils #24")]
public void TestMouseHighlighterDifferentSettings()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
Assert.Fail("Find My Mouse custom not found.");
}
var settings = new MouseHighlighterSettings();
settings.PrimaryButtonHighlightColor = "FF000000";
settings.SecondaryButtonHighlightColor = "FFFFFFFF";
settings.AlwaysHighlightColor = "004cFF71";
settings.Radius = "70";
settings.FadeDelay = "0";
settings.FadeDuration = "90";
var foundCustom = this.Find<Custom>("Mouse Highlighter");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(false);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
// Change the shortcut key for MouseHighlighter
// [TestCase] Test the different settings and verify they apply - Change activation shortcut and test it
// [Test Case] Test the different settings and verify they apply - Left button highlight color
// [Test Case] Test the different settings and verify they apply - Right button highlight color
// [Test Case] Test the different settings and verify they apply - Radius
var activationShortcutButton = foundCustom.Find<Button>("Activation shortcut");
Assert.IsNotNull(activationShortcutButton);
activationShortcutButton.Click();
Task.Delay(500).Wait();
var activationShortcutWindow = Session.Find<Window>("Activation shortcut");
Assert.IsNotNull(activationShortcutWindow);
// Invalid shortcut key
Session.SendKeySequence(Key.H);
// IOUtil.SimulateKeyPress(0x41);
var invalidShortcutText = activationShortcutWindow.Find<TextBlock>("Invalid shortcut");
Assert.IsNotNull(invalidShortcutText);
// IOUtil.SimulateShortcut(0x5B, 0x10, 0x45);
Session.SendKeys(Key.Win, Key.Shift, Key.O);
// Assert.IsNull(activationShortcutWindow.Find<TextBlock>("Invalid shortcut"));
var saveButton = activationShortcutWindow.Find<Button>("Save");
Assert.IsNotNull(saveButton);
saveButton.Click(false, 500, 1000);
SetMouseHighlighterAppearanceBehavior(ref foundCustom, ref settings);
var xy0 = Session.GetMousePosition();
Session.MoveMouseTo(xy0.Item1 - 100, xy0.Item2);
Session.PerformMouseAction(MouseActionType.LeftClick);
// Check the mouse highlighter is enabled
Session.SendKeys(Key.Win, Key.Shift, Key.O);
Task.Delay(1000).Wait();
VerifyMouseHighlighterAppears(ref settings, "leftClick");
VerifyMouseHighlighterAppears(ref settings, "rightClick");
}
else
{
Assert.Fail("Mouse Highlighter Custom not found.");
}
Task.Delay(500).Wait();
}
private void VerifyMouseHighlighterDrag(ref MouseHighlighterSettings settings, string action = "leftClick")
{
Task.Delay(500).Wait();
string expectedColor = string.Empty;
if (action == "leftClick")
{
IOUtil.SimulateMouseDown(true);
expectedColor = settings.PrimaryButtonHighlightColor.Substring(2);
}
else if (action == "rightClick")
{
IOUtil.SimulateMouseDown(false);
expectedColor = settings.SecondaryButtonHighlightColor.Substring(2);
}
else
{
Assert.Fail("Invalid action specified.");
}
expectedColor = "#" + expectedColor;
Task.Delay(100).Wait();
var location = Session.GetMousePosition();
int radius = int.Parse(settings.Radius, CultureInfo.InvariantCulture);
var colorLeftClick = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreEqual(expectedColor, colorLeftClick);
var colorLeftClick2 = this.GetPixelColorString(location.Item1 + radius - 1, location.Item2);
Assert.AreEqual(expectedColor, colorLeftClick2);
var colorBackground = this.GetPixelColorString(location.Item1 + radius + 50, location.Item2 + radius + 50);
Assert.AreNotEqual(expectedColor, colorBackground);
// Drag the mouse
// Session.MoveMouseTo(location.Item1 - 400, location.Item2);
for (int i = 0; i < 500; i++)
{
IOUtil.MoveMouseBy(-1, 0);
Task.Delay(10).Wait();
}
Task.Delay(2000).Wait();
location = Session.GetMousePosition();
colorLeftClick = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreEqual(expectedColor, colorLeftClick);
colorLeftClick2 = this.GetPixelColorString(location.Item1 + radius - 1, location.Item2);
Assert.AreEqual(expectedColor, colorLeftClick2);
colorBackground = this.GetPixelColorString(location.Item1 + radius + 50, location.Item2 + radius + 50);
Assert.AreNotEqual(expectedColor, colorBackground);
if (action == "leftClick")
{
IOUtil.SimulateMouseUp(true);
}
else if (action == "rightClick")
{
IOUtil.SimulateMouseUp(false);
}
int duration = int.Parse(settings.FadeDuration, CultureInfo.InvariantCulture);
Task.Delay(duration + 100).Wait();
colorLeftClick = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreNotEqual("#" + settings.PrimaryButtonHighlightColor, colorLeftClick);
}
private void VerifyMouseHighlighterNotAppears(ref MouseHighlighterSettings settings, string action = "leftClick")
{
Task.Delay(500).Wait();
string expectedColor = string.Empty;
if (action == "leftClick")
{
// MouseSimulator.LeftDown();
Session.PerformMouseAction(MouseActionType.LeftDown);
expectedColor = settings.PrimaryButtonHighlightColor.Substring(2);
}
else if (action == "rightClick")
{
// MouseSimulator.RightDown();
Session.PerformMouseAction(MouseActionType.RightDown);
expectedColor = settings.SecondaryButtonHighlightColor.Substring(2);
}
else
{
Assert.Fail("Invalid action specified.");
}
expectedColor = "#" + expectedColor;
var location = Session.GetMousePosition();
int radius = int.Parse(settings.Radius, CultureInfo.InvariantCulture);
var colorLeftClick = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreNotEqual(expectedColor, colorLeftClick);
var colorLeftClick2 = this.GetPixelColorString(location.Item1 + radius - 1, location.Item2);
Assert.AreNotEqual(expectedColor, colorLeftClick2);
if (action == "leftClick")
{
Session.PerformMouseAction(MouseActionType.LeftUp);
}
else if (action == "rightClick")
{
Session.PerformMouseAction(MouseActionType.RightUp);
}
}
private void VerifyMouseHighlighterAppears(ref MouseHighlighterSettings settings, string action = "leftClick")
{
Task.Delay(500).Wait();
string expectedColor = string.Empty;
if (action == "leftClick")
{
// MouseSimulator.LeftDown();
Session.PerformMouseAction(MouseActionType.LeftDown);
expectedColor = settings.PrimaryButtonHighlightColor.Substring(2);
}
else if (action == "rightClick")
{
// MouseSimulator.RightDown();
Session.PerformMouseAction(MouseActionType.RightDown);
expectedColor = settings.SecondaryButtonHighlightColor.Substring(2);
}
else
{
Assert.Fail("Invalid action specified.");
}
expectedColor = "#" + expectedColor;
var location = Session.GetMousePosition();
int radius = int.Parse(settings.Radius, CultureInfo.InvariantCulture);
var colorLeftClick = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreEqual(expectedColor, colorLeftClick);
var colorLeftClick2 = this.GetPixelColorString(location.Item1 + radius - 1, location.Item2);
Assert.AreEqual(expectedColor, colorLeftClick2);
Task.Delay(500).Wait();
var colorBackground = this.GetPixelColorString(location.Item1 + radius + 50, location.Item2 + radius + 50);
Assert.AreNotEqual(expectedColor, colorBackground);
if (action == "leftClick")
{
// MouseSimulator.LeftUp();
Session.PerformMouseAction(MouseActionType.LeftUp);
}
else if (action == "rightClick")
{
// MouseSimulator.RightUp();
Session.PerformMouseAction(MouseActionType.RightUp);
}
int duration = int.Parse(settings.FadeDuration, CultureInfo.InvariantCulture);
Task.Delay(duration + 100).Wait();
colorLeftClick = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreNotEqual("#" + settings.PrimaryButtonHighlightColor, colorLeftClick);
}
private void SetColor(ref Custom foundCustom, string colorName = "Primary button highlight color", string colorValue = "000000", string opacity = "0")
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
if (foundCustom.FindAll<TextBox>("Fade duration (ms) Minimum0").Count == 0)
{
groupAppearanceBehavior.Click();
}
// Set primary button highlight color
var primaryButtonHighlightColor = foundCustom.Find<Group>(colorName);
Assert.IsNotNull(primaryButtonHighlightColor);
var button = primaryButtonHighlightColor.Find<Button>(By.XPath(".//Button"));
Assert.IsNotNull(button);
button.Click(false, 500, 700);
var popupWindow = Session.Find<Window>("Popup");
Assert.IsNotNull(popupWindow);
var colorModelComboBox = this.Find<ComboBox>("Color model");
Assert.IsNotNull(colorModelComboBox);
colorModelComboBox.Click(false, 500, 700);
var selectedItem = colorModelComboBox.Find<NavigationViewItem>("RGB");
selectedItem.Click();
var rgbHexEdit = this.Find<TextBox>("RGB hex");
Assert.IsNotNull(rgbHexEdit);
Task.Delay(500).Wait();
rgbHexEdit.SetText(colorValue);
int retry = 5;
while (retry > 0)
{
Task.Delay(500).Wait();
rgbHexEdit.SetText(colorValue);
Task.Delay(500).Wait();
string rgbHex = rgbHexEdit.Text;
bool isValid = rgbHex.StartsWith('#') && rgbHex.Length == 9 && rgbHex.Substring(1) == colorValue;
Task.Delay(500).Wait();
if (isValid)
{
break;
}
retry--;
}
Task.Delay(500).Wait();
button.Click();
}
}
private void SetMouseHighlighterAppearanceBehavior(ref Custom foundCustom, ref MouseHighlighterSettings settings)
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// groupAppearanceBehavior.Click();
if (foundCustom.FindAll<TextBox>(settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.FadeDurationEdit)).Count == 0)
{
groupAppearanceBehavior.Click();
}
// Set primary button highlight color
SetColor(ref foundCustom, settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.PrimaryButtonHighlightColorGroup), settings.PrimaryButtonHighlightColor);
// Set secondary button highlight color
SetColor(ref foundCustom, settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.SecondaryButtonHighlightColorGroup), settings.SecondaryButtonHighlightColor);
// Set the duration to duration ms
var fadeDurationEdit = foundCustom.Find<TextBox>(settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.FadeDurationEdit));
Assert.IsNotNull(fadeDurationEdit);
fadeDurationEdit.SetText(settings.FadeDuration);
Assert.AreEqual(settings.FadeDuration, fadeDurationEdit.Text);
// Set Fade delay(ms)
var fadeDelayEdit = foundCustom.Find<TextBox>(settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.FadeDelayEdit));
Assert.IsNotNull(fadeDelayEdit);
fadeDelayEdit.SetText(settings.FadeDelay);
Assert.AreEqual(settings.FadeDelay, fadeDelayEdit.Text);
// Set the fade radius (px)
var fadeRadiusEdit = foundCustom.Find<TextBox>(settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.RadiusEdit));
Assert.IsNotNull(fadeRadiusEdit);
fadeRadiusEdit.SetText(settings.Radius);
Assert.AreEqual(settings.Radius, fadeRadiusEdit.Text);
// Set always highlight color
SetColor(ref foundCustom, settings.GetElementName(MouseHighlighterSettings.SettingsUIElements.AlwaysHighlightColorGroup), settings.AlwaysHighlightColor);
}
else
{
Assert.Fail("Appearance & behavior group not found.");
}
}
private void LaunchFromSetting(bool showWarning = false, bool launchAsAdmin = false)
{
this.Session.SetMainWindowSize(WindowSize.Large);
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").Click();
}
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -1,250 +0,0 @@
// 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;
using System.Threading.Tasks;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MouseUtils.UITests
{
[TestClass]
public class MouseJumpTests : UITestBase
{
[TestMethod("MouseUtils.MouseJump.EnableMouseJump")]
[TestCategory("Mouse Utils #39")]
[TestCategory("Mouse Utils #40")]
[TestCategory("Mouse Utils #41")]
[TestCategory("Mouse Utils #45")]
public void TestEnableMouseJump()
{
LaunchFromSetting(true);
}
[TestMethod("MouseUtils.MouseJump.EnableMouseJump2")]
[TestCategory("Mouse Utils #39")]
[TestCategory("Mouse Utils #41")]
[TestCategory("Mouse Utils #45")]
public void TestEnableMouseJump2()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
Assert.Fail("Find My Mouse custom not found.");
}
for (int i = 0; i < 10; i++)
{
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
var foundCustom = this.Find<Custom>("Mouse Jump");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Mouse Jump").Toggle(true);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
// Change the shortcut key for MouseHighlighter
// [TestCase]Change activation shortcut and test it
var activationShortcutButton = foundCustom.Find<Button>("Activation shortcut");
Assert.IsNotNull(activationShortcutButton);
activationShortcutButton.Click(false, 500, 1000);
var activationShortcutWindow = Session.Find<Window>("Activation shortcut");
Assert.IsNotNull(activationShortcutWindow);
// Invalid shortcut key
Session.SendKeySequence(Key.H);
// IOUtil.SimulateKeyPress(0x41);
var invalidShortcutText = activationShortcutWindow.Find<TextBlock>("Invalid shortcut");
Assert.IsNotNull(invalidShortcutText);
// IOUtil.SimulateShortcut(0x5B, 0x10, 0x45);
Session.SendKeys(Key.Win, Key.Shift, Key.Z);
// Assert.IsNull(activationShortcutWindow.Find<TextBlock>("Invalid shortcut"));
var saveButton = activationShortcutWindow.Find<Button>("Save");
Assert.IsNotNull(saveButton);
saveButton.Click(false, 500, 1500);
var screenCenter = this.GetScreenCenter();
Session.MoveMouseTo(screenCenter.CenterX, screenCenter.CenterY, 500, 1000);
Session.MoveMouseTo(screenCenter.CenterX, screenCenter.CenterY - 300, 500, 1000);
// [TestCase] Enable Mouse Jump. Then - Press the activation shortcut and verify the screens preview appears.
// [TestCase] Enable Mouse Jump. Then - Click around the screen preview and ensure that mouse cursor jumped to clicked location.
Session.SendKeys(Key.Win, Key.Shift, Key.Z);
VerifyWindowAppears();
Task.Delay(1000).Wait();
// [TestCase] Enable Mouse Jump. Then - Disable Mouse Jump and verify that the module is not activated when you press the activation shortcut.
foundCustom.Find<ToggleSwitch>("Enable Mouse Jump").Toggle(false);
Session.MoveMouseTo(screenCenter.CenterX, screenCenter.CenterY - 300, 500, 1000);
Session.SendKeys(Key.Win, Key.Shift, Key.Z);
Task.Delay(500).Wait();
VerifyWindowNotAppears();
}
else
{
Assert.Fail("Mouse Highlighter Custom not found.");
}
Task.Delay(500).Wait();
}
[TestMethod("MouseUtils.MouseJump.EnableMouseJump3")]
[TestCategory("Mouse Utils #40")]
public void TestEnableMouseJump3()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
Assert.Fail("Find My Mouse custom not found.");
}
for (int i = 0; i < 10; i++)
{
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
var foundCustom = this.Find<Custom>("Mouse Jump");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>("Enable Mouse Jump").Toggle(true);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
// Change the shortcut key for MouseHighlighter
// [TestCase]Change activation shortcut and test it
var activationShortcutButton = foundCustom.Find<Button>("Activation shortcut");
Assert.IsNotNull(activationShortcutButton);
activationShortcutButton.Click(false, 500, 1000);
var activationShortcutWindow = Session.Find<Window>("Activation shortcut");
Assert.IsNotNull(activationShortcutWindow);
// Invalid shortcut key
Session.SendKeySequence(Key.H);
// IOUtil.SimulateKeyPress(0x41);
var invalidShortcutText = activationShortcutWindow.Find<TextBlock>("Invalid shortcut");
Assert.IsNotNull(invalidShortcutText);
// IOUtil.SimulateShortcut(0x5B, 0x10, 0x45);
Session.SendKeys(Key.Win, Key.Shift, Key.J);
// Assert.IsNull(activationShortcutWindow.Find<TextBlock>("Invalid shortcut"));
var saveButton = activationShortcutWindow.Find<Button>("Save");
Assert.IsNotNull(saveButton);
saveButton.Click(false, 500, 1500);
var screenCenter = this.GetScreenCenter();
Session.MoveMouseTo(screenCenter.CenterX, screenCenter.CenterY, 500, 1000);
Session.MoveMouseTo(screenCenter.CenterX, screenCenter.CenterY - 300, 500, 1000);
// [TestCase] Enable Mouse Jump. Then - Change activation shortcut and verify that new shortcut triggers Mouse Jump.
Session.SendKeys(Key.Win, Key.Shift, Key.J);
VerifyWindowAppears();
}
else
{
Assert.Fail("Mouse Highlighter Custom not found.");
}
Task.Delay(500).Wait();
}
private void VerifyWindowAppears()
{
string windowName = "MouseJump";
Session.Attach(windowName);
var center = this.Session.GetMainWindowCenter();
Session.MoveMouseTo(center.CenterX, center.CenterY);
Session.PerformMouseAction(MouseActionType.LeftClick, 1000, 1000);
var screenCenter = this.GetScreenCenter();
// Get Mouse position
var xy = Session.GetMousePosition();
double distance = CalculateDistance(xy.Item1, xy.Item2, screenCenter.CenterX, screenCenter.CenterY);
Assert.IsTrue(distance <= 10, "Mouse Jump window should be opened and mouse should be moved to the center of the screen.");
}
private void VerifyWindowNotAppears()
{
string windowName = "MouseJump";
bool open = this.IsWindowOpen(windowName);
Assert.IsFalse(open, "Mouse Jump window should not be opened.");
}
/// <summary>
/// Calculate the Euclidean distance between two 2D points
/// </summary>
/// <param name="x1">X coordinate of first point</param>
/// <param name="y1">Y coordinate of first point</param>
/// <param name="x2">X coordinate of second point</param>
/// <param name="y2">Y coordinate of second point</param>
/// <returns>Distance (double)</returns>
public double CalculateDistance(int x1, int y1, int x2, int y2)
{
int dx = x2 - x1;
int dy = y2 - y1;
return Math.Sqrt((dx * dx) + (dy * dy));
}
private void LaunchFromSetting(bool firstTime = false, bool launchAsAdmin = false)
{
Session.SetMainWindowSize(WindowSize.Large);
Task.Delay(1000).Wait();
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").ClickCenter();
Task.Delay(2000).Wait();
}
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
RestartScopeExe();
Session.SetMainWindowSize(WindowSize.Large);
Task.Delay(1000).Wait();
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").ClickCenter();
Task.Delay(2000).Wait();
}
// Click on the Mouse utilities
// Task.Delay(2000).Wait();
if (firstTime)
{
return;
}
else
{
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}
}

View File

@@ -1,404 +0,0 @@
// 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;
using System.Globalization;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.Devices.Printers;
namespace MouseUtils.UITests
{
[TestClass]
public class MousePointerCrosshairsTests : UITestBase
{
[TestMethod("MouseUtils.MousePointerCrosshairs.EnableMousePointerCrosshairs")]
[TestCategory("Mouse Utils #29")]
[TestCategory("Mouse Utils #30")]
[TestCategory("Mouse Utils #31")]
public void TestEnableMousePointerCrosshairs()
{
LaunchFromSetting();
var settings = new MousePointerCrosshairsSettings();
settings.CrosshairsColor = "FF0000";
settings.CrosshairsBorderColor = "FF0000";
settings.Opacity = "100";
settings.CenterRadius = "0";
settings.Thickness = "20";
settings.BorderSize = "0";
settings.IsFixLength = false;
settings.FixedLength = "1";
var foundCustom = FindMouseUtilElement(MouseUtilsSettings.MouseUtils.FindMyMouse);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.FindMyMouse, false);
foundCustom = FindMouseUtilElement(MouseUtilsSettings.MouseUtils.MouseHighlighter);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MouseHighlighter, true);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MouseHighlighter, false);
for (int i = 0; i < 10; i++)
{
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
foundCustom = FindMouseUtilElement(MouseUtilsSettings.MouseUtils.MousePointerCrosshairs);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MousePointerCrosshairs, false);
Task.Delay(500).Wait();
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MousePointerCrosshairs, true);
Assert.IsNotNull(foundCustom);
// [Test Case] Change activation shortcut and test it.
var activationShortcutButton = foundCustom.Find<Button>("Activation shortcut");
Assert.IsNotNull(activationShortcutButton);
activationShortcutButton.Click(false, 500, 1000);
var activationShortcutWindow = Session.Find<Window>("Activation shortcut");
Assert.IsNotNull(activationShortcutWindow);
// Invalid shortcut key
Session.SendKeySequence(Key.H);
var invalidShortcutText = activationShortcutWindow.Find<TextBlock>("Invalid shortcut");
Assert.IsNotNull(invalidShortcutText);
Session.SendKeys(Key.Win, Key.Alt, Key.A);
var saveButton = activationShortcutWindow.Find<Button>("Save");
Assert.IsNotNull(saveButton);
saveButton.Click(false, 500, 1000);
SetMousePointerCrosshairsAppearanceBehavior(ref foundCustom, ref settings);
Task.Delay(500).Wait();
// [Test Case] Press the activation shortcut and verify the crosshairs appear, and that they follow the mouse around.
var xy0 = Session.GetMousePosition();
Session.MoveMouseTo(xy0.Item1 - 100, xy0.Item2);
IOUtil.MouseClick();
Task.Delay(500).Wait();
Session.SendKeys(Key.Win, Key.Alt, Key.A);
Task.Delay(1000).Wait();
xy0 = Session.GetMousePosition();
VerifyMousePointerCrosshairsAppears(ref settings);
Task.Delay(500).Wait();
for (int i = 0; i < 100; i++)
{
IOUtil.MoveMouseBy(-1, 0);
Task.Delay(10).Wait();
}
VerifyMousePointerCrosshairsAppears(ref settings);
// [Test Case] Press the activation shortcut again and verify the crosshairs disappear.
Session.SendKeys(Key.Win, Key.Alt, Key.A);
Task.Delay(1000).Wait();
VerifyMousePointerCrosshairsNotAppears(ref settings);
Task.Delay(500).Wait();
// [Test Case] Disable Mouse Pointer Crosshairs and verify that the module is not activated when you press the activation shortcut.
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MousePointerCrosshairs, false);
xy0 = Session.GetMousePosition();
Session.MoveMouseTo(xy0.Item1 - 100, xy0.Item2);
Session.PerformMouseAction(MouseActionType.LeftClick);
Session.SendKeys(Key.Win, Key.Alt, Key.A);
Task.Delay(1000).Wait();
VerifyMousePointerCrosshairsNotAppears(ref settings);
}
[TestMethod("MouseUtils.MousePointerCrosshairs.MousePointerCrosshairsDifferentSettings")]
[TestCategory("Mouse Utils #32")]
[TestCategory("Mouse Utils #33")]
public void TestMousePointerCrosshairsDifferentSettings()
{
LaunchFromSetting();
var settings = new MousePointerCrosshairsSettings();
settings.CrosshairsColor = "00FF00";
settings.CrosshairsBorderColor = "00FF00";
settings.Opacity = "100";
settings.CenterRadius = "0";
settings.Thickness = "20";
settings.BorderSize = "0";
settings.IsFixLength = false;
settings.FixedLength = "1";
var foundCustom = FindMouseUtilElement(MouseUtilsSettings.MouseUtils.FindMyMouse);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.FindMyMouse, false);
foundCustom = FindMouseUtilElement(MouseUtilsSettings.MouseUtils.MouseHighlighter);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MouseHighlighter, true);
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MouseHighlighter, false);
for (int i = 0; i < 10; i++)
{
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
foundCustom = FindMouseUtilElement(MouseUtilsSettings.MouseUtils.MousePointerCrosshairs);
// this.FindGroup("Enable Mouse Pointer Crosshairs");
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MousePointerCrosshairs, false);
Task.Delay(500).Wait();
MouseUtilsSettings.SetMouseUtilEnabled(foundCustom, MouseUtilsSettings.MouseUtils.MousePointerCrosshairs, true);
Assert.IsNotNull(foundCustom);
// [Test Case] Change activation shortcut and test it.
var activationShortcutButton = foundCustom.Find<Button>("Activation shortcut");
Assert.IsNotNull(activationShortcutButton);
activationShortcutButton.Click(false, 500, 1000);
var activationShortcutWindow = Session.Find<Window>("Activation shortcut");
Assert.IsNotNull(activationShortcutWindow);
// Invalid shortcut key
Session.SendKeySequence(Key.H);
var invalidShortcutText = activationShortcutWindow.Find<TextBlock>("Invalid shortcut");
Assert.IsNotNull(invalidShortcutText);
Session.SendKeys(Key.Win, Key.Alt, Key.P);
var saveButton = activationShortcutWindow.Find<Button>("Save");
Assert.IsNotNull(saveButton);
saveButton.Click(false, 500, 1000);
SetMousePointerCrosshairsAppearanceBehavior(ref foundCustom, ref settings);
Task.Delay(500).Wait();
// [Test Case] Test the different settings and verify they apply - Change activation shortcut and test it.
// [Test Case] Test the different settings and verify they apply - Crosshairs color.
var xy0 = Session.GetMousePosition();
Session.MoveMouseTo(xy0.Item1 - 100, xy0.Item2);
IOUtil.MouseClick();
Task.Delay(500).Wait();
Session.SendKeys(Key.Win, Key.Alt, Key.P);
Task.Delay(1000).Wait();
xy0 = Session.GetMousePosition();
VerifyMousePointerCrosshairsAppears(ref settings);
Task.Delay(500).Wait();
for (int i = 0; i < 100; i++)
{
IOUtil.MoveMouseBy(-1, 0);
Task.Delay(10).Wait();
}
VerifyMousePointerCrosshairsAppears(ref settings);
// Press the activation shortcut again and verify the crosshairs disappear.
Session.SendKeys(Key.Win, Key.Alt, Key.P);
Task.Delay(1000).Wait();
VerifyMousePointerCrosshairsNotAppears(ref settings);
}
private void VerifyMousePointerCrosshairsNotAppears(ref MousePointerCrosshairsSettings settings)
{
Task.Delay(500).Wait();
string expectedColor = string.Empty;
expectedColor = "#" + settings.CrosshairsColor;
var location = Session.GetMousePosition();
int radius = int.Parse(settings.CenterRadius, CultureInfo.InvariantCulture);
var color = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreNotEqual(expectedColor, color);
}
private void VerifyMousePointerCrosshairsAppears(ref MousePointerCrosshairsSettings settings)
{
Task.Delay(1000).Wait();
string expectedColor = string.Empty;
expectedColor = "#" + settings.CrosshairsColor;
var location = Session.GetMousePosition();
int radius = int.Parse(settings.CenterRadius, CultureInfo.InvariantCulture);
var color = this.GetPixelColorString(location.Item1, location.Item2);
Assert.AreEqual(expectedColor, color, "Center color check failed");
var colorX = this.GetPixelColorString(location.Item1 + 50, location.Item2);
Assert.AreEqual(expectedColor, colorX, "Center x + 50 color check failed");
colorX = this.GetPixelColorString(location.Item1 - 50, location.Item2);
Assert.AreEqual(expectedColor, colorX, "Center x - 50 color check failed");
var colorY = this.GetPixelColorString(location.Item1, location.Item2 + 50);
Assert.AreEqual(expectedColor, colorY, "Center y + 50 color check failed");
colorY = this.GetPixelColorString(location.Item1, location.Item2 - 50);
Assert.AreEqual(expectedColor, colorY, "Center y + 50 color check failed");
}
private void SetColor(ref Custom foundCustom, string colorName, string colorValue = "000000")
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// Set primary button highlight color
var primaryButtonHighlightColor = foundCustom.Find<Group>(colorName);
Assert.IsNotNull(primaryButtonHighlightColor);
var button = primaryButtonHighlightColor.Find<Button>(By.XPath(".//Button"));
Assert.IsNotNull(button);
button.Click(false);
var popupWindow = Session.Find<Window>("Popup");
Assert.IsNotNull(popupWindow);
var colorModelComboBox = this.Find<ComboBox>("Color model");
Assert.IsNotNull(colorModelComboBox);
colorModelComboBox.Click();
var selectedItem = colorModelComboBox.Find<NavigationViewItem>("RGB");
selectedItem.Click();
var rgbHexEdit = this.Find<TextBox>("RGB hex");
Assert.IsNotNull(rgbHexEdit);
rgbHexEdit.SetText(colorValue);
button.Click();
}
}
private void SetMousePointerCrosshairsAppearanceBehavior(ref Custom foundCustom, ref MousePointerCrosshairsSettings settings)
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// groupAppearanceBehavior.Click();
if (foundCustom.FindAll<TextBox>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.ThicknessEdit)).Count == 0)
{
groupAppearanceBehavior.Click();
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
// Set the crosshairs color
SetColor(ref foundCustom, settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.CrosshairsColorGroup), settings.CrosshairsColor);
// Set the crosshairs border color
SetColor(ref foundCustom, settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.CrosshairsBorderColorGroup), settings.CrosshairsBorderColor);
// Set the duration to duration ms
var opacitySlider = foundCustom.Find<Slider>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.OpacitySlider));
Assert.IsNotNull(opacitySlider);
Assert.IsNotNull(settings.Opacity);
int opacityValue = int.Parse(settings.Opacity, CultureInfo.InvariantCulture);
opacitySlider.QuickSetValue(opacityValue);
Assert.AreEqual(settings.Opacity, opacitySlider.Text);
Task.Delay(500).Wait();
// Set the center radius (px)
var centerRadiusEdit = foundCustom.Find<TextBox>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.CenterRadiusEdit));
Assert.IsNotNull(centerRadiusEdit);
centerRadiusEdit.SetText(settings.CenterRadius);
Assert.AreEqual(settings.CenterRadius, centerRadiusEdit.Text);
// Set the thickness (px)
var thicknessEdit = foundCustom.Find<TextBox>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.ThicknessEdit));
Assert.IsNotNull(thicknessEdit);
thicknessEdit.SetText(settings.Thickness);
Assert.AreEqual(settings.Thickness, thicknessEdit.Text);
// Set the border size (px)
var borderSizeEdit = foundCustom.Find<TextBox>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.BorderSizeEdit));
Assert.IsNotNull(borderSizeEdit);
borderSizeEdit.SetText(settings.BorderSize);
Assert.AreEqual(settings.BorderSize, borderSizeEdit.Text);
// Set the fixed length (px)
var isFixedLength = foundCustom.Find<ToggleSwitch>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.IsFixLengthToggle));
Assert.IsNotNull(isFixedLength);
isFixedLength.Toggle(settings.IsFixLength);
Assert.AreEqual(settings.IsFixLength, isFixedLength.IsOn);
if (settings.IsFixLength)
{
var fixedLengthEdit = foundCustom.Find<TextBox>(settings.GetElementName(MousePointerCrosshairsSettings.SettingsUIElements.FixedLengthEdit));
Assert.IsNotNull(fixedLengthEdit);
fixedLengthEdit.SetText(settings.FixedLength);
Assert.AreEqual(settings.FixedLength, fixedLengthEdit.Text);
}
}
else
{
Assert.Fail("Appearance & behavior group not found.");
}
}
private bool FindGroup(string groupName)
{
try
{
var foundElements = this.FindAll<Element>(groupName);
foreach (var element in foundElements)
{
string className = element.ClassName;
string name = element.Name;
string text = element.Text;
string helptext = element.HelpText;
string controlType = element.ControlType;
}
if (foundElements.Count == 0)
{
return false;
}
}
catch (Exception ex)
{
// Validate if group is not found by checking exception.Message
return ex.Message.Contains("No element found");
}
return true;
}
public Custom? FindMouseUtilElement(MouseUtilsSettings.MouseUtils element)
{
var elementName = MouseUtilsSettings.GetMouseUtilUIName(element);
var foundCustom = this.Find<Custom>(elementName);
for (int i = 0; i < 20; i++)
{
if (foundCustom != null)
{
break;
}
Session.PerformMouseAction(MouseActionType.ScrollDown);
foundCustom = this.Find<Custom>(elementName);
}
return foundCustom;
}
private void LaunchFromSetting(bool showWarning = false, bool launchAsAdmin = false)
{
Session.SetMainWindowSize(WindowSize.Large);
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").Click();
}
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -1,25 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ProjectGuid>{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}</ProjectGuid>
<RootNamespace>PowerToys.MouseUtils.UITests</RootNamespace>
<AssemblyName>PowerToys.MouseUtils.UITests</AssemblyName>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\MouseUtils.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,64 +0,0 @@
## [Mouse Utils](tests-checklist-template-mouse-utils-section.md)
Find My Mouse:
* Enable FindMyMouse. Then, without moving your mouse:
- [x] Press Left Ctrl twice and verify the overlay appears.
- [x] Press any other key and verify the overlay disappears.
- [x] Press Left Ctrl twice and verify the overlay appears.
- [x] Press a mouse button and verify the overlay disappears.
* Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice.
* Enable FindMyMouse. Then, without moving your mouse:
- [x] Press Left Ctrl twice and verify the overlay appears.
* Enable the "Do not activate on game mode" option. Start playing a game that uses CG native full screen.
- [ ] Verify the overlay no longer appears when you press Left Ctrl twice.
* Disable the "Do not activate on game mode" option. Start playing the same game.
- [ ] Verify the overlay appears when you press Left Ctrl twice. (though it'll likely minimize the game)
* Test the different settings and verify they apply:
- [ ] Overlay opacity
- [x] Background color
- [x] Spotlight color
- [x] Spotlight radius
- [ ] Spotlight initial zoom (1x vs 9x will show the difference)
- [ ] Animation duration
- [ ] Change activation method to shake and activate by shaking your mouse pointer
- [ ] Excluded apps
Mouse Highlighter:
* Enable Mouse Highlighter. Then:
- [x] Press the activation shortcut and press left and right click somewhere, verifying the hightlights are applied.
- [x] With left mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
- [x] With right mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
- [x] Press the activation shortcut again and verify no highlights appear when the mouse buttons are clicked.
- [x] Disable Mouse Highlighter and verify that the module is not activated when you press the activation shortcut.
* Test the different settings and verify they apply:
- [x] Change activation shortcut and test it
- [x] Left button highlight color
- [x] Right button highlight color
- [ ] Opacity
- [ ] Radius
- [ ] Fade delay
- [ ] Fade duration
Mouse Pointer Crosshairs:
* Enable Mouse Pointer Crosshairs. Then:
- [x] Press the activation shortcut and verify the crosshairs appear, and that they follow the mouse around.
- [x] Press the activation shortcut again and verify the crosshairs disappear.
- [x] Disable Mouse Pointer Crosshairs and verify that the module is not activated when you press the activation shortcut.
* Test the different settings and verify they apply:
- [x] Change activation shortcut and test it
- [x] Crosshairs color
- [ ] Crosshairs opacity
- [ ] Crosshairs center radius
- [ ] Crosshairs thickness
- [ ] Crosshairs border color
- [ ] Crosshairs border size
Mouse Jump:
* Enable Mouse Jump. Then:
- [x] Press the activation shortcut and verify the screens preview appears.
- [x] Change activation shortcut and verify that new shortcut triggers Mouse Jump.
- [x] Click around the screen preview and ensure that mouse cursor jumped to clicked location.
- [ ] Reorder screens in Display settings and confirm that Mouse Jump reflects the change and still works correctly.
- [ ] Change scaling of screens and confirm that Mouse Jump still works correctly.
- [ ] Unplug additional monitors and confirm that Mouse Jump still works correctly.
- [x] Disable Mouse Jump and verify that the module is not activated when you press the activation shortcut.

View File

@@ -1,58 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MouseUtils.UITests
{
public class FindMyMouseSettings
{
// Appearance settings
public string OverlayOpacity { get; set; }
public string Radius { get; set; }
public string InitialZoom { get; set; }
public string AnimationDuration { get; set; }
// Color settings
public string BackgroundColor { get; set; }
public string SpotlightColor { get; set; }
// Activation method settings
public enum ActivationMethod
{
PressLeftControlTwice,
PressRightControlTwice,
ShakeMouse,
CustomShortcut,
}
public ActivationMethod SelectedActivationMethod { get; set; }
// Optional constructor to initialize properties
public FindMyMouseSettings(
string overlayOpacity = "",
string radius = "",
string initialZoom = "",
string animationDuration = "",
string backgroundColor = "",
string spotlightColor = "")
{
OverlayOpacity = overlayOpacity;
Radius = radius;
InitialZoom = initialZoom;
AnimationDuration = animationDuration;
BackgroundColor = backgroundColor;
SpotlightColor = spotlightColor;
SelectedActivationMethod = ActivationMethod.PressLeftControlTwice; // Default value
}
}
}

View File

@@ -1,291 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
// The MouseUtils module relies on simulating system-level input events (such as mouse movement or key presses) to test visual or behavioral responses.
// The UI Test framework provides built-in methods for simulating mouse movement and clicks, which work for MouseUtils reliably on high-performance dev boxes.
// However, on low-performance environments such as CI/CD pipelines or virtual machines, these simulated input events are not always correctly recognized by the operating system.
// IOUtils class is added specifically for MouseUtils tests.
// For any test scenario that involves simulating continuous mouse movement (e.g., detecting crosshair changes while moving the cursor),
// input simulation methods in IOUtils class should be used.
namespace MouseUtils.UITests
{
public class IOUtil
{
private readonly UIntPtr ignoreKeyEventFlag = 0x5555;
[DllImport("user32.dll")]
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct INPUT
{
internal INPUTTYPE type;
internal InputUnion data;
internal static int Size
{
get { return Marshal.SizeOf<INPUT>(); }
}
}
[StructLayout(LayoutKind.Explicit)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct InputUnion
{
[FieldOffset(0)]
internal MOUSEINPUT mi;
[FieldOffset(0)]
internal KEYBDINPUT ki;
[FieldOffset(0)]
internal HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal uint dwFlags;
internal uint time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal uint dwFlags;
internal int time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct HARDWAREINPUT
{
internal int uMsg;
internal short wParamL;
internal short wParamH;
}
internal enum INPUTTYPE : uint
{
INPUT_MOUSE = 0,
INPUT_KEYBOARD = 1,
INPUT_HARDWARE = 2,
}
[Flags]
internal enum KeyEventF
{
KeyDown = 0x0000,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Unicode = 0x0004,
Scancode = 0x0008,
}
[Flags]
internal enum MouseEventF : uint
{
MOVE = 0x0001,
LEFTDOWN = 0x0002,
LEFTUP = 0x0004,
RIGHTDOWN = 0x0008,
RIGHTUP = 0x0010,
ABSOLUTE = 0x8000,
MIDDLEDOWN = 0x0020,
MIDDLEUP = 0x0040,
}
public static void SimulateMouseDown(bool leftButton = true)
{
SendMouseInput(leftButton ? MouseEventF.LEFTDOWN : MouseEventF.RIGHTDOWN);
}
public static void SimulateMouseUp(bool leftButton = true)
{
SendMouseInput(leftButton ? MouseEventF.LEFTUP : MouseEventF.RIGHTUP);
}
public static void MouseClick(bool leftButton = true)
{
SendMouseInput(leftButton ? MouseEventF.LEFTDOWN : MouseEventF.RIGHTDOWN);
SendMouseInput(leftButton ? MouseEventF.LEFTUP : MouseEventF.RIGHTUP);
}
private static void SendMouseInput(MouseEventF mouseFlags)
{
var input = new INPUT
{
type = INPUTTYPE.INPUT_MOUSE,
data = new InputUnion
{
mi = new MOUSEINPUT
{
dx = 0,
dy = 0,
mouseData = 0,
dwFlags = (uint)mouseFlags,
time = 0,
dwExtraInfo = UIntPtr.Zero,
},
},
};
INPUT[] inputs = [input];
_ = SendInput(1, inputs, INPUT.Size);
}
[DllImport("user32.dll")]
private static extern IntPtr GetMessageExtraInfo();
public static void MoveMouseBy(int dx, int dy)
{
var input = new INPUT
{
type = INPUTTYPE.INPUT_MOUSE,
data = new InputUnion
{
mi = new MOUSEINPUT
{
dx = dx,
dy = dy,
mouseData = 0,
dwFlags = (uint)MouseEventF.MOVE,
time = 0,
dwExtraInfo = (nuint)GetMessageExtraInfo(),
},
},
};
INPUT[] inputs = [input];
_ = SendInput(1, inputs, INPUT.Size);
}
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex);
public static void MoveMouseTo(int x, int y)
{
int screenWidth = GetSystemMetrics(0);
int screenHeight = GetSystemMetrics(1);
int normalizedX = (int)(x * 65535 / screenWidth);
int normalizedY = (int)(y * 65535 / screenHeight);
var input = new INPUT
{
type = INPUTTYPE.INPUT_MOUSE,
data = new InputUnion
{
mi = new MOUSEINPUT
{
dx = normalizedX,
dy = normalizedY,
mouseData = 0,
dwFlags = (uint)(MouseEventF.MOVE | MouseEventF.ABSOLUTE),
time = 0,
dwExtraInfo = UIntPtr.Zero,
},
},
};
INPUT[] inputs = [input];
_ = SendInput(1, inputs, INPUT.Size);
}
private void SendSingleKeyboardInput(short keyCode, uint keyStatus)
{
var inputShift = new INPUT
{
type = INPUTTYPE.INPUT_KEYBOARD,
data = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = keyCode,
dwFlags = keyStatus,
// Any keyevent with the extraInfo set to this value will be ignored by the keyboard hook and sent to the system instead.
dwExtraInfo = ignoreKeyEventFlag,
},
},
};
INPUT[] inputs = [inputShift];
_ = SendInput(1, inputs, INPUT.Size);
}
public static void SimulateKeyDown(ushort keyCode)
{
SendKey(keyCode, false);
}
public static void SimulateKeyUp(ushort keyCode)
{
SendKey(keyCode, true);
}
public static void SimulateKeyPress(ushort keyCode)
{
SendKey(keyCode, false);
SendKey(keyCode, true);
}
public static void SimulateShortcut(params ushort[] keyCodes)
{
foreach (var key in keyCodes)
{
SimulateKeyDown(key);
}
for (int i = keyCodes.Length - 1; i >= 0; i--)
{
SimulateKeyUp(keyCodes[i]);
}
}
public static void SendKey(ushort keyCode, bool keyUp)
{
var inputShift = new INPUT
{
type = INPUTTYPE.INPUT_KEYBOARD,
data = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = (short)keyCode,
dwFlags = (uint)(keyUp ? KeyEventF.KeyUp : 0),
dwExtraInfo = (uint)IntPtr.Zero,
},
},
};
INPUT[] inputs = [inputShift];
_ = SendInput(1, inputs, INPUT.Size);
}
}
}

View File

@@ -1,74 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MouseUtils.UITests
{
public class MouseHighlighterSettings
{
// Appearance settings
public string Radius { get; set; }
public string FadeDelay { get; set; }
public string FadeDuration { get; set; }
// Color settings
public string PrimaryButtonHighlightColor { get; set; }
public string SecondaryButtonHighlightColor { get; set; }
public string AlwaysHighlightColor { get; set; }
// Settings UI Elements
public enum SettingsUIElements
{
PrimaryButtonHighlightColorGroup,
SecondaryButtonHighlightColorGroup,
AlwaysHighlightColorGroup,
RadiusEdit,
FadeDelayEdit,
FadeDurationEdit,
}
private Dictionary<SettingsUIElements, string> ElementNameMap { get; }
// Optional constructor to initialize properties
public MouseHighlighterSettings(
string radius = "",
string fadeDelay = "",
string fadeDuration = "",
string primaryButtonHighlightColor = "",
string secondaryButtonHighlightColor = "",
string alwaysHighlightColor = "")
{
this.Radius = radius;
this.FadeDelay = fadeDelay;
this.FadeDuration = fadeDuration;
this.PrimaryButtonHighlightColor = primaryButtonHighlightColor;
this.SecondaryButtonHighlightColor = secondaryButtonHighlightColor;
this.AlwaysHighlightColor = alwaysHighlightColor;
ElementNameMap = new Dictionary<SettingsUIElements, string>
{
[SettingsUIElements.PrimaryButtonHighlightColorGroup] = @"Primary button highlight color",
[SettingsUIElements.SecondaryButtonHighlightColorGroup] = @"Secondary button highlight color",
[SettingsUIElements.AlwaysHighlightColorGroup] = @"Always highlight color",
[SettingsUIElements.RadiusEdit] = @"Radius (px) Minimum5",
[SettingsUIElements.FadeDelayEdit] = @"Fade delay (ms) Minimum0",
[SettingsUIElements.FadeDurationEdit] = @"Fade duration (ms) Minimum0",
};
}
public string GetElementName(SettingsUIElements element)
{
return ElementNameMap[element];
}
}
}

View File

@@ -1,86 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Core.AnimationMetrics;
namespace MouseUtils.UITests
{
public class MousePointerCrosshairsSettings
{
// Appearance settings
public string Opacity { get; set; }
public string CenterRadius { get; set; }
public string Thickness { get; set; }
public string BorderSize { get; set; }
public bool IsFixLength { get; set; }
public string FixedLength { get; set; }
// Color settings
public string CrosshairsColor { get; set; }
public string CrosshairsBorderColor { get; set; }
// Settings UI Elements
public enum SettingsUIElements
{
CrosshairsColorGroup,
CrosshairsBorderColorGroup,
OpacitySlider,
CenterRadiusEdit,
ThicknessEdit,
BorderSizeEdit,
FixedLengthEdit,
IsFixLengthToggle,
}
private Dictionary<SettingsUIElements, string> ElementNameMap { get; }
// Optional constructor to initialize properties
public MousePointerCrosshairsSettings(
string opacity = "",
string centerRadius = "",
string thickness = "",
string borderSize = "",
bool isFixLength = false,
string fixedLength = "",
string crosshairsColor = "",
string crosshairsBorderColor = "")
{
this.Opacity = opacity;
this.CenterRadius = centerRadius;
this.Thickness = thickness;
this.BorderSize = borderSize;
this.IsFixLength = isFixLength;
this.FixedLength = fixedLength;
this.CrosshairsColor = crosshairsColor;
this.CrosshairsBorderColor = crosshairsBorderColor;
ElementNameMap = new Dictionary<SettingsUIElements, string>
{
[SettingsUIElements.CrosshairsColorGroup] = @"Crosshairs color",
[SettingsUIElements.CrosshairsBorderColorGroup] = @"Crosshairs border color",
[SettingsUIElements.OpacitySlider] = @"Crosshairs opacity (%)",
[SettingsUIElements.CenterRadiusEdit] = @"Crosshairs center radius (px) Minimum0 Maximum500",
[SettingsUIElements.ThicknessEdit] = @"Crosshairs thickness (px) Minimum1 Maximum50",
[SettingsUIElements.BorderSizeEdit] = @"Crosshairs border size (px) Minimum0 Maximum50",
[SettingsUIElements.FixedLengthEdit] = @"Crosshairs fixed length (px) Minimum1",
[SettingsUIElements.IsFixLengthToggle] = @"Fix crosshairs length",
};
}
public string GetElementName(SettingsUIElements element)
{
return ElementNameMap[element];
}
}
}

View File

@@ -1,59 +0,0 @@
// 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 Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
namespace MouseUtils.UITests
{
public class MouseUtilsSettings
{
// Mouse Utils Modules
public enum MouseUtils
{
MouseHighlighter,
FindMyMouse,
MousePointerCrosshairs,
MouseJump,
}
private static readonly Dictionary<MouseUtils, string> MouseUtilUINameMap = new()
{
[MouseUtils.MouseHighlighter] = @"Mouse Highlighter",
[MouseUtils.FindMyMouse] = @"Find My Mouse",
[MouseUtils.MousePointerCrosshairs] = @"Mouse Pointer Crosshairs",
[MouseUtils.MouseJump] = @"Mouse Jump",
};
private static readonly Dictionary<MouseUtils, string> MouseUtilUIToggleMap = new()
{
[MouseUtils.MouseHighlighter] = @"Enable Mouse Highlighter",
[MouseUtils.FindMyMouse] = @"Enable Find My Mouse",
[MouseUtils.MousePointerCrosshairs] = @"Enable Mouse Pointer Crosshairs",
[MouseUtils.MouseJump] = @"Enable Mouse Jump",
};
public static string GetMouseUtilUIName(MouseUtils element)
{
return MouseUtilUINameMap[element];
}
public static void SetMouseUtilEnabled(Custom? custom, MouseUtils element, bool isEnable = true)
{
if (custom != null)
{
string toggleName = MouseUtilUIToggleMap[element];
var toggle = custom.Find<ToggleSwitch>(toggleName);
toggle.Toggle(isEnable);
}
else
{
Assert.Fail(element + " custom not found.");
}
}
}
}

View File

@@ -1,24 +0,0 @@
// 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 Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesEditorTests : UITestBase
{
public WorkspacesEditorTests()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
{
}
[TestMethod("WorkspacesEditor.Items.Present")]
[TestCategory("Workspaces UI")]
public void TestItemsPresents()
{
Assert.IsTrue(this.Has<Button>("Create Workspace"), "Should have create workspace button");
}
}

View File

@@ -1,28 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<RunVSTest>false</RunVSTest>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<AssemblyName>PowerToys.Workspaces.UITests</AssemblyName>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\Workspaces.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Appium.WebDriver" />
<PackageReference Include="MSTest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>

View File

@@ -117,18 +117,16 @@ private:
for (DWORD pid : processIds)
{
HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hProcess != NULL)
{
SetEvent(m_hTerminateEvent);
// Wait for 1.5 seconds for the process to end correctly, allowing time for ETW tracer and extensions to stop
if (WaitForSingleObject(hProcess, 1500) == WAIT_TIMEOUT)
{
TerminateProcess(hProcess, 0);
}
// Wait for 1.5 seconds for the process to end correctly and stop etw tracer
WaitForSingleObject(hProcess, 1500);
TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
}
}

View File

@@ -102,8 +102,6 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
}
HandleChangeAlias();
OnPropertyChanged(nameof(AliasText));
OnPropertyChanged(nameof(IsDirectAlias));
}
}
@@ -118,7 +116,6 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
}
HandleChangeAlias();
OnPropertyChanged(nameof(IsDirectAlias));
}
}

View File

@@ -67,7 +67,6 @@ public partial class App : Application
"Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd", () =>
{
EtwTrace?.Dispose();
AppWindow?.Close();
Environment.Exit(0);
});
}

View File

@@ -105,10 +105,6 @@
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<RdXmlFile Include="rd.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj" />

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<Page
x:Class="Microsoft.CmdPal.UI.Settings.ExtensionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
@@ -92,11 +92,12 @@
<controls:SettingsCard x:Uid="Settings_ExtensionPage_Alias_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=&#xE8AC;}">
<StackPanel Orientation="Vertical">
<TextBox Text="{x:Bind AliasText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{x:Bind AliasText, Mode=TwoWay}" />
<ToggleSwitch
x:Uid="Settings_ExtensionPage_Alias_ToggleSwitch"
IsEnabled="{x:Bind AliasText, Converter={StaticResource StringEmptyToBoolConverter}, Mode=OneWay}"
IsOn="{x:Bind IsDirectAlias, Mode=TwoWay}" />
IsOn="{x:Bind IsDirectAlias, Mode=TwoWay}"
OffContent="Indirect"
OnContent="Direct" />
</StackPanel>
</controls:SettingsCard>

View File

@@ -418,10 +418,4 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="TrayMenu_Exit" xml:space="preserve">
<value>Exit</value>
</data>
<data name="Settings_ExtensionPage_Alias_ToggleSwitch.OnContent" xml:space="preserve">
<value>Direct</value>
</data>
<data name="Settings_ExtensionPage_Alias_ToggleSwitch.OffContent" xml:space="preserve">
<value>Indirect</value>
</data>
</root>

View File

@@ -1,7 +0,0 @@
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="Microsoft.WinUI">
<Type Name="Microsoft.UI.Xaml.Controls.FontIconSource" Dynamic="Required All" />
</Assembly>
</Application>
</Directives>

View File

@@ -1,49 +0,0 @@
// 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;
using System.Globalization;
using System.Text;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Apps.Commands;
internal sealed partial class CopyPathCommand : InvokableCommand
{
private static readonly IconInfo TheIcon = new("\ue8c8");
private readonly string _target;
public CopyPathCommand(string target)
{
Name = Resources.copy_path;
Icon = TheIcon;
_target = target;
}
private static readonly CompositeFormat CopyFailedFormat = CompositeFormat.Parse(Resources.copy_failed);
public override CommandResult Invoke()
{
try
{
ClipboardHelper.SetText(_target);
}
catch (Exception ex)
{
Logger.LogError("Copy failed: " + ex.Message);
return CommandResult.ShowToast(
new ToastArgs
{
Message = string.Format(CultureInfo.CurrentCulture, CopyFailedFormat, ex.Message),
Result = CommandResult.KeepOpen(),
});
}
return CommandResult.ShowToast(Resources.copied_to_clipboard);
}
}

View File

@@ -85,10 +85,6 @@ public class UWPApplication : IProgram
// We don't add context menu to 'run as different user', because UWP applications normally installed per user and not for all users.
}
commands.Add(
new CommandContextItem(
new CopyPathCommand(Location)));
commands.Add(
new CommandContextItem(
new OpenPathCommand(Location)

View File

@@ -198,9 +198,6 @@ public class Win32Program : IProgram
new RunAsUserCommand(!string.IsNullOrEmpty(LnkFilePath) ? LnkFilePath : FullPath, ParentDirectory)));
}
commands.Add(new CommandContextItem(
new CopyPathCommand(FullPath)));
commands.Add(new CommandContextItem(
new OpenPathCommand(ParentDirectory)));

View File

@@ -78,33 +78,6 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Copied to clipboard!.
/// </summary>
internal static string copied_to_clipboard {
get {
return ResourceManager.GetString("copied_to_clipboard", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Copy failed ({0}). Please try again..
/// </summary>
internal static string copy_failed {
get {
return ResourceManager.GetString("copy_failed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Copy path.
/// </summary>
internal static string copy_path {
get {
return ResourceManager.GetString("copy_path", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Include apps found on the desktop.
/// </summary>

View File

@@ -163,22 +163,12 @@
<data name="open_location" xml:space="preserve">
<value>Open location</value>
</data>
<data name="copy_path" xml:space="preserve">
<value>Copy path</value>
</data>
<data name="run_as_administrator" xml:space="preserve">
<value>Run as administrator</value>
</data>
<data name="run_as_different_user" xml:space="preserve">
<value>Run as different user</value>
</data>
<data name="copy_failed" xml:space="preserve">
<value>Copy failed ({0}). Please try again.</value>
<comment>{0} is the error message</comment>
</data>
<data name="copied_to_clipboard" xml:space="preserve">
<value>Copied to clipboard!</value>
</data>
<data name="enable_start_menu_source" xml:space="preserve">
<value>Include apps found in the Start Menu</value>
</data>

View File

@@ -18,6 +18,8 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
private IList<ListItem> _results = new List<ListItem>();
private bool initialized;
private SettingsManager _settingsManager;
public TimeDateExtensionPage(SettingsManager settingsManager)
@@ -33,9 +35,12 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
public override IListItem[] GetItems()
{
DoExecuteSearch(string.Empty);
if (!initialized)
{
DoExecuteSearch(string.Empty);
}
lock (_resultsLock)
lock (_resultsLock)
{
ListItem[] results = _results.ToArray();
return results;
@@ -44,6 +49,11 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
public override void UpdateSearchText(string oldSearch, string newSearch)
{
if (newSearch == oldSearch)
{
return;
}
DoExecuteSearch(newSearch);
}
@@ -74,6 +84,7 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
{
lock (_resultsLock)
{
initialized = true;
this._results = result;
}

View File

@@ -23,7 +23,6 @@
namespace NonLocalizable
{
const wchar_t ToolWindowClassName[] = L"FancyZones_ZonesOverlay";
const wchar_t ToolWindowName[] = L"FancyZones_ZonesOverlay";
}
using namespace FancyZonesUtils;
@@ -60,7 +59,7 @@ namespace
HWND windowFromPool = ExtractWindow();
if (windowFromPool == NULL)
{
HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, NonLocalizable::ToolWindowName, WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner);
HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner);
Logger::info("Creating new ZonesOverlay window, hWnd = {}", (void*)window);
FancyZonesWindowUtils::MakeWindowTransparent(window);

View File

@@ -1,673 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using FancyZonesEditor.Models;
using FancyZonesEditorCommon.Data;
using Microsoft.FancyZones.UITests.Utils;
using Microsoft.FancyZonesEditor.UITests.Utils;
using Microsoft.FancyZonesEditor.UnitTests.Utils;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualBasic.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using static FancyZonesEditorCommon.Data.CustomLayouts;
namespace UITests_FancyZones
{
[TestClass]
public class DragWindowTests : UITestBase
{
private static readonly IOTestHelper AppZoneHistory = new FancyZonesEditorFiles().AppZoneHistoryIOHelper;
private static string nonPrimaryMouseButton = "Right";
private static string highlightColor = "#008CFF"; // set highlight color
private static string inactivateColor = "#AACDFF"; // set inactivate zone color
// set screen margin
private static int screenMarginTop;
private static int screenMarginLeft;
private static int screenMarginRight;
private static int screenMarginBottom;
// set 1/4 margin
private static int quarterX;
private static int quarterY;
private static string powertoysWindowName = "PowerToys Settings"; // set powertoys settings window name
public DragWindowTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Medium)
{
}
[TestInitialize]
public void TestInitialize()
{
// ClearOpenWindows
ClearOpenWindows();
// kill all processes related to FancyZones Editor to ensure a clean state
Session.KillAllProcessesByName("PowerToys.FancyZonesEditor");
AppZoneHistory.DeleteFile();
this.RestartScopeExe();
FancyZonesEditorHelper.Files.Restore();
// Set a custom layout with 1 subzones and clear app zone history
SetupCustomLayouts();
// Get the current mouse button setting
nonPrimaryMouseButton = SystemInformation.MouseButtonsSwapped ? "Left" : "Right";
// get PowerToys window Name
powertoysWindowName = ZoneSwitchHelper.GetActiveWindowTitle();
// Ensure FancyZones settings page is visible and enable FancyZones
LaunchFancyZones();
}
/// <summary>
/// Test Use Shift key to activate zones while dragging a window in FancyZones Zone Behaviour Settings
/// <list type="bullet">
/// <item>
/// <description>Verifies that holding Shift while dragging shows all zones as expected.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestShowZonesOnShiftDuringDrag")]
[TestCategory("FancyZones_Dragging #1")]
public void TestShowZonesOnShiftDuringDrag()
{
string testCaseName = nameof(TestShowZonesOnShiftDuringDrag);
Pane dragElement = Find<Pane>(By.Name("Non Client Input Sink Window")); // element to drag
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
var (initialColor, withShiftColor) = RunDragInteractions(
preAction: () =>
{
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
},
postAction: () =>
{
Session.PressKey(Key.Shift);
Task.Delay(500).Wait();
},
releaseAction: () =>
{
Session.ReleaseKey(Key.Shift);
Task.Delay(5000).Wait(); // Optional: Wait for a moment to ensure window switch
},
testCaseName: testCaseName);
string zoneColorWithoutShift = GetOutWindowPixelColor(30);
Assert.AreNotEqual(initialColor, withShiftColor, $"[{testCaseName}] Zone display failed.");
Assert.IsTrue(
withShiftColor == inactivateColor || withShiftColor == highlightColor,
$"[{testCaseName}] Zone display failed: withShiftColor was {withShiftColor}, expected {inactivateColor} or {highlightColor}.");
Assert.AreEqual(inactivateColor, withShiftColor, $"[{testCaseName}] Zone display failed.");
Assert.AreEqual(zoneColorWithoutShift, initialColor, $"[{testCaseName}] Zone deactivated failed.");
dragElement.ReleaseDrag();
Clean();
}
/// <summary>
/// Test dragging a window during Shift key press in FancyZones Zone Behaviour Settings
/// <list type="bullet">
/// <item>
/// <description>Verifies that dragging activates zones as expected.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestShowZonesOnDragDuringShift")]
[TestCategory("FancyZones_Dragging #2")]
public void TestShowZonesOnDragDuringShift()
{
string testCaseName = nameof(TestShowZonesOnDragDuringShift);
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
var (initialColor, withDragColor) = RunDragInteractions(
preAction: () =>
{
dragElement.Drag(offSet.Dx, offSet.Dy);
Session.PressKey(Key.Shift);
},
postAction: () =>
{
dragElement.DragAndHold(0, 0);
Task.Delay(5000).Wait();
},
releaseAction: () =>
{
dragElement.ReleaseDrag();
Session.ReleaseKey(Key.Shift);
},
testCaseName: testCaseName);
Assert.AreNotEqual(initialColor, withDragColor, $"[{testCaseName}] Zone color did not change; zone activation failed.");
Assert.AreEqual(highlightColor, withDragColor, $"[{testCaseName}] Zone color did not match the highlight color; activation failed.");
// double check by app-zone-history.json
string appZoneHistoryJson = AppZoneHistory.GetData();
string? zoneNumber = ZoneSwitchHelper.GetZoneIndexSetByAppName(powertoysWindowName, appZoneHistoryJson);
Assert.IsNull(zoneNumber, $"[{testCaseName}] AppZoneHistory layout was unexpectedly set.");
Clean();
}
/// <summary>
/// Test toggling zones using a non-primary mouse click during window dragging.
/// <list type="bullet">
/// <item>
/// <description>Verifies that clicking a non-primary mouse button deactivates zones while dragging a window.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestToggleZonesWithNonPrimaryMouseClick")]
[TestCategory("FancyZones_Dragging #3")]
public void TestToggleZonesWithNonPrimaryMouseClick()
{
string testCaseName = nameof(TestToggleZonesWithNonPrimaryMouseClick);
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
var (initialColor, withMouseColor) = RunDragInteractions(
preAction: () =>
{
// activate zone
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
},
postAction: () =>
{
// press non-primary mouse button to toggle zones
Session.PerformMouseAction(
nonPrimaryMouseButton == "Right" ? MouseActionType.RightClick : MouseActionType.LeftClick);
},
releaseAction: () =>
{
dragElement.ReleaseDrag();
},
testCaseName: testCaseName);
// check the zone color is deactivated
Assert.AreNotEqual(highlightColor, withMouseColor, $"[{testCaseName}] Zone deactivation failed.");
// check the zone color is activated
Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed.");
Clean();
}
/// <summary>
/// Test both use Shift and non primary mouse off settings.
/// <list type="bullet">
/// <item>
/// <description>Verifies that pressing the Shift key deactivates zones during a window drag-and-hold action.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestShowZonesWhenShiftAndMouseOff")]
[TestCategory("FancyZones_Dragging #4")]
public void TestShowZonesWhenShiftAndMouseOff()
{
string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOff);
Pane dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
var (initialColor, withShiftColor) = RunDragInteractions(
preAction: () =>
{
// activate zone
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
},
postAction: () =>
{
// press Shift Key to deactivate zones
Session.PressKey(Key.Shift);
Task.Delay(500).Wait();
},
releaseAction: () =>
{
dragElement.ReleaseDrag();
Session.ReleaseKey(Key.Shift);
},
testCaseName: testCaseName);
Assert.AreEqual(highlightColor, initialColor, $"[{testCaseName}] Zone activation failed.");
Assert.AreNotEqual(highlightColor, withShiftColor, $"[{testCaseName}] Zone deactivation failed.");
Clean();
}
/// <summary>
/// Test zone visibility when both Shift key and mouse settings are involved.
/// <list type="bullet">
/// <item>
/// <description>Verifies that zones are activated when Shift is pressed during drag, and deactivated by a non-primary mouse click.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestShowZonesWhenShiftAndMouseOn")]
[TestCategory("FancyZones_Dragging #5")]
public void TestShowZonesWhenShiftAndMouseOn()
{
string testCaseName = nameof(TestShowZonesWhenShiftAndMouseOn);
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
var (initialColor, withShiftColor) = RunDragInteractions(
preAction: () =>
{
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
},
postAction: () =>
{
Session.PressKey(Key.Shift);
},
releaseAction: () =>
{
},
testCaseName: testCaseName);
Assert.AreEqual(inactivateColor, withShiftColor, $"[{testCaseName}] show zone failed.");
Session.PerformMouseAction(
nonPrimaryMouseButton == "Right" ? MouseActionType.RightClick : MouseActionType.LeftClick);
string zoneColorWithMouse = GetOutWindowPixelColor(30);
Assert.AreEqual(initialColor, zoneColorWithMouse, $"[{nameof(TestShowZonesWhenShiftAndMouseOff)}] Zone deactivate failed.");
Session.ReleaseKey(Key.Shift);
dragElement.ReleaseDrag();
Clean();
}
/// <summary>
/// Test that a window becomes transparent during dragging when the transparent window setting is enabled.
/// <list type="bullet">
/// <item>
/// <description>Verifies that the window appears transparent while being dragged.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestMakeDraggedWindowTransparentOn")]
[TestCategory("FancyZones_Dragging #8")]
public void TestMakeDraggedWindowTransparentOn()
{
var pixel = GetPixelWhenMakeDraggedWindow();
Assert.AreNotEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOn)}] Window transparency failed.");
Clean();
}
/// <summary>
/// Test that a window remains opaque during dragging when the transparent window setting is disabled.
/// <list type="bullet">
/// <item>
/// <description>Verifies that the window is not transparent while being dragged.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestMakeDraggedWindowTransparentOff")]
[TestCategory("FancyZones_Dragging #8")]
public void TestMakeDraggedWindowTransparentOff()
{
var pixel = GetPixelWhenMakeDraggedWindow();
Assert.AreEqual(pixel.PixelInWindow, pixel.TransPixel, $"[{nameof(TestMakeDraggedWindowTransparentOff)}] Window without transparency failed.");
Clean();
}
private void Clean()
{
// clean app zone history file
AppZoneHistory.DeleteFile();
}
// Helper method to ensure the desktop has no open windows by clicking the "Show Desktop" button
private void ClearOpenWindows()
{
string desktopButtonName;
// Check for both possible button names (Win10/Win11)
if (this.FindAll<Microsoft.PowerToys.UITest.Button>("Show Desktop", 5000, true).Count == 0)
{
// win10
desktopButtonName = "Show desktop";
}
else
{
// win11
desktopButtonName = "Show Desktop";
}
this.Find<Microsoft.PowerToys.UITest.Button>(By.Name(desktopButtonName), 5000, true).Click(false, 500, 2000);
}
// Setup custom layout with 1 subzones
private void SetupCustomLayouts()
{
var customLayouts = new CustomLayouts();
var customLayoutListWrapper = CustomLayoutsList;
if (TestContext.TestName == "TestMakeDraggedWindowTransparentOff")
{
customLayoutListWrapper = CustomLayoutsListWithTwo;
}
FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(customLayoutListWrapper));
}
// launch FancyZones settings page
private void LaunchFancyZones()
{
if (this.FindAll<NavigationViewItem>("FancyZones").Count == 0)
{
this.Find<NavigationViewItem>("Windowing & Layouts").Click();
}
this.Find<NavigationViewItem>("FancyZones").Click();
this.Find<ToggleSwitch>("Enable FancyZones").Toggle(true);
this.Session.SetMainWindowSize(WindowSize.Large);
Find<Element>(By.AccessibilityId("HeaderPresenter")).Click();
this.Scroll(6, "Down"); // Pull the settings page up to make sure the settings are visible
ZoneBehaviourSettings(TestContext.TestName);
this.Find<Microsoft.PowerToys.UITest.Button>("Launch layout editor").Click(false, 500, 10000);
this.Session.Attach(PowerToysModule.FancyZone);
// pipeline machine may have an unstable delays, causing the custom layout to be unavailable as we set. then A retry is required.
// Console.WriteLine($"after launch, Custom layout data: {customLayoutData}");
try
{
this.Find<Microsoft.PowerToys.UITest.Button>("Maximize").Click();
// Set the FancyZones layout to a custom layout
this.Find<Element>(By.Name("Custom Column")).Click();
}
catch (Exception)
{
// Console.WriteLine($"[Exception] Failed to attach to FancyZones window. Retrying...{ex.Message}");
this.Find<Microsoft.PowerToys.UITest.Button>("Close").Click();
this.Session.Attach(PowerToysModule.PowerToysSettings);
SetupCustomLayouts();
this.Find<Microsoft.PowerToys.UITest.Button>("Launch layout editor").Click(false, 5000, 5000);
this.Session.Attach(PowerToysModule.FancyZone);
this.Find<Microsoft.PowerToys.UITest.Button>("Maximize").Click();
// customLayoutData = FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.GetData();
// Console.WriteLine($"after retry, Custom layout data: {customLayoutData}");
// Set the FancyZones layout to a custom layout
this.Find<Element>(By.Name("Custom Column")).Click();
}
// Get screen margins for positioning checks
GetScreenMargins();
// Close layout editor window
SendKeys(Key.Alt, Key.F4);
// make window small to detect zone easily
Session.Attach(powertoysWindowName, WindowSize.Small);
}
// Get the screen margins to calculate the dragged window position
private void GetScreenMargins()
{
var rect = Session.GetMainWindowRect();
screenMarginTop = rect.Top;
screenMarginLeft = rect.Left;
screenMarginRight = rect.Right;
screenMarginBottom = rect.Bottom;
(quarterX, quarterY) = ZoneSwitchHelper.GetScreenMargins(rect, 4);
}
// Get the mouse color of the pixel when make dragged window
private (string PixelInWindow, string TransPixel) GetPixelWhenMakeDraggedWindow()
{
var dragElement = Find<Pane>(By.Name("Non Client Input Sink Window"));
// maximize the window to make sure get pixel color more accurate
dragElement.DoubleClick();
var offSet = ZoneSwitchHelper.GetOffset(dragElement, quarterX, quarterY);
Session.PressKey(Key.Shift);
dragElement.DragAndHold(offSet.Dx, offSet.Dy);
Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the window is in position
Tuple<int, int> pos = GetMousePosition();
string pixelInWindow = this.GetPixelColorString(pos.Item1, pos.Item2);
Session.ReleaseKey(Key.Shift);
Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the window is in position
string transPixel = this.GetPixelColorString(pos.Item1, pos.Item2);
dragElement.ReleaseDrag();
return (pixelInWindow, transPixel);
}
/// <summary>
/// Gets the color of a pixel located just outside the application's window.
/// </summary>
/// <param name="spacing">
/// The minimum spacing (in pixels) required between the window edge and screen margin
/// to determine a safe pixel sampling area outside the window.
/// </param>
/// <returns>
/// A string representing the color of the pixel at the computed location outside the window,
/// </returns>
private string GetOutWindowPixelColor(int spacing)
{
var rect = Session.GetMainWindowRect();
int checkX, checkY;
if ((rect.Top - screenMarginTop) >= spacing)
{
checkX = rect.Left;
checkY = screenMarginTop + (spacing / 2);
}
else if ((screenMarginBottom - rect.Bottom) >= spacing)
{
checkX = rect.Left;
checkY = rect.Bottom + (spacing / 2);
}
else if ((rect.Left - screenMarginLeft) >= spacing)
{
checkX = rect.Left - (spacing / 2);
checkY = rect.Top;
}
else if ((screenMarginRight - rect.Right) >= spacing)
{
checkX = rect.Right + (spacing / 2);
checkY = rect.Top;
}
else
{
throw new ArgumentOutOfRangeException(nameof(spacing), "No sufficient margin to sample outside the window.");
}
Task.Delay(1000).Wait(); // Optional: Wait for a moment to ensure the mouse is in position
string zoneColor = this.GetPixelColorString(checkX, checkY);
return zoneColor;
}
/// <summary>
/// Runs drag interactions during a FancyZones test and returns the initial and final zone highlight colors.
/// </summary>
/// <param name="preAction">An optional action to execute before the drag starts (e.g., setup or key press).</param>
/// <param name="postAction">An optional action to execute after the drag is initiated but before it's released.</param>
/// <param name="releaseAction">An optional action to execute when releasing the dragged window (e.g., mouse up).</param>
/// <param name="testCaseName">The name of the test case for logging or diagnostics.</param>
/// <returns>
/// A tuple containing:
/// <list type="bullet">
/// <item><description><c>InitialZoneColor</c>: The zone highlight color before interaction completes.</description></item>
/// <item><description><c>FinalZoneColor</c>: The zone highlight color after interaction completes.</description></item>
/// </list>
/// </returns>
private (string InitialZoneColor, string FinalZoneColor) RunDragInteractions(
Action? preAction,
Action? postAction,
Action? releaseAction,
string testCaseName)
{
// Invoke the pre-action
preAction?.Invoke();
// Capture initial window state and zone color
var initialWindowRect = Session.GetMainWindowRect();
string initialZoneColor = GetOutWindowPixelColor(30);
// Invoke the post-action
postAction?.Invoke();
// Capture final zone color after the interaction
string finalZoneColor = GetOutWindowPixelColor(30);
releaseAction?.Invoke();
// Return initial and final zone colors
return (initialZoneColor, finalZoneColor);
}
// set the custom layout
private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper
{
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper>
{
new CustomLayouts.CustomLayoutWrapper
{
Uuid = "{63F09977-D327-4DAC-98F4-0C886CAE9517}",
Type = CustomLayout.Grid.TypeToString(),
Name = "Custom Column",
Info = new CustomLayouts().ToJsonElement(new CustomLayouts.GridInfoWrapper
{
Rows = 1,
Columns = 1,
RowsPercentage = new List<int> { 10000 },
ColumnsPercentage = new List<int> { 10000 },
CellChildMap = new int[][] { [0] },
SensitivityRadius = 20,
ShowSpacing = true,
Spacing = 10, // set spacing to 0 make sure the zone is full of the screen
}),
},
},
};
// set the custom layout with 1 subzones
private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsListWithTwo = new CustomLayouts.CustomLayoutListWrapper
{
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper>
{
new CustomLayouts.CustomLayoutWrapper
{
Uuid = "{63F09977-D327-4DAC-98F4-0C886CAE9517}",
Type = CustomLayout.Grid.TypeToString(),
Name = "Custom Column",
Info = new CustomLayouts().ToJsonElement(new CustomLayouts.GridInfoWrapper
{
Rows = 1,
Columns = 2,
RowsPercentage = new List<int> { 10000 },
ColumnsPercentage = new List<int> { 5000, 5000 },
CellChildMap = new int[][] { [0, 1] },
SensitivityRadius = 20,
ShowSpacing = true,
Spacing = 10,
}),
},
},
};
private string GetZoneColor(string color)
{
// Click on the "Highlight color" group
Find<Group>(color).Click();
// Optional: Ensure the hex textbox is found (to wait until the UI loads)
var hexBox = Find<Element>(By.AccessibilityId("HexTextBox"));
Task.Delay(500).Wait(); // Optional: Wait for the UI to update
// Get and return the RGB hex value text
var hexColorElement = Find<Element>("RGB hex");
// return mouse to color set position
Find<Group>(color).Click();
return hexColorElement.Text;
}
// set the zone behaviour settings
private void ZoneBehaviourSettings(string? testName)
{
// test settings
Microsoft.PowerToys.UITest.CheckBox useShiftCheckBox = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Hold Shift key to activate zones while dragging a window");
Microsoft.PowerToys.UITest.CheckBox useNonPrimaryMouseCheckBox = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Use a non-primary mouse button to toggle zone activation");
Microsoft.PowerToys.UITest.CheckBox makeDraggedWindowTransparent = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Make dragged window transparent");
Find<Microsoft.PowerToys.UITest.CheckBox>("Show zone number").SetCheck(false, 100);
Find<Slider>("Opacity (%)").QuickSetValue(100); // make highlight color visible with opacity 100
// Get the highlight and inactivate color from appearance settings
Find<Microsoft.PowerToys.UITest.ComboBox>("Zone appearance").Click();
Find<Element>("Custom colors").Click();
// get the highlight (activated) and inactivate zone color
highlightColor = GetZoneColor("Highlight color");
inactivateColor = GetZoneColor("Inactive color");
this.Scroll(2, "Down");
makeDraggedWindowTransparent.SetCheck(false, 500); // set make dragged window transparent to false or will influence the color comparison
this.Scroll(6, "Up");
switch (testName)
{
case "TestShowZonesOnShiftDuringDrag":
useShiftCheckBox.SetCheck(true, 500);
useNonPrimaryMouseCheckBox.SetCheck(false, 500);
break;
case "TestShowZonesOnDragDuringShift":
useShiftCheckBox.SetCheck(true, 500);
useNonPrimaryMouseCheckBox.SetCheck(false, 500);
break;
case "TestToggleZonesWithNonPrimaryMouseClick":
useShiftCheckBox.SetCheck(false, 500);
useNonPrimaryMouseCheckBox.SetCheck(true, 500);
break;
case "TestShowZonesWhenShiftAndMouseOff":
useShiftCheckBox.SetCheck(false, 500);
useNonPrimaryMouseCheckBox.SetCheck(false, 500);
break;
case "TestShowZonesWhenShiftAndMouseOn":
useShiftCheckBox.SetCheck(true, 500);
useNonPrimaryMouseCheckBox.SetCheck(true, 500);
break;
case "TestMakeDraggedWindowTransparentOff":
useShiftCheckBox.SetCheck(true, 500);
useNonPrimaryMouseCheckBox.SetCheck(false, 500);
break; // Added break to prevent fall-through
case "TestMakeDraggedWindowTransparentOn":
useNonPrimaryMouseCheckBox.SetCheck(false, 500);
useShiftCheckBox.SetCheck(true, 500);
this.Scroll(5, "Down"); // Pull the settings page up to make sure the settings are visible
makeDraggedWindowTransparent.SetCheck(true, 500);
this.Scroll(5, "Up");
break; // Added break to prevent fall-through
default:
throw new ArgumentException("Unsupported Test Case.", testName);
}
}
}
}

View File

@@ -1,673 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Automation;
using FancyZonesEditor.Models;
using FancyZonesEditorCommon.Data;
using Microsoft.FancyZonesEditor.UITests;
using Microsoft.FancyZonesEditor.UnitTests.Utils;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using static FancyZonesEditorCommon.Data.CustomLayouts;
using static Microsoft.FancyZonesEditor.UnitTests.Utils.FancyZonesEditorHelper;
namespace UITests_FancyZones
{
[TestClass]
public class LayoutApplyHotKeyTests : UITestBase
{
public LayoutApplyHotKeyTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
{
}
private static readonly EditorParameters.ParamsWrapper Parameters = new EditorParameters.ParamsWrapper
{
ProcessId = 1,
SpanZonesAcrossMonitors = false,
Monitors = new List<EditorParameters.NativeMonitorDataWrapper>
{
new EditorParameters.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 = true,
},
new EditorParameters.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 = false,
},
},
};
private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper
{
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper>
{
new CustomLayoutWrapper
{
Uuid = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}",
Type = CustomLayout.Grid.TypeToString(),
Name = "Grid custom layout",
Info = new CustomLayouts().ToJsonElement(new GridInfoWrapper
{
Rows = 2,
Columns = 2,
RowsPercentage = new List<int> { 5000, 5000 },
ColumnsPercentage = new List<int> { 5000, 5000 },
CellChildMap = new int[][] { [0, 1], [2, 3] },
SensitivityRadius = 30,
Spacing = 26,
ShowSpacing = false,
}),
},
new CustomLayoutWrapper
{
Uuid = "{0EB9BF3E-010E-46D7-8681-1879D1E111E1}",
Type = CustomLayout.Grid.TypeToString(),
Name = "Grid-9",
Info = new CustomLayouts().ToJsonElement(new GridInfoWrapper
{
Rows = 3,
Columns = 3,
RowsPercentage = new List<int> { 2333, 3333, 4334 },
ColumnsPercentage = new List<int> { 2333, 3333, 4334 },
CellChildMap = new int[][] { [0, 1, 2], [3, 4, 5], [6, 7, 8] },
SensitivityRadius = 20,
Spacing = 3,
ShowSpacing = false,
}),
},
new CustomLayoutWrapper
{
Uuid = "{E7807D0D-6223-4883-B15B-1F3883944C09}",
Type = CustomLayout.Canvas.TypeToString(),
Name = "Canvas custom layout",
Info = new CustomLayouts().ToJsonElement(new CanvasInfoWrapper
{
RefHeight = 1040,
RefWidth = 1920,
SensitivityRadius = 10,
Zones = new List<CanvasInfoWrapper.CanvasZoneWrapper>
{
new CanvasInfoWrapper.CanvasZoneWrapper
{
X = 0,
Y = 0,
Width = 500,
Height = 250,
},
new CanvasInfoWrapper.CanvasZoneWrapper
{
X = 500,
Y = 0,
Width = 1420,
Height = 500,
},
new CanvasInfoWrapper.CanvasZoneWrapper
{
X = 0,
Y = 250,
Width = 1920,
Height = 500,
},
},
}),
},
},
};
private static readonly LayoutHotkeys.LayoutHotkeysWrapper LayoutHotkeysList = new LayoutHotkeys.LayoutHotkeysWrapper
{
LayoutHotkeys = new List<LayoutHotkeys.LayoutHotkeyWrapper>
{
new LayoutHotkeys.LayoutHotkeyWrapper
{
Key = 0,
LayoutId = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}",
},
new LayoutHotkeys.LayoutHotkeyWrapper
{
Key = 1,
LayoutId = "{0EB9BF3E-010E-46D7-8681-1879D1E111E1}",
},
new LayoutHotkeys.LayoutHotkeyWrapper
{
Key = 2,
LayoutId = "{E7807D0D-6223-4883-B15B-1F3883944C09}",
},
},
};
private static readonly LayoutTemplates.TemplateLayoutsListWrapper TemplateLayoutsList = new LayoutTemplates.TemplateLayoutsListWrapper
{
LayoutTemplates = new List<LayoutTemplates.TemplateLayoutWrapper>
{
new LayoutTemplates.TemplateLayoutWrapper
{
Type = LayoutType.Blank.TypeToString(),
},
new LayoutTemplates.TemplateLayoutWrapper
{
Type = LayoutType.Focus.TypeToString(),
ZoneCount = 10,
},
new LayoutTemplates.TemplateLayoutWrapper
{
Type = LayoutType.Rows.TypeToString(),
ZoneCount = 2,
ShowSpacing = true,
Spacing = 10,
SensitivityRadius = 10,
},
new LayoutTemplates.TemplateLayoutWrapper
{
Type = LayoutType.Columns.TypeToString(),
ZoneCount = 2,
ShowSpacing = true,
Spacing = 20,
SensitivityRadius = 20,
},
new LayoutTemplates.TemplateLayoutWrapper
{
Type = LayoutType.Grid.TypeToString(),
ZoneCount = 4,
ShowSpacing = false,
Spacing = 10,
SensitivityRadius = 30,
},
new LayoutTemplates.TemplateLayoutWrapper
{
Type = LayoutType.PriorityGrid.TypeToString(),
ZoneCount = 3,
ShowSpacing = true,
Spacing = 1,
SensitivityRadius = 40,
},
},
};
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(Parameters));
LayoutTemplates layoutTemplates = new LayoutTemplates();
FancyZonesEditorHelper.Files.LayoutTemplatesIOHelper.WriteData(layoutTemplates.Serialize(TemplateLayoutsList));
CustomLayouts customLayouts = new CustomLayouts();
FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(CustomLayoutsList));
DefaultLayouts defaultLayouts = new DefaultLayouts();
DefaultLayouts.DefaultLayoutsListWrapper defaultLayoutsListWrapper = new DefaultLayouts.DefaultLayoutsListWrapper
{
DefaultLayouts = new List<DefaultLayouts.DefaultLayoutWrapper>
{
new DefaultLayouts.DefaultLayoutWrapper
{
MonitorConfiguration = MonitorConfigurationType.Horizontal.TypeToString(),
Layout = new DefaultLayouts.DefaultLayoutWrapper.LayoutWrapper
{
Type = LayoutType.Focus.TypeToString(),
ZoneCount = 4,
ShowSpacing = true,
Spacing = 5,
SensitivityRadius = 20,
},
},
new DefaultLayouts.DefaultLayoutWrapper
{
MonitorConfiguration = MonitorConfigurationType.Vertical.TypeToString(),
Layout = new DefaultLayouts.DefaultLayoutWrapper.LayoutWrapper
{
Type = LayoutType.Custom.TypeToString(),
Uuid = "{0D6D2F58-9184-4804-81E4-4E4CC3476DC1}",
ZoneCount = 0,
ShowSpacing = false,
Spacing = 0,
SensitivityRadius = 0,
},
},
},
};
FancyZonesEditorHelper.Files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(defaultLayoutsListWrapper));
LayoutHotkeys layoutHotkeys = new LayoutHotkeys();
FancyZonesEditorHelper.Files.LayoutHotkeysIOHelper.WriteData(layoutHotkeys.Serialize(LayoutHotkeysList));
AppliedLayouts appliedLayouts = new AppliedLayouts();
AppliedLayouts.AppliedLayoutsListWrapper appliedLayoutsWrapper = new AppliedLayouts.AppliedLayoutsListWrapper
{
AppliedLayouts = new List<AppliedLayouts.AppliedLayoutWrapper> { },
};
FancyZonesEditorHelper.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
this.RestartScopeExe();
}
[TestMethod("FancyZones.Settings.TestApplyHotKey")]
[TestCategory("FancyZones #1")]
public void TestApplyHotKey()
{
this.OpenFancyZonesPanel();
this.ControlQuickLayoutSwitch(true);
// Set Hotkey
this.AttachFancyZonesEditor();
var layout = "Grid custom layout";
Session.Find<Element>(layout).Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
const string key = "0";
var hotkeyComboBox = Session.Find<Element>(By.AccessibilityId(AccessibilityId.HotkeyComboBox));
Assert.IsNotNull(hotkeyComboBox);
hotkeyComboBox.Click();
var popup = Session.Find<Element>(By.ClassName(ClassName.Popup));
Assert.IsNotNull(popup);
popup.Find<Element>($"{key}").Click(); // assign a free hotkey
Task.Delay(3000).Wait();
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num0);
Task.Delay(3000).Wait();
this.AttachFancyZonesEditor();
var element = this.Find<Element>(layout);
Assert.IsTrue(element.Selected, $"{element.Selected} Grid custom layout is not visible");
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num1);
Task.Delay(3000).Wait();
this.AttachFancyZonesEditor();
element = this.Find<Element>("Grid-9");
Assert.IsTrue(element.Selected, $"{element.Selected} Grid-9 is not visible");
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num2);
Task.Delay(3000).Wait();
this.AttachFancyZonesEditor();
element = this.Find<Element>("Canvas custom layout");
Assert.IsTrue(element.Selected, $"{element.Selected} Canvas custom layout is not visible");
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
}
/*
[TestMethod]
[TestCategory("FancyZones #2")]
public void TestDragShiftHotKey()
{
this.OpenFancyZonesPanel();
this.ControlQuickLayoutSwitch(true);
int screenWidth = 1920; // default 1920
int targetX = screenWidth / 2 / 3;
int targetY = screenWidth / 2 / 2;
LaunchHostFromSetting();
this.Session.Attach(PowerToysModule.Hosts, WindowSize.Large_Vertical);
var hostsView = Find<Pane>(By.Name("Non Client Input Sink Window"));
hostsView.DoubleClick(); // maximize the window
hostsView.HoldShiftToDrag(Key.Shift, targetX, targetY);
SendKeys(Key.Num0);
hostsView.ReleaseAction();
hostsView.ReleaseKey(Key.Shift);
SendKeys(Key.Alt, Key.F4);
// Attach FancyZones Editor
this.AttachPowertoySetting();
this.Find<Pane>(By.ClassName("InputNonClientPointerSource")).Click();
this.OpenFancyZonesPanel(isMax: false);
this.AttachFancyZonesEditor();
var elements = this.FindAll<Element>("Grid custom layout");
if (elements.Count == 0)
{
this.Session.Attach(PowerToysModule.Hosts, WindowSize.Large_Vertical);
hostsView = Find<Pane>(By.Name("Non Client Input Sink Window"));
hostsView.DoubleClick(); // maximize the window
hostsView.HoldShiftToDrag(Key.Shift, targetX, targetY);
SendKeys(Key.Num0);
hostsView.ReleaseAction();
hostsView.ReleaseKey(Key.Shift);
SendKeys(Key.Alt, Key.F4);
this.AttachPowertoySetting();
this.Find<Pane>(By.ClassName("InputNonClientPointerSource")).Click();
this.OpenFancyZonesPanel(isMax: false);
this.AttachFancyZonesEditor();
elements = this.FindAll<Element>("Grid custom layout");
}
Assert.IsTrue(elements[0].Selected, "Grid custom layout is not visible");
this.CloseFancyZonesEditor();
Clean();
}
*/
[TestMethod("FancyZones.Settings.HotKeyWindowFlashTest")]
[TestCategory("FancyZones #3")]
public void HotKeyWindowFlashTest()
{
this.OpenFancyZonesPanel();
this.ControlQuickLayoutSwitch(true);
this.TryReaction();
int tries = 24;
Pull(tries, "down");
var switchGroup = this.Find<Group>("Enable quick layout switch");
switchGroup.Click();
var checkbox1 = switchGroup.Find<Element>("Flash zones when switching layout");
if (checkbox1.GetAttribute("Toggle.ToggleState") == "0")
{
checkbox1.Click();
}
this.Session.PressKey(Key.Win);
this.Session.PressKey(Key.Ctrl);
this.Session.PressKey(Key.Alt);
this.Session.PressKey(Key.Num0);
bool res = this.IsWindowOpen("FancyZones_ZonesOverlay");
Assert.IsTrue(res, $" HotKeyWindowFlash Test error: FancyZones_ZonesOverlay is not open");
this.Session.ReleaseKey(Key.Win);
this.Session.ReleaseKey(Key.Ctrl);
this.Session.ReleaseKey(Key.Alt);
this.Session.ReleaseKey(Key.Num0);
var checkbox2 = this.Find<CheckBox>("Flash zones when switching layout");
if (checkbox2.GetAttribute("Toggle.ToggleState") == "1")
{
checkbox2.Click();
}
// this.CloseFancyZonesEditor();
Clean();
}
[TestMethod("FancyZones.Settings.TestDisableApplyHotKey")]
[TestCategory("FancyZones #4")]
public void TestDisableApplyHotKey()
{
this.OpenFancyZonesPanel();
this.ControlQuickLayoutSwitch(false);
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num0);
this.AttachFancyZonesEditor();
var element = this.Find<Element>("Grid custom layout");
Assert.IsFalse(element.Selected, $"{element.Selected} Grid custom layout is not visible");
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num1);
this.AttachFancyZonesEditor();
element = this.Find<Element>("Grid-9");
Assert.IsFalse(element.Selected, $"{element.Selected} Grid-9 is not visible");
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
SendKeys(Key.Win, Key.Ctrl, Key.Alt, Key.Num2);
this.AttachFancyZonesEditor();
element = this.Find<Element>("Canvas custom layout");
Assert.IsFalse(element.Selected, $"{element.Selected} Canvas custom layout is not visible");
this.CloseFancyZonesEditor();
this.AttachPowertoySetting();
Clean();
}
[TestMethod("FancyZones.Settings.TestVirtualDesktopLayout")]
[TestCategory("FancyZones #6")]
public void TestVirtualDesktopLayout()
{
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
var element = this.Find<Element>("Grid custom layout");
element.Click();
this.CloseFancyZonesEditor();
this.ExitScopeExe();
// Add virtual desktop
SendKeys(Key.Ctrl, Key.Win, Key.D);
this.RestartScopeExe();
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
element = this.Find<Element>("Grid custom layout");
Assert.IsTrue(element.Selected, $"{element.Selected} Canvas custom layout is not visible");
this.CloseFancyZonesEditor();
// close the virtual desktop
SendKeys(Key.Ctrl, Key.Win, Key.Right);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
SendKeys(Key.Ctrl, Key.Win, Key.F4);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
Clean();
}
[TestMethod("FancyZones.Settings.TestVirtualDesktopLayoutExt")]
[TestCategory("FancyZones #7")]
public void TestVirtualDesktopLayoutExt()
{
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
var element = this.Find<Element>("Grid custom layout");
element.Click();
this.CloseFancyZonesEditor();
this.ExitScopeExe();
// Add virtual desktop
SendKeys(Key.Ctrl, Key.Win, Key.D);
this.RestartScopeExe();
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
element = this.Find<Element>("Grid-9");
element.Click();
this.CloseFancyZonesEditor();
this.ExitScopeExe();
SendKeys(Key.Ctrl, Key.Win, Key.Left);
this.RestartScopeExe();
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
element = this.Find<Element>("Grid custom layout");
Assert.IsTrue(element.Selected, $"{element.Selected} Grid custom layout is not visible");
this.CloseFancyZonesEditor();
this.ExitScopeExe();
// close the virtual desktop
SendKeys(Key.Ctrl, Key.Win, Key.Right);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
SendKeys(Key.Ctrl, Key.Win, Key.F4);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
Clean();
}
[TestMethod("FancyZones.Settings.TestDeleteCustomLayoutBehavior")]
[TestCategory("FancyZones #8")]
public void TestDeleteCustomLayoutBehavior()
{
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
this.Find<Element>("Grid custom layout").Click();
this.Find<Element>("Grid custom layout").Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
Session.Find<Button>(By.AccessibilityId(AccessibilityId.DeleteLayoutButton)).Click();
Session.SendKeySequence(Key.Tab, Key.Enter);
// verify the empty layout is selected
Assert.IsTrue(Session.Find<Element>(TestConstants.TemplateLayoutNames[LayoutType.Blank])!.Selected);
Clean();
}
[TestMethod("FancyZones.Settings.TestCreateGridLayoutChangeMonitorSetting")]
[TestCategory("FancyZones #9")]
public void TestCreateGridLayoutChangeMonitorSetting()
{
this.OpenFancyZonesPanel();
this.AttachFancyZonesEditor();
string name = "Custom layout 1";
this.Session.Find<Element>(By.AccessibilityId(AccessibilityId.NewLayoutButton)).Click();
this.Session.Find<Element>(By.AccessibilityId(AccessibilityId.PrimaryButton)).Click();
this.Session.Find<Button>(ElementName.Save).Click();
// verify new layout presented
Assert.IsNotNull(Session.Find<Element>(name));
this.CloseFancyZonesEditor();
int nowHeight = UITestBase.MonitorInfoData.Monitors[UITestBase.MonitorInfoData.Monitors.Count - 1].PelsHeight;
int nowWidth = UITestBase.MonitorInfoData.Monitors[UITestBase.MonitorInfoData.Monitors.Count - 1].PelsWidth;
int height = UITestBase.MonitorInfoData.Monitors[0].PelsHeight;
int width = UITestBase.MonitorInfoData.Monitors[0].PelsWidth;
UITestBase.NativeMethods.ChangeDisplayResolution(width, height);
this.AttachPowertoySetting();
this.AttachFancyZonesEditor();
var maxButton = this.Find<Button>("Maximize");
maxButton.Click(); // maximize the window
var resolution = this.Session.Find<Element>(By.AccessibilityId("Monitors")).Find<Element>("Monitor 1").Find<Element>(By.AccessibilityId("ResolutionText"));
if (resolution.Text != "640 × 480")
{
this.CloseFancyZonesEditor();
UITestBase.NativeMethods.ChangeDisplayResolution(nowWidth, nowHeight);
Assert.AreEqual("640 × 480", resolution.Text);
}
this.CloseFancyZonesEditor();
UITestBase.NativeMethods.ChangeDisplayResolution(nowWidth, nowHeight);
Clean();
}
private void OpenFancyZonesPanel(bool launchAsAdmin = false, bool isMax = false)
{
var windowingElement = this.Find<NavigationViewItem>("Windowing & Layouts");
// Goto FancyZones Editor setting page
if (this.FindAll<NavigationViewItem>("FancyZones").Count == 0)
{
// Expand Advanced list-group if needed
windowingElement.Click();
}
windowingElement.Find<Element>("FancyZones").Click();
this.Find<ToggleSwitch>("Enable FancyZones").Toggle(true);
if (isMax == true)
{
this.Find<Button>("Maximize").Click(); // maximize the window
}
this.Find<Custom>("Editor").Find<TextBlock>(By.AccessibilityId("HeaderPresenter")).Click();
}
private void ControlQuickLayoutSwitch(bool flag)
{
this.TryReaction();
int tries = 24;
Pull(tries, "down"); // Pull the setting page up to make sure the setting is visible
this.Find<ToggleSwitch>("Enable quick layout switch").Toggle(flag);
tries = 24;
Pull(tries, "up");
}
private void TryReaction()
{
this.Find<Custom>("Editor").Find<TextBlock>(By.AccessibilityId("HeaderPresenter")).Click();
}
private void AttachPowertoySetting()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
private void AttachFancyZonesEditor()
{
Task.Delay(4000).Wait();
this.Find<Button>("Launch layout editor").Click();
Task.Delay(3000).Wait();
this.Session.Attach(PowerToysModule.FancyZone);
Task.Delay(3000).Wait();
}
private void CloseFancyZonesEditor()
{
this.Session.Find<Button>("Close").Click();
}
private void Clean()
{
// clean app zone history file
FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.DeleteFile();
FancyZonesEditorHelper.Files.LayoutHotkeysIOHelper.DeleteFile();
FancyZonesEditorHelper.Files.LayoutTemplatesIOHelper.DeleteFile();
}
private void Pull(int tries = 5, string direction = "up")
{
Key keyToSend = direction == "up" ? Key.Up : Key.Down;
for (int i = 0; i < tries; i++)
{
SendKeys(keyToSend);
}
}
private void LaunchHostFromSetting(bool showWarning = false, bool launchAsAdmin = false)
{
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Hosts File Editor").Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Advanced").Click();
}
this.Find<NavigationViewItem>("Hosts File Editor").Click();
Task.Delay(1000).Wait();
this.Find<ToggleSwitch>("Enable Hosts File Editor").Toggle(true);
this.Find<ToggleSwitch>("Launch as administrator").Toggle(launchAsAdmin);
this.Find<ToggleSwitch>("Show a warning at startup").Toggle(showWarning);
// launch Hosts File Editor
this.Find<Button>("Launch Hosts File Editor").Click();
Task.Delay(5000).Wait();
}
}
}

View File

@@ -1,313 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Threading.Tasks;
using FancyZonesEditor.Models;
using FancyZonesEditorCommon.Data;
using Microsoft.FancyZones.UITests.Utils;
using Microsoft.FancyZonesEditor.UITests.Utils;
using Microsoft.FancyZonesEditor.UnitTests.Utils;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_FancyZones
{
[TestClass]
public class OneZoneSwitchTests : UITestBase
{
private static readonly int SubZones = 2;
private static readonly IOTestHelper AppZoneHistory = new FancyZonesEditorFiles().AppZoneHistoryIOHelper;
private static string powertoysWindowName = "PowerToys Settings"; // set powertoys settings window name
public OneZoneSwitchTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Medium)
{
}
[TestInitialize]
public void TestInitialize()
{
// kill all processes related to FancyZones Editor to ensure a clean state
Session.KillAllProcessesByName("PowerToys.FancyZonesEditor");
AppZoneHistory.DeleteFile();
this.RestartScopeExe();
FancyZonesEditorHelper.Files.Restore();
// Set a custom layout with 1 subzones and clear app zone history
SetupCustomLayouts();
// get PowerToys window Name
powertoysWindowName = ZoneSwitchHelper.GetActiveWindowTitle();
// Launch FancyZones
LaunchFancyZones();
// Launch the Hosts File Editor
LaunchFromSetting();
}
/// <summary>
/// Test switching between two snapped windows using keyboard shortcuts in FancyZones
/// <list type="bullet">
/// <item>
/// <description>Verifies that after snapping two windows, the active window switches correctly using Win+PageDown.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod]
[TestCategory("FancyZones #Switch between windows in the current zone #1")]
public void TestSwitchWindow()
{
var (preWindow, postWindow) = SnapToOneZone();
string? activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreEqual(postWindow, activeWindowTitle);
// switch to the previous window by shortcut win+page down
SendKeys(Key.Win, Key.PageDown);
activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreEqual(preWindow, activeWindowTitle);
Clean(); // close the windows
}
/// <summary>
/// Test window switch behavior across virtual desktops in FancyZones
/// <list type="bullet">
/// <item>
/// <description>Verifies that a window remains correctly snapped after switching desktops and can be switched using Win+PageDown.</description>
/// </item>
/// </summary>
[TestMethod("FancyZones.Settings.TestSwitchAfterDesktopChange")]
[TestCategory("FancyZones #Switch between windows in the current zone #2")]
public void TestSwitchAfterDesktopChange()
{
var (preWindow, postWindow) = SnapToOneZone();
string? windowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreEqual(postWindow, windowTitle);
// Add virtual desktop
SendKeys(Key.Ctrl, Key.Win, Key.D);
// return back
SendKeys(Key.Ctrl, Key.Win, Key.Left);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
string? returnWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreEqual(postWindow, returnWindowTitle);
// check shortcut
SendKeys(Key.Win, Key.PageDown);
string? activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreEqual(preWindow, activeWindowTitle);
// close the virtual desktop
SendKeys(Key.Ctrl, Key.Win, Key.Right);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
SendKeys(Key.Ctrl, Key.Win, Key.F4);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
Clean(); // close the windows
}
/// <summary>
/// Test window switching shortcut behavior when the shortcut is disabled in FancyZones settings
/// <list type="bullet">
/// <item>
/// <description>Verifies that pressing Win+PageDown does not switch to the previously snapped window when the shortcut is disabled.</description>
/// </item>
/// </list>
/// </summary>
[TestMethod("FancyZones.Settings.TestSwitchShortCutDisable")]
[TestCategory("FancyZones #Switch between windows in the current zone #3")]
public void TestSwitchShortCutDisable()
{
var (preWindow, postWindow) = SnapToOneZone();
string? activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreEqual(postWindow, activeWindowTitle);
// switch to the previous window by shortcut win+page down
SendKeys(Key.Win, Key.PageDown);
Task.Delay(500).Wait(); // Optional: Wait for a moment to ensure window switch
activeWindowTitle = ZoneSwitchHelper.GetActiveWindowTitle();
Assert.AreNotEqual(preWindow, activeWindowTitle);
Clean(); // close the windows
}
private (string PreWindow, string PostWindow) SnapToOneZone()
{
this.Session.Attach(PowerToysModule.Hosts, WindowSize.Large_Vertical);
var hostsView = Find<Pane>(By.Name("Non Client Input Sink Window"));
hostsView.DoubleClick(); // maximize the window
var rect = Session.GetMainWindowRect();
var (targetX, targetY) = ZoneSwitchHelper.GetScreenMargins(rect, 4);
var offSet = ZoneSwitchHelper.GetOffset(hostsView, targetX, targetY);
DragWithShift(hostsView, offSet);
string preWindow = ZoneSwitchHelper.GetActiveWindowTitle();
// Attach the PowerToys settings window to the front
Session.Attach(powertoysWindowName, WindowSize.UnSpecified);
string windowNameFront = ZoneSwitchHelper.GetActiveWindowTitle();
Pane settingsView = Find<Pane>(By.Name("Non Client Input Sink Window"));
settingsView.DoubleClick(); // maximize the window
DragWithShift(settingsView, offSet);
string appZoneHistoryJson = AppZoneHistory.GetData();
string? zoneIndexOfFileWindow = ZoneSwitchHelper.GetZoneIndexSetByAppName("PowerToys.Hosts.exe", appZoneHistoryJson); // explorer.exe
string? zoneIndexOfPowertoys = ZoneSwitchHelper.GetZoneIndexSetByAppName("PowerToys.Settings.exe", appZoneHistoryJson);
// check the AppZoneHistory layout is set and in the same zone
Assert.AreEqual(zoneIndexOfPowertoys, zoneIndexOfFileWindow);
return (preWindow, powertoysWindowName);
}
private void DragWithShift(Pane settingsView, (int Dx, int Dy) offSet)
{
Session.PressKey(Key.Shift);
settingsView.DragAndHold(offSet.Dx, offSet.Dy);
Task.Delay(1000).Wait(); // Wait for drag to start (optional)
settingsView.ReleaseDrag();
Task.Delay(1000).Wait(); // Wait after drag (optional)
Session.ReleaseKey(Key.Shift);
}
private static readonly CustomLayouts.CustomLayoutListWrapper CustomLayoutsList = new CustomLayouts.CustomLayoutListWrapper
{
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper>
{
new CustomLayouts.CustomLayoutWrapper
{
Uuid = "{63F09977-D327-4DAC-98F4-0C886CAE9517}",
Type = CustomLayout.Grid.TypeToString(),
Name = "Custom Column",
Info = new CustomLayouts().ToJsonElement(new CustomLayouts.GridInfoWrapper
{
Rows = 1,
Columns = SubZones,
RowsPercentage = new List<int> { 10000 },
ColumnsPercentage = new List<int> { 5000, 5000 },
CellChildMap = new int[][] { [0, 1] },
SensitivityRadius = 20,
ShowSpacing = true,
Spacing = 10,
}),
},
},
};
// clean window
private void Clean()
{
// Close First window
SendKeys(Key.Alt, Key.F4);
// Close Second window
SendKeys(Key.Alt, Key.F4);
// clean app zone history file
AppZoneHistory.DeleteFile();
}
// Setup custom layout with 1 subzones
private void SetupCustomLayouts()
{
var customLayouts = new CustomLayouts();
var customLayoutListWrapper = CustomLayoutsList;
FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(customLayoutListWrapper));
}
// launch FancyZones settings page
private void LaunchFancyZones()
{
// Goto FancyZones setting page
if (this.FindAll<NavigationViewItem>("FancyZones").Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Windowing & Layouts").Click();
}
this.Find<NavigationViewItem>("FancyZones").Click();
this.Find<ToggleSwitch>("Enable FancyZones").Toggle(true);
this.Session.SetMainWindowSize(WindowSize.Large);
// fixed settings
this.Find<CheckBox>("Hold Shift key to activate zones while dragging a window").SetCheck(true, 500);
// should bind mouse to suitable zone for scrolling
Find<Element>(By.AccessibilityId("HeaderPresenter")).Click();
this.Scroll(9, "Down"); // Pull the setting page up to make sure the setting is visible
bool switchWindowEnable = TestContext.TestName == "TestSwitchShortCutDisable" ? false : true;
this.Find<ToggleSwitch>("Switch between windows in the current zone").Toggle(switchWindowEnable);
Task.Delay(500).Wait(); // Wait for the setting to be applied
this.Scroll(9, "Up"); // Pull the setting page down to make sure the setting is visible
this.Find<Button>("Launch layout editor").Click(false, 500, 5000);
this.Session.Attach(PowerToysModule.FancyZone);
// pipeline machine may have an unstable delays, causing the custom layout to be unavailable as we set. then A retry is required.
// Console.WriteLine($"after launch, Custom layout data: {customLayoutData}");
try
{
// Set the FancyZones layout to a custom layout
this.Find<Element>(By.Name("Custom Column")).Click();
}
catch (Exception)
{
// Console.WriteLine($"[Exception] Failed to attach to FancyZones window. Retrying...{ex.Message}");
this.Find<Microsoft.PowerToys.UITest.Button>("Close").Click();
this.Session.Attach(PowerToysModule.PowerToysSettings);
SetupCustomLayouts();
this.Find<Microsoft.PowerToys.UITest.Button>("Launch layout editor").Click(false, 5000, 5000);
this.Session.Attach(PowerToysModule.FancyZone);
// customLayoutData = FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.GetData();
// Console.WriteLine($"after retry, Custom layout data: {customLayoutData}");
// Set the FancyZones layout to a custom layout
this.Find<Element>(By.Name("Custom Column")).Click();
}
// Close layout editor window
SendKeys(Key.Alt, Key.F4);
this.Session.Attach(powertoysWindowName);
}
private void LaunchFromSetting(bool showWarning = false, bool launchAsAdmin = false)
{
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Hosts File Editor").Count == 0)
{
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Advanced").Click();
}
this.Find<NavigationViewItem>("Hosts File Editor").Click();
Task.Delay(1000).Wait();
this.Find<ToggleSwitch>("Enable Hosts File Editor").Toggle(true);
this.Find<ToggleSwitch>("Launch as administrator").Toggle(launchAsAdmin);
this.Find<ToggleSwitch>("Show a warning at startup").Toggle(showWarning);
// launch Hosts File Editor
this.Find<Button>("Launch Hosts File Editor").Click();
Task.Delay(5000).Wait();
}
}
}

View File

@@ -27,15 +27,5 @@
<ItemGroup>
<Folder Include="Properties\" />
<ProjectReference Include="..\editor\FancyZonesEditor\FancyZonesEditor.csproj" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<ProjectReference Include="..\UITests-FancyZonesEditor\UITests-FancyZonesEditor.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\editor\FancyZonesEditor\FancyZonesEditor.csproj" />
<ProjectReference Include="..\UITests-FancyZonesEditor\UITests-FancyZonesEditor.csproj" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,98 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Internal;
namespace Microsoft.FancyZones.UITests.Utils
{
public class ZoneSwitchHelper
{
public static string? GetZoneIndexSetByAppName(string exeName, string json)
{
if (string.IsNullOrEmpty(exeName) || string.IsNullOrEmpty(json))
{
return null;
}
try
{
using var doc = JsonDocument.Parse(json);
var historyArray = doc.RootElement.GetProperty("app-zone-history");
foreach (var item in historyArray.EnumerateArray())
{
if (item.TryGetProperty("app-path", out var appPathElement) &&
appPathElement.GetString() is string path &&
path.EndsWith(exeName, StringComparison.OrdinalIgnoreCase))
{
var history = item.GetProperty("history");
if (history.GetArrayLength() > 0)
{
return history[0].GetProperty("zone-index-set")[0].GetRawText();
}
}
}
}
catch (JsonException ex)
{
throw new InvalidOperationException("JSON parse error: " + ex.Message, ex);
}
return null;
}
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public static string GetActiveWindowTitle()
{
const int nChars = 256;
StringBuilder buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, buff, nChars) > 0)
{
return buff.ToString();
}
else
{
// Handle the error if needed
throw new InvalidOperationException("Failed to get window title.");
}
}
public static (int Dx, int Dy) GetOffset(Element element, int targetX, int targetY)
{
Assert.IsNotNull(element.Rect, "element is null");
var rect = element.Rect.Value;
return (targetX - rect.X, targetY - rect.Y);
}
public static (int X, int Y) GetScreenMargins((int Left, int Top, int Right, int Bottom) rect, int quantile = 4)
{
if (quantile == 0)
{
throw new ArgumentException("Quantile cannot be zero.", nameof(quantile));
}
int x = (rect.Left + rect.Right) / quantile;
int y = (rect.Top + rect.Bottom) / quantile;
return (x, y);
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class ApplyLayoutTests : UITestBase
{
public ApplyLayoutTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -135,7 +135,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(Parameters));
@@ -196,6 +195,12 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void ApplyCustomLayout()
{
@@ -229,8 +234,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(Parameters.Monitors[0].MonitorNumber, data.AppliedLayouts[0].Device.MonitorNumber);
}
[TestMethod("FancyZonesEditor.Basic.ApplyLayoutsOnEachMonitor")]
[TestCategory("FancyZones Editor #10")]
[TestMethod]
public void ApplyLayoutsOnEachMonitor()
{
// apply the layout on the first monitor
@@ -257,8 +261,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(secondLayout.Uuid, data.AppliedLayouts.Find(x => x.Device.MonitorNumber == 2).AppliedLayout.Uuid);
}
[TestMethod("FancyZonesEditor.Basic.ApplyTemplateWithDifferentParametersOnEachMonitor")]
[TestCategory("FancyZones Editor #10")]
[TestMethod]
public void ApplyTemplateWithDifferentParametersOnEachMonitor()
{
var layoutType = LayoutType.Columns;
@@ -267,10 +270,10 @@ namespace Microsoft.FancyZonesEditor.UITests
// apply the layout on the first monitor, set parameters
Session.Find<Element>(layoutName).Click();
Session.Find<Element>(layoutName).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
slider.SendKeys(Keys.Right);
var expectedFirstLayoutZoneCount = int.Parse(slider.Text!, CultureInfo.InvariantCulture);
Session.Find<Button>(ElementName.Save).Click();
@@ -278,16 +281,16 @@ namespace Microsoft.FancyZonesEditor.UITests
Session.Find<Element>(PowerToys.UITest.By.AccessibilityId("Monitors")).Find<Element>("Monitor 2").Click();
Session.Find<Element>(layoutName).Click();
Session.Find<Element>(layoutName).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Left);
slider.SendKeys(Keys.Left);
var expectedSecondLayoutZoneCount = int.Parse(slider.Text!, CultureInfo.InvariantCulture);
Session.Find<Button>(ElementName.Save).Click();
// verify the layout on the first monitor wasn't changed
Session.Find<Element>(PowerToys.UITest.By.AccessibilityId("Monitors")).Find<Element>("Monitor 1").Click();
Session.Find<Element>(layoutName).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
Assert.IsNotNull(slider);
Assert.AreEqual(expectedFirstLayoutZoneCount, int.Parse(slider.Text!, CultureInfo.InvariantCulture));
Session.Find<Button>(ElementName.Cancel).Click();

View File

@@ -17,7 +17,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class CopyLayoutTests : UITestBase
{
public CopyLayoutTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -76,7 +76,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
@@ -173,8 +172,13 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestMethod("FancyZonesEditor.Basic.CopyTemplate_FromEditLayoutWindow")]
[TestCategory("FancyZones Editor #4")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void CopyTemplate_FromEditLayoutWindow()
{
string copiedLayoutName = TestConstants.TemplateLayoutNames[LayoutType.Focus] + " (1)";
@@ -191,8 +195,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == copiedLayoutName));
}
[TestMethod("FancyZonesEditor.Basic.CopyTemplate_FromEditLayoutWindow")]
[TestCategory("FancyZones Editor #4")]
[TestMethod]
public void CopyTemplate_FromContextMenu()
{
string copiedLayoutName = TestConstants.TemplateLayoutNames[LayoutType.Rows] + " (1)";
@@ -208,8 +211,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == copiedLayoutName));
}
[TestMethod("FancyZonesEditor.Basic.CopyTemplate_DefaultLayout")]
[TestCategory("FancyZones Editor #13")]
[TestMethod]
public void CopyTemplate_DefaultLayout()
{
string copiedLayoutName = TestConstants.TemplateLayoutNames[LayoutType.PriorityGrid] + " (1)";
@@ -241,8 +243,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(defaultLayouts.Serialize(DefaultLayouts), defaultLayouts.Serialize(defaultLayoutData));
}
[TestMethod("FancyZonesEditor.Basic.CopyCustomLayout_FromEditLayoutWindow")]
[TestCategory("FancyZones Editor #4")]
[TestMethod]
public void CopyCustomLayout_FromEditLayoutWindow()
{
string copiedLayoutName = CustomLayouts.CustomLayouts[0].Name + " (1)";
@@ -259,8 +260,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == copiedLayoutName));
}
[TestMethod("FancyZonesEditor.Basic.CopyCustomLayout_FromContextMenu")]
[TestCategory("FancyZones Editor #4")]
[TestMethod]
public void CopyCustomLayout_FromContextMenu()
{
string copiedLayoutName = CustomLayouts.CustomLayouts[0].Name + " (1)";
@@ -276,8 +276,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == copiedLayoutName));
}
[TestMethod("FancyZonesEditor.Basic.CopyCustomLayout_DefaultLayout")]
[TestCategory("FancyZones Editor #13")]
[TestMethod]
public void CopyCustomLayout_DefaultLayout()
{
string copiedLayoutName = CustomLayouts.CustomLayouts[0].Name + " (1)";
@@ -309,8 +308,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(defaultLayouts.Serialize(DefaultLayouts), defaultLayouts.Serialize(defaultLayoutData));
}
[TestMethod("FancyZonesEditor.Basic.CopyCustomLayout_Hotkey")]
[TestCategory("FancyZones Editor #4")]
[TestMethod]
public void CopyCustomLayout_Hotkey()
{
string copiedLayoutName = CustomLayouts.CustomLayouts[0].Name + " (1)";

View File

@@ -16,15 +16,13 @@ namespace Microsoft.FancyZonesEditor.UITests
public class CreateLayoutTests : UITestBase
{
public CreateLayoutTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
// prepare test editor parameters with 2 monitors before launching the editor
EditorParameters editorParameters = new EditorParameters();
EditorParameters.ParamsWrapper parameters = new EditorParameters.ParamsWrapper
@@ -134,6 +132,12 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void CreateWithDefaultName()
{
@@ -152,8 +156,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == name));
}
[TestMethod("FancyZonesEditor.Basic.CreateWithCustomName")]
[TestCategory("FancyZones Editor #3")]
[TestMethod]
public void CreateWithCustomName()
{
string name = "Layout Name";
@@ -174,8 +177,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == name));
}
[TestMethod("FancyZonesEditor.Basic.CreateGrid")]
[TestCategory("FancyZones Editor #3")]
[TestMethod]
public void CreateGrid()
{
CustomLayout type = CustomLayout.Grid;
@@ -191,8 +193,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Type == type.TypeToString()));
}
[TestMethod("FancyZonesEditor.Basic.CreateCanvas")]
[TestCategory("FancyZones Editor #3")]
[TestMethod]
public void CreateCanvas()
{
CustomLayout type = CustomLayout.Canvas;
@@ -208,8 +209,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Type == type.TypeToString()));
}
[TestMethod("FancyZonesEditor.Basic.CancelGridCreation")]
[TestCategory("FancyZones Editor #3")]
[TestMethod]
public void CancelGridCreation()
{
Session.Find<Element>(By.AccessibilityId(AccessibilityId.NewLayoutButton)).Click();
@@ -223,8 +223,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(0, data.CustomLayouts.Count);
}
[TestMethod("FancyZonesEditor.Basic.CancelCanvasCreation")]
[TestCategory("FancyZones Editor #3")]
[TestMethod]
public void CancelCanvasCreation()
{
Session.Find<Element>(By.AccessibilityId(AccessibilityId.NewLayoutButton)).Click();

View File

@@ -20,7 +20,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class CustomLayoutsTests : UITestBase
{
public CustomLayoutsTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -104,7 +104,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
CustomLayouts customLayouts = new CustomLayouts();
FancyZonesEditorHelper.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(Layouts));
@@ -209,6 +208,12 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void Name_Initialize()
{
@@ -287,14 +292,14 @@ namespace Microsoft.FancyZonesEditor.UITests
var type = layout.Type;
Session.Find<Element>(layout.Name).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
var value = type == CustomLayout.Canvas.TypeToString() ?
new CustomLayouts().CanvasFromJsonElement(layout.Info.GetRawText()).SensitivityRadius :
new CustomLayouts().GridFromJsonElement(layout.Info.GetRawText()).SensitivityRadius;
var expected = value; // if have one step right please + 1
var expected = value + 1; // one step right
Assert.AreEqual($"{expected}", slider.Text);
@@ -316,9 +321,9 @@ namespace Microsoft.FancyZonesEditor.UITests
var type = layout.Type;
Session.Find<Element>(layout.Name).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
var expected = type == CustomLayout.Canvas.TypeToString() ?
new CustomLayouts().CanvasFromJsonElement(layout.Info.GetRawText()).SensitivityRadius :
@@ -368,15 +373,15 @@ namespace Microsoft.FancyZonesEditor.UITests
public void SpaceAroundZones_Slider_Save()
{
var layout = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Grid.TypeToString() && new CustomLayouts().GridFromJsonElement(x.Info.GetRawText()).ShowSpacing);
var expected = new CustomLayouts().GridFromJsonElement(layout.Info.GetRawText()).Spacing; // one step right
var expected = new CustomLayouts().GridFromJsonElement(layout.Info.GetRawText()).Spacing + 1; // one step right
Session.Find<Element>(layout.Name).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
Assert.AreEqual($"{expected}", slider.Text);
Session.Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.PrimaryButton)).Click();
Session.Find<Button>(ElementName.Save).Click();
// verify the file
var customLayouts = new CustomLayouts();
@@ -392,9 +397,9 @@ namespace Microsoft.FancyZonesEditor.UITests
Session.Find<Element>(layout.Name).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
var expected = new CustomLayouts().GridFromJsonElement(layout.Info.GetRawText()).Spacing;
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
Session.Find<Button>(ElementName.Cancel).Click();
// verify the file

View File

@@ -20,7 +20,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class DefaultLayoutsTest : UITestBase
{
public DefaultLayoutsTest()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -121,7 +121,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
var defaultLayouts = new DefaultLayouts();
FancyZonesEditorHelper.Files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(Layouts));
@@ -238,16 +237,20 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestMethod("FancyZonesEditor.Basic.Default_Initialize")]
[TestCategory("FancyZones Editor #12")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void Initialize()
{
CheckTemplateLayouts(LayoutType.Grid, null);
CheckCustomLayouts(string.Empty, CustomLayouts.CustomLayouts[0].Uuid);
}
[TestMethod("FancyZonesEditor.Basic.Default_Assign_Cancel")]
[TestCategory("FancyZones Editor #12")]
[TestMethod]
public void Assign_Cancel()
{
// assign Focus as a default horizontal and vertical layout
@@ -263,8 +266,7 @@ namespace Microsoft.FancyZonesEditor.UITests
CheckCustomLayouts(string.Empty, CustomLayouts.CustomLayouts[0].Uuid);
}
[TestMethod("FancyZonesEditor.Basic.Default_Assign_Save")]
[TestCategory("FancyZones Editor #12")]
[TestMethod]
public void Assign_Save()
{
// assign Focus as a default horizontal and vertical layout

View File

@@ -22,7 +22,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class DeleteLayoutTests : UITestBase
{
public DeleteLayoutTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -142,7 +142,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
FancyZonesEditorHelper.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(Parameters));
@@ -216,14 +215,19 @@ namespace Microsoft.FancyZonesEditor.UITests
Session.Find<Element>(CustomLayouts.CustomLayouts[0].Name).Click();
}
[TestMethod("FancyZonesEditor.Basic.DeleteNotAppliedLayout")]
[TestCategory("FancyZones Editor #5")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void DeleteNotAppliedLayout()
{
var deletedLayout = CustomLayouts.CustomLayouts[1].Name;
Session.Find<Element>(deletedLayout).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
Session.Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.DeleteLayoutButton)).Click();
Session.SendKeySequence(Key.Tab, Key.Enter);
Session.KeyboardAction(Keys.Tab, Keys.Enter);
// verify the layout is removed
Assert.IsTrue(Session.FindAll<Element>(deletedLayout).Count == 0);
@@ -235,15 +239,13 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsFalse(data.CustomLayouts.Exists(x => x.Name == deletedLayout));
}
[TestMethod("FancyZonesEditor.Basic.DeleteAppliedLayout")]
[TestCategory("FancyZones Editor #5")]
[TestMethod]
public void DeleteAppliedLayout()
{
var deletedLayout = CustomLayouts.CustomLayouts[0].Name;
Session.Find<Element>(deletedLayout).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
Session.Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.DeleteLayoutButton)).Click();
Session.SendKeySequence(Key.Tab, Key.Enter);
Session.KeyboardAction(Keys.Tab, Keys.Enter);
// verify the layout is removed
Assert.IsTrue(Session.FindAll<Element>(deletedLayout).Count == 0);
@@ -262,14 +264,13 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(LayoutType.Blank.TypeToString(), appliedLayoutsData.AppliedLayouts.Find(x => x.Device.Monitor == Parameters.Monitors[0].Monitor).AppliedLayout.Type);
}
[TestMethod("FancyZonesEditor.Basic.CancelDeletion")]
[TestCategory("FancyZones Editor #5")]
[TestMethod]
public void CancelDeletion()
{
var deletedLayout = CustomLayouts.CustomLayouts[1].Name;
Session.Find<Element>(deletedLayout).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
Session.Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.DeleteLayoutButton)).Click();
Session.SendKeySequence(Key.Tab, Key.Tab, Key.Enter);
Session.KeyboardAction(Keys.Tab, Keys.Tab, Keys.Enter);
// verify the layout is not removed
Assert.IsNotNull(Session.Find<Element>(deletedLayout));
@@ -281,13 +282,12 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(data.CustomLayouts.Exists(x => x.Name == deletedLayout));
}
[TestMethod("FancyZonesEditor.Basic.DeleteFromContextMenu")]
[TestCategory("FancyZones Editor #5")]
[TestMethod]
public void DeleteFromContextMenu()
{
var deletedLayout = CustomLayouts.CustomLayouts[1].Name;
FancyZonesEditorHelper.ClickContextMenuItem(Session, deletedLayout, FancyZonesEditorHelper.ElementName.Delete);
Session.SendKeySequence(Key.Tab, Key.Enter);
Session.KeyboardAction(Keys.Tab, Keys.Enter);
// verify the layout is removed
Assert.IsTrue(Session.FindAll<Element>(deletedLayout).Count == 0);
@@ -299,13 +299,12 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsFalse(data.CustomLayouts.Exists(x => x.Name == deletedLayout));
}
[TestMethod("FancyZonesEditor.Basic.DeleteDefaultLayout")]
[TestCategory("FancyZones Editor #5")]
[TestMethod]
public void DeleteDefaultLayout()
{
var deletedLayout = CustomLayouts.CustomLayouts[1].Name;
FancyZonesEditorHelper.ClickContextMenuItem(Session, deletedLayout, FancyZonesEditorHelper.ElementName.Delete);
Session.SendKeySequence(Key.Tab, Key.Enter);
Session.KeyboardAction(Keys.Tab, Keys.Enter);
// verify the default layout is reset to the "default" default
Session.Find<Element>(TestConstants.TemplateLayoutNames[LayoutType.PriorityGrid]).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
@@ -319,13 +318,12 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(LayoutType.PriorityGrid.TypeToString(), data.DefaultLayouts.Find(x => x.MonitorConfiguration == configuration).Layout.Type);
}
[TestMethod("FancyZonesEditor.Basic.DeleteLayoutWithHotkey")]
[TestCategory("FancyZones Editor #5")]
[TestMethod]
public void DeleteLayoutWithHotkey()
{
var deletedLayout = CustomLayouts.CustomLayouts[1].Name;
FancyZonesEditorHelper.ClickContextMenuItem(Session, deletedLayout, FancyZonesEditorHelper.ElementName.Delete);
Session.SendKeySequence(Key.Tab, Key.Enter);
Session.KeyboardAction(Keys.Tab, Keys.Enter);
// verify the hotkey is available
Session.Find<Element>(CustomLayouts.CustomLayouts[0].Name).Find<Button>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();

View File

@@ -21,7 +21,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class EditLayoutTests : UITestBase
{
public EditLayoutTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -105,7 +105,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
@@ -210,8 +209,13 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestMethod("FancyZonesEditor.Basic.OpenEditMode")]
[TestCategory("FancyZones Editor #7")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void OpenEditMode()
{
Session.Find<Element>(Layouts.CustomLayouts[0].Name).Find<Button>(By.AccessibilityId(AccessibilityId.EditLayoutButton)).Click();
@@ -220,8 +224,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Session.Find<Button>(ElementName.Cancel).Click();
}
[TestMethod("FancyZonesEditor.Basic.OpenEditModeFromContextMenu")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void OpenEditModeFromContextMenu()
{
FancyZonesEditorHelper.ClickContextMenuItem(Session, Layouts.CustomLayouts[0].Name, FancyZonesEditorHelper.ElementName.EditZones);
@@ -229,8 +232,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Session.Find<Button>(ElementName.Cancel).Click();
}
[TestMethod("FancyZonesEditor.Basic.Canvas_AddZone_Save")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_AddZone_Save()
{
var canvas = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Canvas.TypeToString());
@@ -246,8 +248,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected.Zones.Count + 1, actual.Zones.Count);
}
[TestMethod("FancyZonesEditor.Basic.Canvas_AddZone_Cancel")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_AddZone_Cancel()
{
var canvas = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Canvas.TypeToString());
@@ -263,8 +264,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected.Zones.Count, actual.Zones.Count);
}
[TestMethod("FancyZonesEditor.Basic.Canvas_DeleteZone_Save")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_DeleteZone_Save()
{
var canvas = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Canvas.TypeToString());
@@ -280,8 +280,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected.Zones.Count - 1, actual.Zones.Count);
}
[TestMethod("FancyZonesEditor.Basic.Canvas_DeleteZone_Cancel")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_DeleteZone_Cancel()
{
var canvas = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Canvas.TypeToString());
@@ -297,8 +296,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected.Zones.Count, actual.Zones.Count);
}
[TestMethod("FancyZonesEditor.Basic.Canvas_MoveZone_Save")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_MoveZone_Save()
{
int zoneNumber = 1;
@@ -335,8 +333,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Canvas_MoveZone_Cancel")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_MoveZone_Cancel()
{
int zoneNumber = 1;
@@ -360,8 +357,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Canvas_ResizeZone_Save")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_ResizeZone_Save()
{
int zoneNumber = 1;
@@ -370,7 +366,7 @@ namespace Microsoft.FancyZonesEditor.UITests
var canvas = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Canvas.TypeToString());
FancyZonesEditorHelper.ClickContextMenuItem(Session, canvas.Name, FancyZonesEditorHelper.ElementName.EditZones);
FancyZonesEditorHelper.GetZone(Session, zoneNumber, FancyZonesEditorHelper.ClassName.CanvasZone)?.Find<Thumb>(By.AccessibilityId(FancyZonesEditorHelper.AccessibilityId.TopRightCorner)).Drag(xOffset, yOffset);
FancyZonesEditorHelper.GetZone(Session, zoneNumber, FancyZonesEditorHelper.ClassName.CanvasZone)?.Find<Element>(By.AccessibilityId(FancyZonesEditorHelper.AccessibilityId.TopRightCorner)).Drag(xOffset, yOffset);
Session.Find<Button>(ElementName.Save).Click();
// check the file
@@ -398,8 +394,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Canvas_ResizeZone_Cancel")]
[TestCategory("FancyZones Editor #7")]
[TestMethod]
public void Canvas_ResizeZone_Cancel()
{
int zoneNumber = 1;
@@ -408,7 +403,7 @@ namespace Microsoft.FancyZonesEditor.UITests
var canvas = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Canvas.TypeToString());
FancyZonesEditorHelper.ClickContextMenuItem(Session, canvas.Name, FancyZonesEditorHelper.ElementName.EditZones);
FancyZonesEditorHelper.GetZone(Session, zoneNumber, FancyZonesEditorHelper.ClassName.CanvasZone)?.Find<Thumb>(By.AccessibilityId(FancyZonesEditorHelper.AccessibilityId.TopRightCorner)).Drag(xOffset, yOffset);
FancyZonesEditorHelper.GetZone(Session, zoneNumber, FancyZonesEditorHelper.ClassName.CanvasZone)?.Find<Element>(By.AccessibilityId(FancyZonesEditorHelper.AccessibilityId.TopRightCorner)).Drag(xOffset, yOffset);
Session.Find<Button>(ElementName.Cancel).Click();
// check the file
@@ -426,8 +421,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Grid_SplitZone_Save")]
[TestCategory("FancyZones Editor #8")]
[TestMethod]
public void Grid_SplitZone_Save()
{
int zoneNumber = 1;
@@ -456,8 +450,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Grid_SplitZone_Cancel")]
[TestCategory("FancyZones Editor #8")]
[TestMethod]
public void Grid_SplitZone_Cancel()
{
int zoneNumber = 1;
@@ -488,8 +481,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Grid_MergeZones_Save")]
[TestCategory("FancyZones Editor #8")]
[TestMethod]
public void Grid_MergeZones_Save()
{
var grid = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Grid.TypeToString());
@@ -523,8 +515,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.IsTrue(actual.CellChildMap[1].SequenceEqual([1, 2]));
}
[TestMethod("FancyZonesEditor.Basic.Grid_MergeZones_Cancel")]
[TestCategory("FancyZones Editor #8")]
[TestMethod]
public void Grid_MergeZones_Cancel()
{
var grid = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Grid.TypeToString());
@@ -560,8 +551,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Grid_MoveSplitter_Save")]
[TestCategory("FancyZones Editor #8")]
[TestMethod]
public void Grid_MoveSplitter_Save()
{
EditorParameters editorParameters = new EditorParameters();
@@ -624,8 +614,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.Grid_MoveSplitter_Cancel")]
[TestCategory("FancyZones Editor #8")]
[TestMethod]
public void Grid_MoveSplitter_Cancel()
{
var grid = Layouts.CustomLayouts.Find(x => x.Type == CustomLayout.Grid.TypeToString() && x.Name == "Grid-9");

View File

@@ -25,14 +25,13 @@ namespace Microsoft.FancyZonesEditor.UITests
public class FirstLunchTest : UITestBase
{
public FirstLunchTest()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
@@ -142,6 +141,12 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void FirstLaunch()
{

View File

@@ -23,7 +23,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class LayoutHotkeysTests : UITestBase
{
public LayoutHotkeysTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
@@ -106,7 +106,6 @@ namespace Microsoft.FancyZonesEditor.UITests
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
@@ -207,8 +206,13 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestMethod("FancyZonesEditor.Basic.HotKey_Initialize")]
[TestCategory("FancyZones Editor #11")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void Initialize()
{
foreach (var layout in CustomLayouts.CustomLayouts)
@@ -252,8 +256,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.HotKey_Assign_Save")]
[TestCategory("FancyZones Editor #11")]
[TestMethod]
public void Assign_Save()
{
var layout = CustomLayouts.CustomLayouts[2]; // a layout without assigned hotkey
@@ -296,8 +299,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.HotKey_Assign_Cancel")]
[TestCategory("FancyZones Editor #11")]
[TestMethod]
public void Assign_Cancel()
{
var layout = CustomLayouts.CustomLayouts[2]; // a layout without assigned hotkey
@@ -336,8 +338,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.HotKey_Assign_AllPossibleValues")]
[TestCategory("FancyZones Editor #11")]
[TestMethod]
public void Assign_AllPossibleValues()
{
for (int i = 0; i < 4; i++)
@@ -383,8 +384,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.HotKey_Reset_Save")]
[TestCategory("FancyZones Editor #11")]
[TestMethod]
public void Reset_Save()
{
var layout = CustomLayouts.CustomLayouts[0]; // a layout with assigned hotkey
@@ -424,8 +424,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.HotKey_Reset_Cancel")]
[TestCategory("FancyZones Editor #11")]
[TestMethod]
public void Reset_Cancel()
{
var layout = CustomLayouts.CustomLayouts[0]; // a layout with assigned hotkey

View File

@@ -26,7 +26,7 @@ namespace Microsoft.FancyZonesEditor.UITests
public class TestCaseFirstLaunch : UITestBase
{
public TestCaseFirstLaunch()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}

View File

@@ -18,12 +18,12 @@ namespace Microsoft.FancyZonesEditor.UITests
public class RunFancyZonesEditorTest : UITestBase
{
public RunFancyZonesEditorTest()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
[TestInitialize]
public void TestInitialize()
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
FancyZonesEditorHelper.Files.Restore();
@@ -163,8 +163,12 @@ namespace Microsoft.FancyZonesEditor.UITests
AppliedLayouts = new List<AppliedLayouts.AppliedLayoutWrapper> { },
};
FancyZonesEditorHelper.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
}
this.RestartScopeExe();
[ClassCleanup]
public static void ClassCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]

View File

@@ -73,14 +73,13 @@ namespace Microsoft.FancyZonesEditor.UITests
};
public TemplateLayoutsTests()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
[TestInitialize]
public void TestInitialize()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{
@@ -192,8 +191,13 @@ namespace Microsoft.FancyZonesEditor.UITests
this.RestartScopeExe();
}
[TestMethod("FancyZonesEditor.Basic.ZoneNumber_Cancel")]
[TestCategory("FancyZones Editor #6")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void ZoneNumber_Cancel()
{
var type = LayoutType.Rows;
@@ -201,9 +205,9 @@ namespace Microsoft.FancyZonesEditor.UITests
var expected = layout.ZoneCount;
Session.Find<Button>(TestConstants.TemplateLayoutNames[type]).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.TemplateZoneSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Left);
slider.SendKeys(Keys.Left);
Session.Find<Button>(ElementName.Cancel).Click();
@@ -214,8 +218,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected, actual);
}
[TestMethod("FancyZonesEditor.Basic.HighlightDistance_Initialize")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void HighlightDistance_Initialize()
{
foreach (var (type, name) in TestConstants.TemplateLayoutNames)
@@ -236,8 +239,7 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.HighlightDistance_Save")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void HighlightDistance_Save()
{
var type = LayoutType.Focus;
@@ -245,11 +247,11 @@ namespace Microsoft.FancyZonesEditor.UITests
var value = layout.SensitivityRadius;
Session.Find<Button>(TestConstants.TemplateLayoutNames[type]).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
var expected = value; // one step right
var expected = value + 1; // one step right
Assert.AreEqual($"{expected}", slider.Text);
Session.Find<Button>(ElementName.Save).Click();
@@ -261,8 +263,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected, actual);
}
[TestMethod("FancyZonesEditor.Basic.HighlightDistance_Cancel")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void HighlightDistance_Cancel()
{
var type = LayoutType.Focus;
@@ -270,9 +271,9 @@ namespace Microsoft.FancyZonesEditor.UITests
var expected = layout.SensitivityRadius;
Session.Find<Button>(TestConstants.TemplateLayoutNames[type]).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SensitivitySlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
Session.Find<Button>(ElementName.Cancel).Click();
// verify the file
@@ -282,8 +283,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected, actual);
}
[TestMethod("FancyZonesEditor.Basic.SpaceAroundZones_Initialize")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void SpaceAroundZones_Initialize()
{
foreach (var (type, name) in TestConstants.TemplateLayoutNames)
@@ -309,18 +309,17 @@ namespace Microsoft.FancyZonesEditor.UITests
}
}
[TestMethod("FancyZonesEditor.Basic.SpaceAroundZones_Slider_Save")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void SpaceAroundZones_Slider_Save()
{
var type = LayoutType.PriorityGrid;
var layout = Layouts.LayoutTemplates.Find(x => x.Type == type.TypeToString());
var expected = layout.Spacing;
var expected = layout.Spacing + 1;
Session.Find<Button>(TestConstants.TemplateLayoutNames[type]).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
slider.SendKeys(Keys.Right);
Assert.AreEqual($"{expected}", slider.Text);
Session.Find<Button>(ElementName.Save).Click();
@@ -332,8 +331,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected, actual);
}
[TestMethod("FancyZonesEditor.Basic.SpaceAroundZones_Slider_Cancel")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void SpaceAroundZones_Slider_Cancel()
{
var type = LayoutType.PriorityGrid;
@@ -341,10 +339,10 @@ namespace Microsoft.FancyZonesEditor.UITests
var expected = layout.Spacing;
Session.Find<Button>(TestConstants.TemplateLayoutNames[type]).Click();
var slider = Session.Find<Custom>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
var slider = Session.Find<Element>(PowerToys.UITest.By.AccessibilityId(AccessibilityId.SpacingSlider));
Assert.IsNotNull(slider);
slider.SendKeys(Key.Right);
Assert.AreEqual($"{expected}", slider.Text);
slider.SendKeys(Keys.Right);
Assert.AreEqual($"{expected + 1}", slider.Text);
Session.Find<Button>(ElementName.Cancel).Click();
@@ -355,8 +353,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected, actual);
}
[TestMethod("FancyZonesEditor.Basic.SpaceAroundZones_Toggle_Save")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void SpaceAroundZones_Toggle_Save()
{
var type = LayoutType.PriorityGrid;
@@ -379,8 +376,7 @@ namespace Microsoft.FancyZonesEditor.UITests
Assert.AreEqual(expected, actual);
}
[TestMethod("FancyZonesEditor.Basic.SpaceAroundZones_Toggle_Cancel")]
[TestCategory("FancyZones Editor #6")]
[TestMethod]
public void SpaceAroundZones_Toggle_Cancel()
{
var type = LayoutType.PriorityGrid;

View File

@@ -23,12 +23,17 @@ namespace Microsoft.FancyZonesEditor.UITests
public class UIInitializeTest : UITestBase
{
public UIInitializeTest()
: base(PowerToysModule.FancyZone, WindowSize.UnSpecified)
: base(PowerToysModule.FancyZone)
{
}
[TestMethod("FancyZonesEditor.Basic.EditorParams_VerifySelectedMonitor")]
[TestCategory("FancyZones Editor #10")]
[TestCleanup]
public void TestCleanup()
{
FancyZonesEditorHelper.Files.Restore();
}
[TestMethod]
public void EditorParams_VerifySelectedMonitor()
{
InitFileData();
@@ -732,7 +737,6 @@ namespace Microsoft.FancyZonesEditor.UITests
private void InitFileData()
{
FancyZonesEditorHelper.Files.Restore();
EditorParameters editorParameters = new EditorParameters();
ParamsWrapper parameters = new ParamsWrapper
{

View File

@@ -1,78 +0,0 @@
// 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 System.Text.Json;
using static FancyZonesEditorCommon.Data.AppZoneHistory.AppZoneHistoryWrapper;
using static FancyZonesEditorCommon.Data.CustomLayouts;
namespace FancyZonesEditorCommon.Data
{
public class AppZoneHistory : EditorData<AppZoneHistory.AppZoneHistoryListWrapper>
{
public string File
{
get
{
return GetDataFolder() + "\\Microsoft\\PowerToys\\FancyZones\\app-zone-history.json";
}
}
public struct AppZoneHistoryWrapper
{
public struct ZoneHistoryWrapper
{
public int[][] ZoneIndexSet { get; set; }
public DeviceIdWrapper Device { get; set; }
public string ZonesetUuid { get; set; }
}
public struct DeviceIdWrapper
{
public string Monitor { get; set; }
public string MonitorInstance { get; set; }
public int MonitorNumber { get; set; }
public string SerialNumber { get; set; }
public string VirtualDesktop { get; set; }
}
public string AppPath { get; set; }
public List<ZoneHistoryWrapper> History { get; set; }
}
public struct AppZoneHistoryListWrapper
{
public List<AppZoneHistoryWrapper> AppZoneHistory { get; set; }
}
public JsonElement ToJsonElement(ZoneHistoryWrapper info)
{
string json = JsonSerializer.Serialize(info, this.JsonOptions);
return JsonSerializer.Deserialize<JsonElement>(json);
}
public JsonElement ToJsonElement(DeviceIdWrapper info)
{
string json = JsonSerializer.Serialize(info, this.JsonOptions);
return JsonSerializer.Deserialize<JsonElement>(json);
}
public ZoneHistoryWrapper ZoneHistoryFromJsonElement(string json)
{
return JsonSerializer.Deserialize<ZoneHistoryWrapper>(json, this.JsonOptions);
}
public DeviceIdWrapper GridFromJsonElement(string json)
{
return JsonSerializer.Deserialize<DeviceIdWrapper>(json, this.JsonOptions);
}
}
}

Some files were not shown because too many files have changed in this diff Show More