Compare commits

...

15 Commits

Author SHA1 Message Date
Leilei Zhang
f8934769c9 remove all 2025-07-21 15:50:37 +08:00
Leilei Zhang
6d80dce74c remove base line image 2025-07-21 15:27:04 +08:00
Leilei Zhang
393fb15019 pdf need more time to load 2025-07-21 14:38:29 +08:00
Leilei Zhang
1d0cee997b use hot key clean screnn 2025-07-21 13:09:54 +08:00
Leilei Zhang
28d585fef6 don't close explorer 2025-07-21 12:44:33 +08:00
Leilei Zhang
65e67f4098 add diagnosticsEnabled 2025-07-21 11:26:02 +08:00
Leilei Zhang
68f474ef64 update project 2025-07-21 10:18:35 +08:00
Leilei Zhang
f1db717b6d update baseline image name 2025-07-21 10:05:52 +08:00
Leilei Zhang
d90c1954b0 update close explorer logic 2025-07-18 18:43:31 +08:00
Leilei Zhang
b0d7bfef7c Merge branch 'main' of https://github.com/microsoft/PowerToys into leilzh/peekuitests 2025-07-18 16:59:45 +08:00
Leilei Zhang
c8cea4a017 exit cmdpal 2025-07-18 16:57:00 +08:00
Yu Leng
ca473b488b [CmdPal][UI Tests] Add basic test cases for cmdpal (#40694)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. Create some basic cmdpal test cases.
2. Add ui tests support for cmdpal modules.


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #40695
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-07-18 16:22:37 +08:00
Leilei Zhang
a16813b73b fix error 2025-07-18 15:50:03 +08:00
Leilei Zhang
1fbb784bea add win10 explorer find 2025-07-18 15:19:41 +08:00
Leilei Zhang
421a7a62d5 update logic 2025-07-18 14:27:00 +08:00
28 changed files with 399 additions and 123 deletions

View File

@@ -742,6 +742,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{02EA681E-C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Core.ViewModels", "src\modules\cmdpal\Microsoft.CmdPal.Core.ViewModels\Microsoft.CmdPal.Core.ViewModels.csproj", "{24133F7F-C1D1-DE04-EFA8-F5D5467FE027}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.UITests", "src\modules\cmdpal\Microsoft.CmdPal.UITests\Microsoft.CmdPal.UITests.csproj", "{840455DF-5634-51BB-D937-9D7D32F0B0C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2748,6 +2750,14 @@ Global
{BCDC7246-F4F8-4EED-8DE6-037AA2E7C6D1}.Release|ARM64.Build.0 = Release|ARM64
{BCDC7246-F4F8-4EED-8DE6-037AA2E7C6D1}.Release|x64.ActiveCfg = Release|x64
{BCDC7246-F4F8-4EED-8DE6-037AA2E7C6D1}.Release|x64.Build.0 = Release|x64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Debug|ARM64.Build.0 = Debug|ARM64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Debug|x64.ActiveCfg = Debug|x64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Debug|x64.Build.0 = Debug|x64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Release|ARM64.ActiveCfg = Release|ARM64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Release|ARM64.Build.0 = Release|ARM64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Release|x64.ActiveCfg = Release|x64
{840455DF-5634-51BB-D937-9D7D32F0B0C2}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3036,6 +3046,7 @@ Global
{24133F7F-C1D1-DE04-EFA8-F5D5467FE027} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{9D3F3793-EFE3-4525-8782-238015DABA62} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
{BCDC7246-F4F8-4EED-8DE6-037AA2E7C6D1} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20}
{840455DF-5634-51BB-D937-9D7D32F0B0C2} = {7520A2FE-00A2-49B8-83ED-DB216E874C04}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -32,6 +32,7 @@ namespace Microsoft.PowerToys.UITest
Runner,
Workspaces,
PowerRename,
CommandPalette,
}
/// <summary>
@@ -104,6 +105,7 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.Runner] = new ModuleInfo("PowerToys.exe", "PowerToys"),
[PowerToysModule.Workspaces] = new ModuleInfo("PowerToys.WorkspacesEditor.exe", "Workspaces Editor"),
[PowerToysModule.PowerRename] = new ModuleInfo("PowerToys.PowerRename.exe", "PowerRename", "WinUI3Apps"),
[PowerToysModule.CommandPalette] = new ModuleInfo("Microsoft.CmdPal.UI.exe", "PowerToys Command Palette", "WinUI3Apps\\CmdPal"),
};
}

