From a64de74f3ba6b77dfdc5046827a1ebdd3a74a200 Mon Sep 17 00:00:00 2001 From: leileizhang Date: Mon, 7 Jul 2025 12:58:49 +0800 Subject: [PATCH] [UI Tests] Fix incorrect Settings Page launch method in UI test framework (#40367) ## Summary of the Pull Request ### Root Cause The UI test framework previously attempted to launch PowerToys.Settings.exe directly. However, this bypasses the PowerToys Runner, which is required for proper interaction and communication between runner. ### Fix 1. This change updates the launch mechanism to start PowerToys through the Runner with the appropriate argument ("--open-settings"). 2. Prevents the Debug dialog from appearing in test runs ## 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 ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- src/common/UITestAutomation/SessionHelper.cs | 97 ++++++++++++++------ src/common/UITestAutomation/UITestBase.cs | 10 -- 2 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/common/UITestAutomation/SessionHelper.cs b/src/common/UITestAutomation/SessionHelper.cs index 44633f45c1..e29fcb7cf3 100644 --- a/src/common/UITestAutomation/SessionHelper.cs +++ b/src/common/UITestAutomation/SessionHelper.cs @@ -9,6 +9,7 @@ using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Windows; +using static Microsoft.PowerToys.UITest.WindowHelper; namespace Microsoft.PowerToys.UITest { @@ -41,18 +42,6 @@ namespace Microsoft.PowerToys.UITest this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); CheckWinAppDriverAndRoot(); - - var runnerProcessInfo = new ProcessStartInfo - { - FileName = locationPath + this.runnerPath, - Verb = "runas", - }; - - if (scope == PowerToysModule.PowerToysSettings) - { - this.ExitExe(runnerProcessInfo.FileName); - this.runner = Process.Start(runnerProcessInfo); - } } /// @@ -76,6 +65,7 @@ namespace Microsoft.PowerToys.UITest public SessionHelper Init() { this.ExitExe(this.locationPath + this.sessionPath); + this.StartExe(this.locationPath + this.sessionPath); Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null."); @@ -89,19 +79,6 @@ namespace Microsoft.PowerToys.UITest public void Cleanup() { ExitScopeExe(); - try - { - if (this.scope == PowerToysModule.PowerToysSettings) - { - runner?.Kill(); - runner?.WaitForExit(); // Optional: Wait for the process to exit - } - } - catch (Exception ex) - { - // Handle exceptions if needed - Debug.WriteLine($"Exception during Cleanup: {ex.Message}"); - } } /// @@ -135,10 +112,65 @@ namespace Microsoft.PowerToys.UITest public void StartExe(string appPath) { var opts = new AppiumOptions(); - opts.AddAdditionalCapability("app", appPath); + + // if we want to start settings, we need to use the runner exe to open settings + if (scope == PowerToysModule.PowerToysSettings) + { + TryLaunchPowerToysSettings(opts); + } + else + { + opts.AddAdditionalCapability("app", appPath); + } + this.Driver = NewWindowsDriver(opts); } + private void TryLaunchPowerToysSettings(AppiumOptions opts) + { + CheckWinAppDriverAndRoot(); + + var runnerProcessInfo = new ProcessStartInfo + { + FileName = locationPath + this.runnerPath, + Verb = "runas", + Arguments = "--open-settings", + }; + + this.ExitExe(runnerProcessInfo.FileName); + this.runner = Process.Start(runnerProcessInfo); + Thread.Sleep(5000); + + if (root != null) + { + const int maxRetries = 3; + const int delayMs = 5000; + var windowName = "PowerToys Settings"; + + for (int attempt = 1; attempt <= maxRetries; attempt++) + { + var settingsWindow = ApiHelper.FindDesktopWindowHandler( + new[] { windowName, AdministratorPrefix + windowName }); + + if (settingsWindow.Count > 0) + { + var hexHwnd = settingsWindow[0].HWnd.ToString("x"); + opts.AddAdditionalCapability("appTopLevelWindow", hexHwnd); + return; + } + + if (attempt < maxRetries) + { + Thread.Sleep(delayMs); + } + else + { + throw new TimeoutException("Failed to find PowerToys Settings window after multiple attempts."); + } + } + } + } + /// /// Starts a new exe and takes control of it. /// @@ -176,6 +208,19 @@ namespace Microsoft.PowerToys.UITest public void ExitScopeExe() { ExitExe(sessionPath); + try + { + if (this.scope == PowerToysModule.PowerToysSettings) + { + runner?.Kill(); + runner?.WaitForExit(); // Optional: Wait for the process to exit + } + } + catch (Exception ex) + { + // Handle exceptions if needed + Debug.WriteLine($"Exception during Cleanup: {ex.Message}"); + } } /// diff --git a/src/common/UITestAutomation/UITestBase.cs b/src/common/UITestAutomation/UITestBase.cs index 57ca0685bb..f5bdf77e91 100644 --- a/src/common/UITestAutomation/UITestBase.cs +++ b/src/common/UITestAutomation/UITestBase.cs @@ -68,16 +68,6 @@ namespace Microsoft.PowerToys.UITest this.sessionHelper = new SessionHelper(scope).Init(); this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), scope, size); - - if (this.scope == PowerToysModule.PowerToysSettings) - { - // close Debug warning dialog if any - // Such debug warning dialog seems only appear in PowerToys Settings - if (this.FindAll("DEBUG").Count > 0) - { - this.Find("DEBUG").Find