2025-02-20 13:25:20 +08:00
// 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 ;
2025-06-17 17:56:48 +08:00
using System.Runtime.InteropServices ;
[UI Tests] Enhance UI Test Automation and Pipeline Support for CmdPal Module (#40871)
<!-- 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
This pull request introduces enhancements to UI test automation,
improvements to pipeline configuration, and project structure updates.
The goal is to improve flexibility, maintainability, and efficiency in
PowerToys’ CI/CD processes.
### UI Test Enhancements:
Delayed Text Input Support
- UI tests now support character-by-character text input with
configurable delays.
- This serves as a workaround for a known CmdPal bug where input is
swallowed too quickly. The delay mitigates the issue until it is fixed
in CmdPal.
Centralized Environment Management
- Introduced a new class to centralize environment variable access for
UI test configuration.
CmdPal Launch Handling in Pipelines
- Adjusted test logic to handle CmdPal module startup specifically in CI
pipelines
### Pipeline Configuration Updates:
Build Artifact Customization
- Included test-related folders in pipeline build outputs for better
traceability.
Support for Build ID Targeting
- Added support for specifying PowerToys build IDs in test pipelines,
with conditional logic for specific or latest build selection.
<img width="264" height="44" alt="image"
src="https://github.com/user-attachments/assets/0d68a51e-e41a-4868-a1c3-f4233c56b0ee"
/>
### Project Structure Updates:
Added Peek.UITests back to the solution which removed by
https://github.com/microsoft/PowerToys/pull/40754
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-31 13:55:23 +08:00
using System.Text.RegularExpressions ;
2025-02-20 13:25:20 +08:00
using Microsoft.VisualStudio.TestTools.UnitTesting ;
2025-06-17 17:56:48 +08:00
using OpenQA.Selenium ;
2025-02-20 13:25:20 +08:00
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Base class that should be inherited by all Test Classes.
/// </summary>
2025-02-28 03:08:08 -08:00
[TestClass]
2025-06-17 17:56:48 +08:00
public class UITestBase : IDisposable
2025-02-20 13:25:20 +08:00
{
2025-06-17 17:56:48 +08:00
public required TestContext TestContext { get ; set ; }
2025-02-20 13:25:20 +08:00
2025-06-17 17:56:48 +08:00
public required Session Session { get ; set ; }
[UI Tests] Enhance UI Test Automation and Pipeline Support for CmdPal Module (#40871)
<!-- 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
This pull request introduces enhancements to UI test automation,
improvements to pipeline configuration, and project structure updates.
The goal is to improve flexibility, maintainability, and efficiency in
PowerToys’ CI/CD processes.
### UI Test Enhancements:
Delayed Text Input Support
- UI tests now support character-by-character text input with
configurable delays.
- This serves as a workaround for a known CmdPal bug where input is
swallowed too quickly. The delay mitigates the issue until it is fixed
in CmdPal.
Centralized Environment Management
- Introduced a new class to centralize environment variable access for
UI test configuration.
CmdPal Launch Handling in Pipelines
- Adjusted test logic to handle CmdPal module startup specifically in CI
pipelines
### Pipeline Configuration Updates:
Build Artifact Customization
- Included test-related folders in pipeline build outputs for better
traceability.
Support for Build ID Targeting
- Added support for specifying PowerToys build IDs in test pipelines,
with conditional logic for specific or latest build selection.
<img width="264" height="44" alt="image"
src="https://github.com/user-attachments/assets/0d68a51e-e41a-4868-a1c3-f4233c56b0ee"
/>
### Project Structure Updates:
Added Peek.UITests back to the solution which removed by
https://github.com/microsoft/PowerToys/pull/40754
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-31 13:55:23 +08:00
/// <summary>
/// Gets a value indicating whether the tests are running in a CI/CD pipeline.
/// </summary>
2025-06-17 17:56:48 +08:00
public bool IsInPipeline { get ; }
[UI tests] Add full UI test coverage for Peek based on release checklist (#40734)
<!-- 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
Add full UI test coverage for Peek based on release checklist
### Peek
* Open different files to check that they're shown properly
- [x] Image
- [x] Text or dev file
- [x] Markdown file
- [ ] PDF
- [x] Archive files (.zip, .tar, .rar)
- [x] Any other not mentioned file (.exe for example) to verify the
unsupported file view is shown
* Pinning/unpinning
- [x] Pin the window, switch between images of different size, verify
the window stays at the same place and the same size.
- [x] Pin the window, close and reopen Peek, verify the new window is
opened at the same place and the same size as before.
- [x] Unpin the window, switch to a different file, verify the window is
moved to the default place.
- [x] Unpin the window, close and reopen Peek, verify the new window is
opened on the default place.
* Open with a default program
- [x] By clicking a button.
- [x] By pressing enter.
- [x] Switch between files in the folder using `LeftArrow` and
`RightArrow`, verify you can switch between all files in the folder.
- [x] Open multiple files, verify you can switch only between selected
files.
<img width="519" height="266" alt="image"
src="https://github.com/user-attachments/assets/f27c555d-9939-476f-9ecc-50d598285aef"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] **Closes:**
#[40676](https://github.com/microsoft/PowerToys/issues/40676)
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-22 09:00:39 +08:00
public string? ScreenshotDirectory { get ; set ; }
2025-06-17 17:56:48 +08:00
public static MonitorInfoData . ParamsWrapper MonitorInfoData { get ; set ; } = new MonitorInfoData . ParamsWrapper ( ) { Monitors = new List < MonitorInfoData . MonitorInfoDataWrapper > ( ) } ;
2025-02-28 03:08:08 -08:00
private readonly PowerToysModule scope ;
2025-06-17 17:56:48 +08:00
private readonly WindowSize size ;
2025-07-09 14:32:40 +08:00
private readonly string [ ] ? commandLineArgs ;
2025-06-17 17:56:48 +08:00
private SessionHelper ? sessionHelper ;
private System . Threading . Timer ? screenshotTimer ;
2025-02-20 13:25:20 +08:00
2025-07-09 14:32:40 +08:00
public UITestBase ( PowerToysModule scope = PowerToysModule . PowerToysSettings , WindowSize size = WindowSize . UnSpecified , string [ ] ? commandLineArgs = null )
2025-02-20 13:25:20 +08:00
{
[UI Tests] Enhance UI Test Automation and Pipeline Support for CmdPal Module (#40871)
<!-- 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
This pull request introduces enhancements to UI test automation,
improvements to pipeline configuration, and project structure updates.
The goal is to improve flexibility, maintainability, and efficiency in
PowerToys’ CI/CD processes.
### UI Test Enhancements:
Delayed Text Input Support
- UI tests now support character-by-character text input with
configurable delays.
- This serves as a workaround for a known CmdPal bug where input is
swallowed too quickly. The delay mitigates the issue until it is fixed
in CmdPal.
Centralized Environment Management
- Introduced a new class to centralize environment variable access for
UI test configuration.
CmdPal Launch Handling in Pipelines
- Adjusted test logic to handle CmdPal module startup specifically in CI
pipelines
### Pipeline Configuration Updates:
Build Artifact Customization
- Included test-related folders in pipeline build outputs for better
traceability.
Support for Build ID Targeting
- Added support for specifying PowerToys build IDs in test pipelines,
with conditional logic for specific or latest build selection.
<img width="264" height="44" alt="image"
src="https://github.com/user-attachments/assets/0d68a51e-e41a-4868-a1c3-f4233c56b0ee"
/>
### Project Structure Updates:
Added Peek.UITests back to the solution which removed by
https://github.com/microsoft/PowerToys/pull/40754
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-31 13:55:23 +08:00
this . IsInPipeline = EnvironmentConfig . IsInPipeline ;
Console . WriteLine ( $"Running tests on platform: {EnvironmentConfig.Platform}" ) ;
2025-06-17 17:56:48 +08:00
if ( IsInPipeline )
{
NativeMethods . ChangeDisplayResolution ( 1920 , 1080 ) ;
NativeMethods . GetMonitorInfo ( ) ;
// Escape Popups before starting
System . Windows . Forms . SendKeys . SendWait ( "{ESC}" ) ;
}
2025-02-28 03:08:08 -08:00
this . scope = scope ;
2025-06-17 17:56:48 +08:00
this . size = size ;
2025-07-09 14:32:40 +08:00
this . commandLineArgs = commandLineArgs ;
2025-02-20 13:25:20 +08:00
}
2025-02-28 03:08:08 -08:00
/// <summary>
/// Initializes the test.
/// </summary>
[TestInitialize]
public void TestInit ( )
{
[UI Tests] Enhance UI Test Automation and Pipeline Support for CmdPal Module (#40871)
<!-- 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
This pull request introduces enhancements to UI test automation,
improvements to pipeline configuration, and project structure updates.
The goal is to improve flexibility, maintainability, and efficiency in
PowerToys’ CI/CD processes.
### UI Test Enhancements:
Delayed Text Input Support
- UI tests now support character-by-character text input with
configurable delays.
- This serves as a workaround for a known CmdPal bug where input is
swallowed too quickly. The delay mitigates the issue until it is fixed
in CmdPal.
Centralized Environment Management
- Introduced a new class to centralize environment variable access for
UI test configuration.
CmdPal Launch Handling in Pipelines
- Adjusted test logic to handle CmdPal module startup specifically in CI
pipelines
### Pipeline Configuration Updates:
Build Artifact Customization
- Included test-related folders in pipeline build outputs for better
traceability.
Support for Build ID Targeting
- Added support for specifying PowerToys build IDs in test pipelines,
with conditional logic for specific or latest build selection.
<img width="264" height="44" alt="image"
src="https://github.com/user-attachments/assets/0d68a51e-e41a-4868-a1c3-f4233c56b0ee"
/>
### Project Structure Updates:
Added Peek.UITests back to the solution which removed by
https://github.com/microsoft/PowerToys/pull/40754
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-31 13:55:23 +08:00
KeyboardHelper . SendKeys ( Key . Win , Key . M ) ;
2025-06-17 17:56:48 +08:00
CloseOtherApplications ( ) ;
if ( IsInPipeline )
{
[UI tests] Add full UI test coverage for Peek based on release checklist (#40734)
<!-- 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
Add full UI test coverage for Peek based on release checklist
### Peek
* Open different files to check that they're shown properly
- [x] Image
- [x] Text or dev file
- [x] Markdown file
- [ ] PDF
- [x] Archive files (.zip, .tar, .rar)
- [x] Any other not mentioned file (.exe for example) to verify the
unsupported file view is shown
* Pinning/unpinning
- [x] Pin the window, switch between images of different size, verify
the window stays at the same place and the same size.
- [x] Pin the window, close and reopen Peek, verify the new window is
opened at the same place and the same size as before.
- [x] Unpin the window, switch to a different file, verify the window is
moved to the default place.
- [x] Unpin the window, close and reopen Peek, verify the new window is
opened on the default place.
* Open with a default program
- [x] By clicking a button.
- [x] By pressing enter.
- [x] Switch between files in the folder using `LeftArrow` and
`RightArrow`, verify you can switch between all files in the folder.
- [x] Open multiple files, verify you can switch only between selected
files.
<img width="519" height="266" alt="image"
src="https://github.com/user-attachments/assets/f27c555d-9939-476f-9ecc-50d598285aef"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] **Closes:**
#[40676](https://github.com/microsoft/PowerToys/issues/40676)
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-22 09:00:39 +08:00
ScreenshotDirectory = Path . Combine ( this . TestContext . TestResultsDirectory ? ? string . Empty , "UITestScreenshots_" + Guid . NewGuid ( ) . ToString ( ) ) ;
Directory . CreateDirectory ( ScreenshotDirectory ) ;
2025-06-17 17:56:48 +08:00
// Take screenshot every 1 second
[UI tests] Add full UI test coverage for Peek based on release checklist (#40734)
<!-- 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
Add full UI test coverage for Peek based on release checklist
### Peek
* Open different files to check that they're shown properly
- [x] Image
- [x] Text or dev file
- [x] Markdown file
- [ ] PDF
- [x] Archive files (.zip, .tar, .rar)
- [x] Any other not mentioned file (.exe for example) to verify the
unsupported file view is shown
* Pinning/unpinning
- [x] Pin the window, switch between images of different size, verify
the window stays at the same place and the same size.
- [x] Pin the window, close and reopen Peek, verify the new window is
opened at the same place and the same size as before.
- [x] Unpin the window, switch to a different file, verify the window is
moved to the default place.
- [x] Unpin the window, close and reopen Peek, verify the new window is
opened on the default place.
* Open with a default program
- [x] By clicking a button.
- [x] By pressing enter.
- [x] Switch between files in the folder using `LeftArrow` and
`RightArrow`, verify you can switch between all files in the folder.
- [x] Open multiple files, verify you can switch only between selected
files.
<img width="519" height="266" alt="image"
src="https://github.com/user-attachments/assets/f27c555d-9939-476f-9ecc-50d598285aef"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] **Closes:**
#[40676](https://github.com/microsoft/PowerToys/issues/40676)
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-22 09:00:39 +08:00
screenshotTimer = new System . Threading . Timer ( ScreenCapture . TimerCallback , ScreenshotDirectory , TimeSpan . Zero , TimeSpan . FromMilliseconds ( 1000 ) ) ;
2025-06-17 17:56:48 +08:00
// Escape Popups before starting
System . Windows . Forms . SendKeys . SendWait ( "{ESC}" ) ;
}
2025-07-09 14:32:40 +08:00
this . sessionHelper = new SessionHelper ( scope , commandLineArgs ) . Init ( ) ;
2025-06-17 17:56:48 +08:00
this . Session = new Session ( this . sessionHelper . GetRoot ( ) , this . sessionHelper . GetDriver ( ) , scope , size ) ;
2025-02-20 13:25:20 +08:00
}
2025-03-14 17:04:23 +08:00
/// <summary>
2025-06-17 17:56:48 +08:00
/// Cleanups the test.
2025-03-14 17:04:23 +08:00
/// </summary>
[TestCleanup]
2025-06-17 17:56:48 +08:00
public void TestCleanup ( )
{
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 ( )
2025-03-14 17:04:23 +08:00
{
2025-06-17 17:56:48 +08:00
screenshotTimer ? . Dispose ( ) ;
GC . SuppressFinalize ( this ) ;
2025-03-14 17:04:23 +08:00
}
2025-02-24 02:05:55 -08:00
/// <summary>
/// Finds an element by selector.
/// Shortcut for this.Session.Find<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>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-24 02:05:55 -08:00
/// <returns>The found element.</returns>
2025-06-17 17:56:48 +08:00
protected T Find < T > ( By by , int timeoutMS = 5000 , bool global = false )
2025-02-24 02:05:55 -08:00
where T : Element , new ( )
{
2025-06-17 17:56:48 +08:00
return this . Session . Find < T > ( by , timeoutMS , global ) ;
2025-02-24 02:05:55 -08:00
}
2025-02-28 03:08:08 -08:00
/// <summary>
2025-06-17 17:56:48 +08:00
/// Shortcut for this.Session.Find<Element>(name, timeoutMS)
2025-02-28 03:08:08 -08:00
/// </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>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-28 03:08:08 -08:00
/// <returns>The found element.</returns>
2025-06-17 17:56:48 +08:00
protected T Find < T > ( string name , int timeoutMS = 5000 , bool global = false )
2025-02-28 03:08:08 -08:00
where T : Element , new ( )
{
2025-06-17 17:56:48 +08:00
return this . Session . Find < T > ( By . Name ( name ) , timeoutMS , global ) ;
2025-02-28 03:08:08 -08:00
}
/// <summary>
/// Shortcut for this.Session.Find<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the element.</param>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-28 03:08:08 -08:00
/// <returns>The found element.</returns>
2025-06-17 17:56:48 +08:00
protected Element Find ( By by , int timeoutMS = 5000 , bool global = false )
2025-02-28 03:08:08 -08:00
{
2025-06-17 17:56:48 +08:00
return this . Session . Find ( by , timeoutMS , global ) ;
2025-02-28 03:08:08 -08:00
}
/// <summary>
2025-06-17 17:56:48 +08:00
/// Shortcut for this.Session.Find<Element>(name, timeoutMS)
2025-02-28 03:08:08 -08:00
/// </summary>
/// <param name="name">The name of the element.</param>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-28 03:08:08 -08:00
/// <returns>The found element.</returns>
2025-06-17 17:56:48 +08:00
protected Element Find ( string name , int timeoutMS = 5000 , bool global = false )
{
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if only has one element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if only has one element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if only has one element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if only has one element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if has one or more element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if has one or more element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if has one or more element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
public bool Has < T > ( string name , int timeoutMS = 5000 , bool global = false )
where T : Element , new ( )
2025-02-28 03:08:08 -08:00
{
2025-06-17 17:56:48 +08:00
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>
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
/// <returns>True if has one or more element; otherwise, false.</returns>
2025-06-17 17:56:48 +08:00
public bool Has ( string name , int timeoutMS = 5000 , bool global = false )
{
return this . Session . Has < Element > ( name , timeoutMS , global ) ;
2025-02-28 03:08:08 -08:00
}
[UI Tests] Enhance UI Test Automation and Pipeline Support for CmdPal Module (#40871)
<!-- 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
This pull request introduces enhancements to UI test automation,
improvements to pipeline configuration, and project structure updates.
The goal is to improve flexibility, maintainability, and efficiency in
PowerToys’ CI/CD processes.
### UI Test Enhancements:
Delayed Text Input Support
- UI tests now support character-by-character text input with
configurable delays.
- This serves as a workaround for a known CmdPal bug where input is
swallowed too quickly. The delay mitigates the issue until it is fixed
in CmdPal.
Centralized Environment Management
- Introduced a new class to centralize environment variable access for
UI test configuration.
CmdPal Launch Handling in Pipelines
- Adjusted test logic to handle CmdPal module startup specifically in CI
pipelines
### Pipeline Configuration Updates:
Build Artifact Customization
- Included test-related folders in pipeline build outputs for better
traceability.
Support for Build ID Targeting
- Added support for specifying PowerToys build IDs in test pipelines,
with conditional logic for specific or latest build selection.
<img width="264" height="44" alt="image"
src="https://github.com/user-attachments/assets/0d68a51e-e41a-4868-a1c3-f4233c56b0ee"
/>
### Project Structure Updates:
Added Peek.UITests back to the solution which removed by
https://github.com/microsoft/PowerToys/pull/40754
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-31 13:55:23 +08:00
/// <summary>
/// Finds an element using partial name matching (contains).
/// Useful for finding windows with variable titles like "filename.txt - Notepad" or "filename - Notepad".
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="partialName">Part of the name to search for.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected T FindByPartialName < T > ( string partialName , int timeoutMS = 5000 , bool global = false )
where T : Element , new ( )
{
return Session . Find < T > ( By . XPath ( $"//*[contains(@Name, '{partialName}')]" ) , timeoutMS , global ) ;
}
/// <summary>
/// Finds an element using partial name matching (contains).
/// </summary>
/// <param name="partialName">Part of the name to search for.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected Element FindByPartialName ( string partialName , int timeoutMS = 5000 , bool global = false )
{
return FindByPartialName < Element > ( partialName , timeoutMS , global ) ;
}
/// <summary>
/// Base method for finding elements by selector and filtering by name pattern.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="selector">The selector to find initial candidates.</param>
/// <param name="namePattern">Pattern to match against the Name attribute. Supports regex patterns.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <param name="errorMessage">Custom error message when no element is found.</param>
/// <returns>The found element.</returns>
private T FindByNamePattern < T > ( By selector , string namePattern , int timeoutMS = 5000 , bool global = false , string? errorMessage = null )
where T : Element , new ( )
{
var elements = Session . FindAll < T > ( selector , timeoutMS , global ) ;
var regex = new Regex ( namePattern , RegexOptions . IgnoreCase ) ;
foreach ( var element in elements )
{
var name = element . GetAttribute ( "Name" ) ;
if ( ! string . IsNullOrEmpty ( name ) & & regex . IsMatch ( name ) )
{
return element ;
}
}
throw new NoSuchElementException ( errorMessage ? ? $"No element found matching pattern: {namePattern}" ) ;
}
/// <summary>
/// Finds an element using regular expression pattern matching.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="pattern">Regular expression pattern to match against the Name attribute.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected T FindByPattern < T > ( string pattern , int timeoutMS = 5000 , bool global = false )
where T : Element , new ( )
{
return FindByNamePattern < T > ( By . XPath ( "//*[@Name]" ) , pattern , timeoutMS , global , $"No element found matching pattern: {pattern}" ) ;
}
/// <summary>
/// Finds an element using regular expression pattern matching.
/// </summary>
/// <param name="pattern">Regular expression pattern to match against the Name attribute.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected Element FindByPattern ( string pattern , int timeoutMS = 5000 , bool global = false )
{
return FindByPattern < Element > ( pattern , timeoutMS , global ) ;
}
/// <summary>
/// Finds an element by ClassName only.
/// Returns the first element found with the specified ClassName.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="className">The ClassName to search for (e.g., "Notepad", "CabinetWClass").</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected T FindByClassName < T > ( string className , int timeoutMS = 5000 , bool global = false )
where T : Element , new ( )
{
return Session . Find < T > ( By . ClassName ( className ) , timeoutMS , global ) ;
}
/// <summary>
/// Finds an element by ClassName only.
/// Returns the first element found with the specified ClassName.
/// </summary>
/// <param name="className">The ClassName to search for (e.g., "Notepad", "CabinetWClass").</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected Element FindByClassName ( string className , int timeoutMS = 5000 , bool global = false )
{
return FindByClassName < Element > ( className , timeoutMS , global ) ;
}
/// <summary>
/// Finds an element by ClassName and matches its Name attribute using regex pattern matching.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="className">The ClassName to search for (e.g., "Notepad", "CabinetWClass").</param>
/// <param name="namePattern">Pattern to match against the Name attribute. Supports regex patterns.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected T FindByClassNameAndNamePattern < T > ( string className , string namePattern , int timeoutMS = 5000 , bool global = false )
where T : Element , new ( )
{
return FindByNamePattern < T > ( By . ClassName ( className ) , namePattern , timeoutMS , global , $"No element with ClassName '{className}' found matching name pattern: {namePattern}" ) ;
}
/// <summary>
/// Finds an element by ClassName and matches its Name attribute using regex pattern matching.
/// </summary>
/// <param name="className">The ClassName to search for (e.g., "Notepad", "CabinetWClass").</param>
/// <param name="namePattern">Pattern to match against the Name attribute. Supports regex patterns.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found element.</returns>
protected Element FindByClassNameAndNamePattern ( string className , string namePattern , int timeoutMS = 5000 , bool global = false )
{
return FindByClassNameAndNamePattern < Element > ( className , namePattern , timeoutMS , global ) ;
}
/// <summary>
/// Finds a Notepad window regardless of whether the file extension is shown in the title.
/// Handles both "filename.txt - Notepad" and "filename - Notepad" formats.
/// Uses ClassName to efficiently find Notepad windows first, then matches the filename.
/// </summary>
/// <param name="baseFileName">The base filename without extension (e.g., "test" for "test.txt").</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found Notepad window element.</returns>
protected Element FindNotepadWindow ( string baseFileName , int timeoutMS = 5000 , bool global = false )
{
string pattern = $@"^{Regex.Escape(baseFileName)}(\.\w+)?(\s*-\s*|\s+)Notepad$" ;
return FindByClassNameAndNamePattern ( "Notepad" , pattern , timeoutMS , global ) ;
}
/// <summary>
/// Finds an Explorer window regardless of the folder or file name display format.
/// Handles various Explorer window title formats like "FolderName", "FileName", "FolderName - File Explorer", etc.
/// Uses ClassName to efficiently find Explorer windows first, then matches the folder or file name.
/// </summary>
/// <param name="folderName">The folder or file name to search for (e.g., "Documents", "Desktop", "test.txt").</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found Explorer window element.</returns>
protected Element FindExplorerWindow ( string folderName , int timeoutMS = 5000 , bool global = false )
{
string pattern = $@"^{Regex.Escape(folderName)}(\s*-\s*(File\s+Explorer|Windows\s+Explorer))?$" ;
return FindByClassNameAndNamePattern ( "CabinetWClass" , pattern , timeoutMS , global ) ;
}
/// <summary>
/// Finds an Explorer window by partial folder path.
/// Useful when the full path might be displayed in the title.
/// </summary>
/// <param name="partialPath">Part of the folder path to search for.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>The found Explorer window element.</returns>
protected Element FindExplorerByPartialPath ( string partialPath , int timeoutMS = 5000 , bool global = false )
{
return FindByPartialName ( partialPath , timeoutMS , global ) ;
}
2025-02-24 02:05:55 -08:00
/// <summary>
/// Finds all elements by selector.
/// Shortcut for this.Session.FindAll<T>(by, timeoutMS)
/// </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>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-24 02:05:55 -08:00
/// <returns>A read-only collection of the found elements.</returns>
2025-06-17 17:56:48 +08:00
protected ReadOnlyCollection < T > FindAll < T > ( By by , int timeoutMS = 5000 , bool global = false )
2025-02-24 02:05:55 -08:00
where T : Element , new ( )
{
2025-06-17 17:56:48 +08:00
return this . Session . FindAll < T > ( by , timeoutMS , global ) ;
2025-02-24 02:05:55 -08:00
}
2025-02-20 13:25:20 +08:00
/// <summary>
2025-02-28 03:08:08 -08:00
/// Finds all elements by selector.
/// Shortcut for this.Session.FindAll<Element>(By.Name(name), timeoutMS)
2025-02-20 13:25:20 +08:00
/// </summary>
2025-02-28 03:08:08 -08:00
/// <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>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-28 03:08:08 -08:00
/// <returns>A read-only collection of the found elements.</returns>
2025-06-17 17:56:48 +08:00
protected ReadOnlyCollection < T > FindAll < T > ( string name , int timeoutMS = 5000 , bool global = false )
2025-02-28 03:08:08 -08:00
where T : Element , new ( )
2025-02-20 13:25:20 +08:00
{
2025-06-17 17:56:48 +08:00
return this . Session . FindAll < T > ( By . Name ( name ) , timeoutMS , global ) ;
2025-02-28 03:08:08 -08:00
}
2025-02-20 13:25:20 +08:00
2025-02-28 03:08:08 -08:00
/// <summary>
/// Finds all elements by selector.
/// Shortcut for this.Session.FindAll<Element>(by, timeoutMS)
/// </summary>
/// <param name="by">The selector to find the elements.</param>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-28 03:08:08 -08:00
/// <returns>A read-only collection of the found elements.</returns>
2025-06-17 17:56:48 +08:00
protected ReadOnlyCollection < Element > FindAll ( By by , int timeoutMS = 5000 , bool global = false )
2025-02-28 03:08:08 -08:00
{
2025-06-17 17:56:48 +08:00
return this . Session . FindAll < Element > ( by , timeoutMS , global ) ;
2025-02-28 03:08:08 -08:00
}
2025-02-20 13:25:20 +08:00
2025-02-28 03:08:08 -08:00
/// <summary>
/// Finds all elements by selector.
/// Shortcut for this.Session.FindAll<Element>(By.Name(name), timeoutMS)
/// </summary>
/// <param name="name">The name of the elements.</param>
2025-06-17 17:56:48 +08:00
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
2025-02-28 03:08:08 -08:00
/// <returns>A read-only collection of the found elements.</returns>
2025-06-17 17:56:48 +08:00
protected ReadOnlyCollection < Element > FindAll ( string name , int timeoutMS = 5000 , bool global = false )
{
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 )
2025-02-28 03:08:08 -08:00
{
2025-06-17 17:56:48 +08:00
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 ( )
{
[UI tests] Add full UI test coverage for Peek based on release checklist (#40734)
<!-- 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
Add full UI test coverage for Peek based on release checklist
### Peek
* Open different files to check that they're shown properly
- [x] Image
- [x] Text or dev file
- [x] Markdown file
- [ ] PDF
- [x] Archive files (.zip, .tar, .rar)
- [x] Any other not mentioned file (.exe for example) to verify the
unsupported file view is shown
* Pinning/unpinning
- [x] Pin the window, switch between images of different size, verify
the window stays at the same place and the same size.
- [x] Pin the window, close and reopen Peek, verify the new window is
opened at the same place and the same size as before.
- [x] Unpin the window, switch to a different file, verify the window is
moved to the default place.
- [x] Unpin the window, close and reopen Peek, verify the new window is
opened on the default place.
* Open with a default program
- [x] By clicking a button.
- [x] By pressing enter.
- [x] Switch between files in the folder using `LeftArrow` and
`RightArrow`, verify you can switch between all files in the folder.
- [x] Open multiple files, verify you can switch only between selected
files.
<img width="519" height="266" alt="image"
src="https://github.com/user-attachments/assets/f27c555d-9939-476f-9ecc-50d598285aef"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] **Closes:**
#[40676](https://github.com/microsoft/PowerToys/issues/40676)
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-22 09:00:39 +08:00
if ( ScreenshotDirectory ! = null )
2025-06-17 17:56:48 +08:00
{
[UI tests] Add full UI test coverage for Peek based on release checklist (#40734)
<!-- 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
Add full UI test coverage for Peek based on release checklist
### Peek
* Open different files to check that they're shown properly
- [x] Image
- [x] Text or dev file
- [x] Markdown file
- [ ] PDF
- [x] Archive files (.zip, .tar, .rar)
- [x] Any other not mentioned file (.exe for example) to verify the
unsupported file view is shown
* Pinning/unpinning
- [x] Pin the window, switch between images of different size, verify
the window stays at the same place and the same size.
- [x] Pin the window, close and reopen Peek, verify the new window is
opened at the same place and the same size as before.
- [x] Unpin the window, switch to a different file, verify the window is
moved to the default place.
- [x] Unpin the window, close and reopen Peek, verify the new window is
opened on the default place.
* Open with a default program
- [x] By clicking a button.
- [x] By pressing enter.
- [x] Switch between files in the folder using `LeftArrow` and
`RightArrow`, verify you can switch between all files in the folder.
- [x] Open multiple files, verify you can switch only between selected
files.
<img width="519" height="266" alt="image"
src="https://github.com/user-attachments/assets/f27c555d-9939-476f-9ecc-50d598285aef"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] **Closes:**
#[40676](https://github.com/microsoft/PowerToys/issues/40676)
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-22 09:00:39 +08:00
foreach ( string file in Directory . GetFiles ( ScreenshotDirectory ) )
2025-06-17 17:56:48 +08:00
{
this . TestContext . AddResultFile ( file ) ;
}
}
2025-02-20 13:25:20 +08:00
}
2025-03-14 17:04:23 +08:00
/// <summary>
/// Restart scope exe.
/// </summary>
public void RestartScopeExe ( )
{
2025-06-17 17:56:48 +08:00
this . sessionHelper ! . RestartScopeExe ( ) ;
this . Session = new Session ( this . sessionHelper . GetRoot ( ) , this . sessionHelper . GetDriver ( ) , this . scope , this . size ) ;
2025-03-14 17:04:23 +08:00
return ;
}
/// <summary>
/// Restart scope exe.
/// </summary>
public void ExitScopeExe ( )
{
2025-06-17 17:56:48 +08:00
this . sessionHelper ! . ExitScopeExe ( ) ;
2025-03-14 17:04:23 +08:00
return ;
}
2025-06-17 17:56:48 +08:00
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}" ) ;
}
}
[UI tests] Add full UI test coverage for Peek based on release checklist (#40734)
<!-- 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
Add full UI test coverage for Peek based on release checklist
### Peek
* Open different files to check that they're shown properly
- [x] Image
- [x] Text or dev file
- [x] Markdown file
- [ ] PDF
- [x] Archive files (.zip, .tar, .rar)
- [x] Any other not mentioned file (.exe for example) to verify the
unsupported file view is shown
* Pinning/unpinning
- [x] Pin the window, switch between images of different size, verify
the window stays at the same place and the same size.
- [x] Pin the window, close and reopen Peek, verify the new window is
opened at the same place and the same size as before.
- [x] Unpin the window, switch to a different file, verify the window is
moved to the default place.
- [x] Unpin the window, close and reopen Peek, verify the new window is
opened on the default place.
* Open with a default program
- [x] By clicking a button.
- [x] By pressing enter.
- [x] Switch between files in the folder using `LeftArrow` and
`RightArrow`, verify you can switch between all files in the folder.
- [x] Open multiple files, verify you can switch only between selected
files.
<img width="519" height="266" alt="image"
src="https://github.com/user-attachments/assets/f27c555d-9939-476f-9ecc-50d598285aef"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] **Closes:**
#[40676](https://github.com/microsoft/PowerToys/issues/40676)
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-07-22 09:00:39 +08:00
// Windows API for moving windows
[DllImport("user32.dll")]
private static extern bool SetWindowPos ( IntPtr hWnd , IntPtr hWndInsertAfter , int x , int y , int cx , int cy , uint uFlags ) ;
private const uint SWPNOSIZE = 0x0001 ;
private const uint SWPNOZORDER = 0x0004 ;
public static void MoveWindow ( Element window , int x , int y )
{
var windowHandle = IntPtr . Parse ( window . GetAttribute ( "NativeWindowHandle" ) ? ? "0" , System . Globalization . CultureInfo . InvariantCulture ) ;
if ( windowHandle ! = IntPtr . Zero )
{
SetWindowPos ( windowHandle , IntPtr . Zero , x , y , 0 , 0 , SWPNOSIZE | SWPNOZORDER ) ;
Task . Delay ( 500 ) . Wait ( ) ;
}
}
2025-06-17 17:56:48 +08:00
}
2025-02-20 13:25:20 +08:00
}
}