View File

@@ -176,6 +176,9 @@ namespace Microsoft.PowerToys.UITest
runner = Process.Start(runnerProcessInfo);
Thread.Sleep(5000);
// Exit CmdPal UI before launching new process if use installer for test
ExitExeByName("Microsoft.CmdPal.UI");
if (root != null)
{
const int maxRetries = 5;
@@ -204,9 +207,6 @@ namespace Microsoft.PowerToys.UITest
}
}
}
// Exit CmdPal UI before launching new process if use installer for test
ExitExeByName("Microsoft.CmdPal.UI");
}
/// <summary>

View File

@@ -0,0 +1,151 @@
// 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 Microsoft.CmdPal.UITests;
[TestClass]
public class BasicTests : UITestBase
{
public BasicTests()
: base(PowerToysModule.CommandPalette)
{
}
private void SetSearchBox(string text)
{
Assert.AreEqual(this.Find<TextBox>("Type here to search...").SetText(text, true).Text, text);
}
private void SetFilesExtensionSearchBox(string text)
{
Assert.AreEqual(this.Find<TextBox>("Search for files and folders...").SetText(text, true).Text, text);
}
private void SetCalculatorExtensionSearchBox(string text)
{
Assert.AreEqual(this.Find<TextBox>("Type an equation...").SetText(text, true).Text, text);
}
private void SetTimeAndDaterExtensionSearchBox(string text)
{
Assert.AreEqual(this.Find<TextBox>("Search values or type a custom time stamp...").SetText(text, true).Text, text);
}
[TestMethod]
public void BasicFileSearchTest()
{
SetSearchBox("files");
var searchFileItem = this.Find<NavigationViewItem>("Search files");
Assert.AreEqual(searchFileItem.Name, "Search files");
searchFileItem.DoubleClick();
SetFilesExtensionSearchBox("AppData");
Assert.IsNotNull(this.Find<NavigationViewItem>("AppData"));
}
[TestMethod]
public void BasicCalculatorTest()
{
SetSearchBox("calculator");
var searchFileItem = this.Find<NavigationViewItem>("Calculator");
Assert.AreEqual(searchFileItem.Name, "Calculator");
searchFileItem.DoubleClick();
SetCalculatorExtensionSearchBox("1+2");
Assert.IsNotNull(this.Find<NavigationViewItem>("3"));
}
[TestMethod]
public void BasicTimeAndDateTest()
{
SetSearchBox("time and date");
var searchFileItem = this.Find<NavigationViewItem>("Time and Date");
Assert.AreEqual(searchFileItem.Name, "Time and Date");
searchFileItem.DoubleClick();
SetTimeAndDaterExtensionSearchBox("year");
Assert.IsNotNull(this.Find<NavigationViewItem>("2025"));
}
[TestMethod]
public void BasicWindowsTerminalTest()
{
SetSearchBox("Windows Terminal");
var searchFileItem = this.Find<NavigationViewItem>("Open Windows Terminal Profiles");
Assert.AreEqual(searchFileItem.Name, "Open Windows Terminal Profiles");
searchFileItem.DoubleClick();
SetSearchBox("PowerShell");
Assert.IsNotNull(this.Find<NavigationViewItem>("PowerShell"));
}
[TestMethod]
public void BasicWindowsSettingsTest()
{
SetSearchBox("Windows Settings");
var searchFileItem = this.Find<NavigationViewItem>("Windows Settings");
Assert.AreEqual(searchFileItem.Name, "Windows Settings");
searchFileItem.DoubleClick();
SetSearchBox("power");
Assert.IsNotNull(this.Find<NavigationViewItem>("Power and sleep"));
}
[TestMethod]
public void BasicRegistryTest()
{
SetSearchBox("Registry");
var searchFileItem = this.Find<NavigationViewItem>("Registry");
Assert.AreEqual(searchFileItem.Name, "Registry");
searchFileItem.DoubleClick();
SetSearchBox("HKEY_LOCAL_MACHINE");
Assert.IsNotNull(this.Find<NavigationViewItem>("HKEY_LOCAL_MACHINE\\SECURITY"));
}
[TestMethod]
public void BasicWindowsServicesTest()
{
SetSearchBox("Windows Services");
var searchFileItem = this.Find<NavigationViewItem>("Windows Services");
Assert.AreEqual(searchFileItem.Name, "Windows Services");
searchFileItem.DoubleClick();
SetSearchBox("hyper-v");
Assert.IsNotNull(this.Find<NavigationViewItem>("Hyper-V Heartbeat Service"));
}
[TestMethod]
public void BasicWindowsSystemCommandsTest()
{
SetSearchBox("Windows System Commands");
var searchFileItem = this.Find<NavigationViewItem>("Windows System Commands");
Assert.AreEqual(searchFileItem.Name, "Windows System Commands");
searchFileItem.DoubleClick();
SetSearchBox("Sleep");
Assert.IsNotNull(this.Find<NavigationViewItem>("Sleep"));
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<RootNamespace>Microsoft.CmdPal.UITests</RootNamespace>
<AssemblyName>Microsoft.CmdPal.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\Microsoft.CmdPal.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<PackageReference Include="System.Net.Http" />
<PackageReference Include="System.Private.Uri" />
<PackageReference Include="System.Text.RegularExpressions" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>

View File

@@ -15,30 +15,6 @@
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_2_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_3_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_4_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_5_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_6_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_7_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_8_arm64.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_2_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_3_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_4_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_5_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_6_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_7_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_8_x64Win11.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_2_x64Win10.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_3_x64Win10.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_4_x64Win10.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_5_x64Win10.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_6_x64Win10.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_7_x64Win10.png" />
<EmbeddedResource Include="Baseline\PeekFilePreviewTests_TestFilePreviewWithVisualComparison_8_x64Win10.png" />
</ItemGroup>
<ItemGroup>
<Content Include="TestAssets\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@@ -3,10 +3,13 @@
// 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.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -36,7 +39,7 @@ public class PeekFilePreviewTests : UITestBase
public void TestInitialize()
{
Session.CloseMainWindow();
SendKeys(Key.Win, Key.D);
SendKeys(Key.Win, Key.M);
}
[TestMethod("Peek.FilePreview.Folder")]
@@ -55,28 +58,80 @@ public class PeekFilePreviewTests : UITestBase
}
/// <summary>
/// Comprehensive test for all files in TestAssets with visual comparison
/// Tests all supported file types and validates preview rendering with image comparison
/// Test JPEG image preview
/// </summary>
[TestMethod("Peek.FilePreview.AllTestAssets")]
[TestMethod("Peek.FilePreview.JPEGImage")]
[TestCategory("Preview files")]
public void PeekAllTestAssets()
public void PeekJPEGImagePreview()
{
// Get all test asset files
var testFiles = GetTestAssetFiles();
string imagePath = Path.GetFullPath(@".\TestAssets\2.jpg");
TestSingleFilePreview(imagePath, "2");
}
Assert.IsTrue(testFiles.Count > 0, "Should have test files in TestAssets folder");
/// <summary>
/// Test PDF document preview
/// </summary>
[TestMethod("Peek.FilePreview.PDFDocument")]
[TestCategory("Preview files")]
public void PeekPDFDocumentPreview()
{
string pdfPath = Path.GetFullPath(@".\TestAssets\3.pdf");
TestSingleFilePreview(pdfPath, "3", 10000);
}
// Test each file individually with visual comparison
foreach (var testFile in testFiles)
{
string fileName = Path.GetFileName(testFile);
string fileExtension = Path.GetExtension(testFile).ToLowerInvariant();
/// <summary>
/// Test QOI image preview
/// </summary>
[TestMethod("Peek.FilePreview.QOIImage")]
[TestCategory("Preview files")]
public void PeekQOIImagePreview()
{
string qoiPath = Path.GetFullPath(@".\TestAssets\4.qoi");
TestSingleFilePreview(qoiPath, "4");
}
TestFilePreviewWithVisualComparison(testFile);
}
/// <summary>
/// Test C++ source code preview
/// </summary>
[TestMethod("Peek.FilePreview.CPPSourceCode")]
[TestCategory("Preview files")]
public void PeekCPPSourceCodePreview()
{
string cppPath = Path.GetFullPath(@".\TestAssets\5.cpp");
TestSingleFilePreview(cppPath, "5");
}
// Assert.Fail("All test files should be processed without failure. If this fails, check the TestAssets folder for missing or unsupported files.");
/// <summary>
/// Test Markdown document preview
/// </summary>
[TestMethod("Peek.FilePreview.MarkdownDocument")]
[TestCategory("Preview files")]
public void PeekMarkdownDocumentPreview()
{
string markdownPath = Path.GetFullPath(@".\TestAssets\6.md");
TestSingleFilePreview(markdownPath, "6");
}
/// <summary>
/// Test ZIP archive preview
/// </summary>
[TestMethod("Peek.FilePreview.ZIPArchive")]
[TestCategory("Preview files")]
public void PeekZIPArchivePreview()
{
string zipPath = Path.GetFullPath(@".\TestAssets\7.zip");
TestSingleFilePreview(zipPath, "7");
}
/// <summary>
/// Test PNG image preview
/// </summary>
[TestMethod("Peek.FilePreview.PNGImage")]
[TestCategory("Preview files")]
public void PeekPNGImagePreview()
{
string pngPath = Path.GetFullPath(@".\TestAssets\8.png");
TestSingleFilePreview(pngPath, "8");
}
/// <summary>
@@ -142,7 +197,7 @@ public class PeekFilePreviewTests : UITestBase
// Close peek
ClosePeekAndExplorer();
Task.Delay(1000).Wait(); // Wait for window to close completely
Thread.Sleep(1000); // Wait for window to close completely
// Reopen the same image
var reopenedWindow = OpenPeekWindow(imagePath);
@@ -267,7 +322,7 @@ public class PeekFilePreviewTests : UITestBase
openButton.Click();
// Wait a moment for the default program to launch
Task.Delay(2000).Wait();
Thread.Sleep(2000);
// Verify that the default program process has started (check for Explorer opening 7-zip)
bool defaultProgramLaunched = CheckIfExplorerLaunched();
@@ -292,7 +347,7 @@ public class PeekFilePreviewTests : UITestBase
SendKeys(Key.Enter);
// Wait a moment for the default program to launch
Task.Delay(2000).Wait();
Thread.Sleep(2000);
// Verify that the default program process has started (check for Explorer opening 7-zip)
bool defaultProgramLaunched = CheckIfExplorerLaunched();
@@ -325,7 +380,7 @@ public class PeekFilePreviewTests : UITestBase
SendKeys(Key.Right);
// Wait for file to load
Task.Delay(2000).Wait();
Thread.Sleep(2000);
// Try to determine current file from window title
var currentWindow = peekWindow.Name;
@@ -345,7 +400,7 @@ public class PeekFilePreviewTests : UITestBase
SendKeys(Key.Left);
// Wait for file to load
Task.Delay(2000).Wait();
Thread.Sleep(2000);
// Try to determine current file from window title during backward navigation
var currentWindow = peekWindow.Name;
@@ -385,13 +440,13 @@ public class PeekFilePreviewTests : UITestBase
WaitForExplorerWindow(selectedFiles[0]);
// Give Explorer time to fully load
Task.Delay(2000).Wait();
Thread.Sleep(2000);
// Use Shift+Down to extend selection to include the next 2 files
SendKeys(Key.Shift, Key.Down); // Extend to second file
Task.Delay(300).Wait();
Thread.Sleep(300);
SendKeys(Key.Shift, Key.Down); // Extend to third file
Task.Delay(300).Wait();
Thread.Sleep(300);
// Now we should have the first 3 files selected, open Peek
SendPeekHotkeyWithRetry();
@@ -411,7 +466,7 @@ public class PeekFilePreviewTests : UITestBase
for (int i = 0; i < 5; i++)
{
SendKeys(Key.Left);
Task.Delay(2000).Wait(); // Wait for file to load
Thread.Sleep(2000); // Wait for file to load
var currentWindowTitle = peekWindow.Name;
windowTitles.Add(currentWindowTitle);
@@ -432,6 +487,8 @@ public class PeekFilePreviewTests : UITestBase
{
"7.zip - File Explorer",
"7 - File Explorer",
"7",
"7.zip",
};
foreach (var title in possibleTitles)
@@ -469,21 +526,29 @@ public class PeekFilePreviewTests : UITestBase
WaitForCondition(
condition: () =>
{
// Check if Explorer window is open and responsive
var explorerProcesses = Process.GetProcessesByName("explorer")
.Where(p => p.MainWindowHandle != IntPtr.Zero)
.ToList();
if (explorerProcesses.Count != 0)
try
{
// Give Explorer a moment to fully load the file selection
Task.Delay(ExplorerLoadDelayMs).Wait();
// Check if Explorer window is open and responsive
var explorerProcesses = Process.GetProcessesByName("explorer")
.Where(p => p.MainWindowHandle != IntPtr.Zero)
.ToList();
// Verify the file is accessible
return File.Exists(filePath) || Directory.Exists(filePath);
if (explorerProcesses.Count != 0)
{
// Give Explorer a moment to fully load the file selection
Thread.Sleep(ExplorerLoadDelayMs);
// Verify the file is accessible
return File.Exists(filePath) || Directory.Exists(filePath);
}
return false;
}
catch (Exception ex)
{
Debug.WriteLine($"WaitForExplorerWindow exception: {ex.Message}");
return false;
}
return false;
},
timeoutSeconds: ExplorerOpenTimeoutSeconds,
checkIntervalMs: ExplorerCheckIntervalMs,
@@ -507,35 +572,47 @@ public class PeekFilePreviewTests : UITestBase
}
catch (Exception ex)
{
Debug.WriteLine($"SendPeekHotkeyWithRetry attempt {attempt} failed: {ex.Message}");
if (attempt == MaxRetryAttempts)
{
throw new InvalidOperationException($"Failed to open Peek after {MaxRetryAttempts} attempts. Last error: {ex.Message}", ex);
}
}
// Wait before retry
Task.Delay(RetryDelayMs).Wait();
// Wait before retry using Thread.Sleep
Thread.Sleep(RetryDelayMs);
}
throw new InvalidOperationException($"Failed to open Peek after {MaxRetryAttempts} attempts");
}
private bool WaitForPeekWindow()
{
WaitForCondition(
condition: () =>
{
if (TryFindPeekWindow())
try
{
WaitForCondition(
condition: () =>
{
// Give Peek a moment to fully initialize
Task.Delay(PeekInitializeDelayMs).Wait();
return true;
}
if (TryFindPeekWindow())
{
// Give Peek a moment to fully initialize using Thread.Sleep
Thread.Sleep(PeekInitializeDelayMs);
return true;
}
return false;
},
timeoutSeconds: PeekWindowTimeoutSeconds,
checkIntervalMs: PeekCheckIntervalMs,
timeoutMessage: "Peek window did not appear");
return true;
return false;
},
timeoutSeconds: PeekWindowTimeoutSeconds,
checkIntervalMs: PeekCheckIntervalMs,
timeoutMessage: "Peek window did not appear");
return true;
}
catch (Exception ex)
{
Debug.WriteLine($"WaitForPeekWindow failed: {ex.Message}");
return false;
}
}
private bool WaitForCondition(Func<bool> condition, int timeoutSeconds, int checkIntervalMs, string timeoutMessage)
@@ -552,12 +629,14 @@ public class PeekFilePreviewTests : UITestBase
return true;
}
}
catch
catch (Exception ex)
{
// Continue waiting on errors
// Log exception but continue waiting
Debug.WriteLine($"WaitForCondition exception: {ex.Message}");
}
Task.Delay(checkIntervalMs).Wait();
// Use async delay to prevent blocking the thread
Thread.Sleep(checkIntervalMs);
}
throw new TimeoutException($"{timeoutMessage} (timeout: {timeoutSeconds}s)");
@@ -567,48 +646,91 @@ public class PeekFilePreviewTests : UITestBase
{
try
{
// Check for Peek process
// Check for Peek process with timeout
var peekProcesses = Process.GetProcessesByName("PowerToys.Peek.UI")
.Where(p => p.MainWindowHandle != IntPtr.Zero);
return peekProcesses.Any();
var foundProcess = peekProcesses.Any();
if (foundProcess)
{
// Additional validation - check if window is responsive
Thread.Sleep(100); // Small delay to ensure window is ready
return true;
}
return false;
}
catch
catch (Exception ex)
{
// Ignore all errors in detection
Debug.WriteLine($"TryFindPeekWindow exception: {ex.Message}");
return false;
}
}
private Element OpenPeekWindow(string filePath)
{
SendKeys(Key.Enter);
try
{
SendKeys(Key.Enter);
// Open file with Peek
OpenAndPeekFile(filePath);
// Open file with Peek
OpenAndPeekFile(filePath);
// Find the Peek window using the common method
var peekWindow = FindPeekWindow(filePath);
// Find the Peek window using the common method with timeout
var peekWindow = FindPeekWindow(filePath);
// Attach to the found window
Session.Attach(peekWindow.Name);
// Attach to the found window with error handling
try
{
Session.Attach(peekWindow.Name);
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to attach to window: {ex.Message}");
}
return peekWindow;
return peekWindow;
}
catch (Exception ex)
{
Debug.WriteLine($"OpenPeekWindow failed for {filePath}: {ex.Message}");
throw;
}
}
private void TestFilePreviewWithVisualComparison(string filePath)
/// <summary>
/// Test a single file preview with visual comparison
/// </summary>
/// <param name="filePath">Full path to the file to test</param>
/// <param name="expectedFileName">Expected file name for visual comparison</param>
private void TestSingleFilePreview(string filePath, string expectedFileName, int? delayMs = null)
{
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath);
Element? previewWindow = null;
Element previewWindow = OpenPeekWindow(filePath);
try
{
Debug.WriteLine($"Testing file preview: {Path.GetFileName(filePath)}");
Assert.IsNotNull(previewWindow, $"Should open Peek window for {fileNameWithoutExt}");
previewWindow = OpenPeekWindow(filePath);
// previewWindow.SaveToPngFile(Path.Combine(ScreenshotDirectory ?? string.Empty, $"{fileNameWithoutExt}.png"));
VisualAssert.AreEqual(TestContext, previewWindow, fileNameWithoutExt );
if (delayMs.HasValue)
{
Thread.Sleep(delayMs.Value); // Allow time for the preview to load
}
// Close peek window
ClosePeekAndExplorer();
Assert.IsNotNull(previewWindow, $"Should open Peek window for {Path.GetFileName(filePath)}");
// Perform visual comparison
VisualAssert.AreEqual(TestContext, previewWindow, expectedFileName);
Debug.WriteLine($"Successfully tested: {Path.GetFileName(filePath)}");
}
finally
{
// Always cleanup in finally block
ClosePeekAndExplorer();
}
}
private Rectangle GetWindowBounds(Element window)
@@ -630,7 +752,7 @@ public class PeekFilePreviewTests : UITestBase
Assert.IsNotNull(pinButton, "Pin button should be found");
pinButton.Click();
Task.Delay(PinActionDelayMs).Wait(); // Wait for pin action to complete
Thread.Sleep(PinActionDelayMs); // Wait for pin action to complete
}
private void UnpinWindow()
@@ -640,33 +762,21 @@ public class PeekFilePreviewTests : UITestBase
Assert.IsNotNull(pinButton, "Pin button should be found");
pinButton.Click();
Task.Delay(PinActionDelayMs).Wait(); // Wait for unpin action to complete
Thread.Sleep(PinActionDelayMs); // Wait for unpin action to complete
}
private void ClosePeekAndExplorer()
{
// Close Peek window
Session.CloseMainWindow();
// Close all Explorer windows that have a main window handle
try
{
// Get all explorer processes that have a window (HasWindow=True)
var explorerProcesses = Process.GetProcessesByName("explorer")
.Where(p => p.MainWindowHandle != IntPtr.Zero)
.ToList();
foreach (var explorer in explorerProcesses)
{
explorer.CloseMainWindow();
// Give time for the window to close before continuing
Task.Delay(500).Wait();
}
// Close Peek window
Session.CloseMainWindow();
Thread.Sleep(500);
SendKeys(Key.Win, Key.M);
}
catch
catch (Exception ex)
{
// Ignore errors when closing Explorer
Debug.WriteLine($"Error closing Peek window: {ex.Message}");
}
}