mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[UI automation test] Add basic tests case for powerrename module. (#40393)
<!-- 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. Add command args support in ui test core 2. Add command line parse logic in powerrename 3. Add some test cases. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] **Closes:** #xxx - [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>
This commit is contained in:
3
.github/actions/spell-check/expect.txt
vendored
3
.github/actions/spell-check/expect.txt
vendored
@@ -480,6 +480,8 @@ eyetracker
|
||||
FANCYZONESDRAWLAYOUTTEST
|
||||
FANCYZONESEDITOR
|
||||
FARPROC
|
||||
fesf
|
||||
fff
|
||||
FFFF
|
||||
FILEEXPLORER
|
||||
fileexploreraddons
|
||||
@@ -523,6 +525,7 @@ frm
|
||||
FROMTOUCH
|
||||
fsanitize
|
||||
fsmgmt
|
||||
fxf
|
||||
fuzzingtesting
|
||||
FZE
|
||||
gacutil
|
||||
|
||||
@@ -605,9 +605,6 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLib", "src\modules\Workspaces\WorkspacesLib\WorkspacesLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLibUnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkspacesLauncherUI", "src\modules\Workspaces\WorkspacesLauncherUI\WorkspacesLauncherUI.csproj", "{9C53CC25-0623-4569-95BC-B05410675EE3}"
|
||||
EndProject
|
||||
@@ -725,6 +722,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorEngineCommon", "s
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCsWin32", "src\common\ManagedCsWin32\ManagedCsWin32.csproj", "{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerRenameUITest", "src\modules\powerrename\PowerRenameUITest\PowerRenameUITest.csproj", "{9D3F3793-EFE3-4525-8782-238015DABA62}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2605,22 +2604,6 @@ Global
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
|
||||
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2643,6 +2626,22 @@ Global
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
|
||||
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2659,6 +2658,14 @@ Global
|
||||
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|x64.ActiveCfg = Release|x64
|
||||
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|x64.Build.0 = Release|x64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|x64.Build.0 = Debug|x64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|x64.ActiveCfg = Release|x64
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2798,7 +2805,7 @@ Global
|
||||
{25C91A4E-BA4E-467A-85CD-8B62545BF674} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
|
||||
{6AB6A2D6-F859-4A82-9184-0BD29C9F07D1} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
|
||||
{212AD910-8488-4036-BE20-326931B75FB2} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{7AC943C9-52E8-44CF-9083-744D8049667B} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{7AC943C9-52E8-44CF-9083-744D8049667B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{54A93AF7-60C7-4F6C-99D2-FBB1F75F853A} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
||||
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
||||
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
|
||||
@@ -2930,13 +2937,14 @@ Global
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
|
||||
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
|
||||
{64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
|
||||
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
|
||||
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
|
||||
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{9D3F3793-EFE3-4525-8782-238015DABA62} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
Hosts,
|
||||
Runner,
|
||||
Workspaces,
|
||||
PowerRename,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,6 +98,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
[PowerToysModule.Hosts] = "Hosts File Editor",
|
||||
[PowerToysModule.Runner] = "PowerToys",
|
||||
[PowerToysModule.Workspaces] = "Workspaces Editor",
|
||||
[PowerToysModule.PowerRename] = "PowerRename",
|
||||
};
|
||||
|
||||
// Exe start path for the module if it exists.
|
||||
@@ -107,6 +109,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
[PowerToysModule.Hosts] = @"\..\..\..\WinUI3Apps\PowerToys.Hosts.exe",
|
||||
[PowerToysModule.Runner] = @"\..\..\..\PowerToys.exe",
|
||||
[PowerToysModule.Workspaces] = @"\..\..\..\PowerToys.WorkspacesEditor.exe",
|
||||
[PowerToysModule.PowerRename] = @"\..\..\..\WinUI3Apps\PowerToys.PowerRename.exe",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
@@ -33,11 +35,13 @@ namespace Microsoft.PowerToys.UITest
|
||||
private Process? runner;
|
||||
|
||||
private PowerToysModule scope;
|
||||
private string[]? commandLineArgs;
|
||||
|
||||
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
|
||||
public SessionHelper(PowerToysModule scope)
|
||||
public SessionHelper(PowerToysModule scope, string[]? commandLineArgs = null)
|
||||
{
|
||||
this.scope = scope;
|
||||
this.commandLineArgs = commandLineArgs;
|
||||
this.sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
|
||||
this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
@@ -66,7 +70,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
this.ExitExe(this.locationPath + this.sessionPath);
|
||||
|
||||
this.StartExe(this.locationPath + this.sessionPath);
|
||||
this.StartExe(this.locationPath + this.sessionPath, this.commandLineArgs);
|
||||
|
||||
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
|
||||
|
||||
@@ -109,18 +113,27 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// Starts a new exe and takes control of it.
|
||||
/// </summary>
|
||||
/// <param name="appPath">The path to the application executable.</param>
|
||||
public void StartExe(string appPath)
|
||||
/// <param name="args">Optional command line arguments to pass to the application.</param>
|
||||
public void StartExe(string appPath, string[]? args = null)
|
||||
{
|
||||
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)
|
||||
if (args != null && args.Length > 0)
|
||||
{
|
||||
TryLaunchPowerToysSettings(opts);
|
||||
}
|
||||
else
|
||||
{
|
||||
opts.AddAdditionalCapability("app", appPath);
|
||||
// Build command line arguments string
|
||||
string argsString = string.Join(" ", args.Select(arg =>
|
||||
{
|
||||
// Quote arguments that contain spaces
|
||||
if (arg.Contains(' '))
|
||||
{
|
||||
return $"\"{arg}\"";
|
||||
}
|
||||
|
||||
return arg;
|
||||
}));
|
||||
|
||||
opts.AddAdditionalCapability("appArguments", argsString);
|
||||
}
|
||||
|
||||
this.Driver = NewWindowsDriver(opts);
|
||||
@@ -174,7 +187,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <summary>
|
||||
/// Starts a new exe and takes control of it.
|
||||
/// </summary>
|
||||
/// <param name="info">The path to the application executable.</param>
|
||||
/// <param name="info">The AppiumOptions for the application.</param>
|
||||
private WindowsDriver<WindowsElement> NewWindowsDriver(AppiumOptions info)
|
||||
{
|
||||
// Create driver with retry
|
||||
@@ -229,7 +242,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
public void RestartScopeExe()
|
||||
{
|
||||
ExitScopeExe();
|
||||
StartExe(locationPath + sessionPath);
|
||||
StartExe(locationPath + sessionPath, this.commandLineArgs);
|
||||
}
|
||||
|
||||
public WindowsDriver<WindowsElement> GetRoot()
|
||||
|
||||
@@ -26,11 +26,12 @@ namespace Microsoft.PowerToys.UITest
|
||||
|
||||
private readonly PowerToysModule scope;
|
||||
private readonly WindowSize size;
|
||||
private readonly string[]? commandLineArgs;
|
||||
private SessionHelper? sessionHelper;
|
||||
private System.Threading.Timer? screenshotTimer;
|
||||
private string? screenshotDirectory;
|
||||
|
||||
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified)
|
||||
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified, string[]? commandLineArgs = null)
|
||||
{
|
||||
this.IsInPipeline = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("platform"));
|
||||
Console.WriteLine($"Running tests on platform: {Environment.GetEnvironmentVariable("platform")}");
|
||||
@@ -45,6 +46,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
|
||||
this.scope = scope;
|
||||
this.size = size;
|
||||
this.commandLineArgs = commandLineArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,7 +68,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
System.Windows.Forms.SendKeys.SendWait("{ESC}");
|
||||
}
|
||||
|
||||
this.sessionHelper = new SessionHelper(scope).Init();
|
||||
this.sessionHelper = new SessionHelper(scope, commandLineArgs).Init();
|
||||
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), scope, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/logger/logger_settings.h>
|
||||
@@ -28,6 +30,61 @@ std::vector<std::wstring> g_files;
|
||||
|
||||
const std::wstring moduleName = L"PowerRename";
|
||||
|
||||
// Helper function to parse command line arguments for file paths
|
||||
std::vector<std::wstring> ParseCommandLineArgs(const std::wstring& commandLine)
|
||||
{
|
||||
std::vector<std::wstring> filePaths;
|
||||
|
||||
// Skip executable name
|
||||
size_t argsStart = 0;
|
||||
if (!commandLine.empty() && commandLine[0] == L'"')
|
||||
{
|
||||
argsStart = commandLine.find(L'"', 1);
|
||||
if (argsStart != std::wstring::npos) argsStart++;
|
||||
}
|
||||
else
|
||||
{
|
||||
argsStart = commandLine.find_first_of(L" \t");
|
||||
}
|
||||
|
||||
if (argsStart == std::wstring::npos) return filePaths;
|
||||
|
||||
// Get the arguments part
|
||||
std::wstring args = commandLine.substr(argsStart);
|
||||
|
||||
// Simple split with quote handling
|
||||
std::wstring current;
|
||||
bool inQuotes = false;
|
||||
|
||||
for (wchar_t ch : args)
|
||||
{
|
||||
if (ch == L'"')
|
||||
{
|
||||
inQuotes = !inQuotes;
|
||||
}
|
||||
else if ((ch == L' ' || ch == L'\t') && !inQuotes)
|
||||
{
|
||||
if (!current.empty())
|
||||
{
|
||||
filePaths.push_back(current);
|
||||
current.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current += ch;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last argument if any
|
||||
if (!current.empty())
|
||||
{
|
||||
filePaths.push_back(current);
|
||||
}
|
||||
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
@@ -69,131 +126,147 @@ void App::OnLaunched(LaunchActivatedEventArgs const&)
|
||||
}
|
||||
|
||||
auto args = std::wstring{ GetCommandLine() };
|
||||
size_t pos{ args.rfind(L"\\\\.\\pipe\\") };
|
||||
|
||||
std::wstring pipe_name;
|
||||
if (pos != std::wstring::npos)
|
||||
// Try to parse command line arguments first
|
||||
std::vector<std::wstring> cmdLineFiles = ParseCommandLineArgs(args);
|
||||
|
||||
if (!cmdLineFiles.empty())
|
||||
{
|
||||
pipe_name = args.substr(pos);
|
||||
}
|
||||
|
||||
HANDLE hStdin;
|
||||
|
||||
if (pipe_name.size() > 0)
|
||||
{
|
||||
while (1)
|
||||
// Use command line arguments for UI testing
|
||||
for (const auto& filePath : cmdLineFiles)
|
||||
{
|
||||
hStdin = CreateFile(
|
||||
pipe_name.c_str(), // pipe name
|
||||
GENERIC_READ | GENERIC_WRITE, // read and write
|
||||
0, // no sharing
|
||||
NULL, // default security attributes
|
||||
OPEN_EXISTING, // opens existing pipe
|
||||
0, // default attributes
|
||||
NULL); // no template file
|
||||
|
||||
// Break if the pipe handle is valid.
|
||||
if (hStdin != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
||||
auto error = GetLastError();
|
||||
if (error != ERROR_PIPE_BUSY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WaitNamedPipe(pipe_name.c_str(), 3))
|
||||
{
|
||||
printf("Could not open pipe: 20 second wait timed out.");
|
||||
}
|
||||
g_files.push_back(filePath);
|
||||
}
|
||||
Logger::debug(L"Starting PowerRename with {} files from command line", g_files.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
// Use original pipe/stdin logic for normal operation
|
||||
size_t pos{ args.rfind(L"\\\\.\\pipe\\") };
|
||||
|
||||
if (hStdin == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Logger::error(L"Invalid input handle.");
|
||||
ExitProcess(1);
|
||||
}
|
||||
std::wstring pipe_name;
|
||||
if (pos != std::wstring::npos)
|
||||
{
|
||||
pipe_name = args.substr(pos);
|
||||
}
|
||||
|
||||
HANDLE hStdin;
|
||||
|
||||
if (pipe_name.size() > 0)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
hStdin = CreateFile(
|
||||
pipe_name.c_str(), // pipe name
|
||||
GENERIC_READ | GENERIC_WRITE, // read and write
|
||||
0, // no sharing
|
||||
NULL, // default security attributes
|
||||
OPEN_EXISTING, // opens existing pipe
|
||||
0, // default attributes
|
||||
NULL); // no template file
|
||||
|
||||
// Break if the pipe handle is valid.
|
||||
if (hStdin != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
||||
auto error = GetLastError();
|
||||
if (error != ERROR_PIPE_BUSY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WaitNamedPipe(pipe_name.c_str(), 3))
|
||||
{
|
||||
printf("Could not open pipe: 20 second wait timed out.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
|
||||
if (hStdin == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Logger::error(L"Invalid input handle.");
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_BENCHMARK_100K_ENTRIES
|
||||
const std::wstring_view ROOT_PATH = L"R:\\PowerRenameBenchmark";
|
||||
const std::wstring_view ROOT_PATH = L"R:\\PowerRenameBenchmark";
|
||||
|
||||
std::wstring subdirectory_name = L"0";
|
||||
std::error_code _;
|
||||
std::wstring subdirectory_name = L"0";
|
||||
std::error_code _;
|
||||
|
||||
#if 1
|
||||
constexpr bool recreate_files = true;
|
||||
constexpr bool recreate_files = true;
|
||||
#else
|
||||
constexpr bool recreate_files = false;
|
||||
constexpr bool recreate_files = false;
|
||||
#endif
|
||||
if constexpr (recreate_files)
|
||||
fs::remove_all(ROOT_PATH, _);
|
||||
|
||||
g_files.push_back(fs::path{ ROOT_PATH });
|
||||
constexpr int pow2_threshold = 10;
|
||||
constexpr int num_files = 100'000;
|
||||
for (int i = 0; i < num_files; ++i)
|
||||
{
|
||||
fs::path file_path{ ROOT_PATH };
|
||||
// Create a subdirectory for each subsequent 2^pow2_threshold files, o/w filesystem becomes too slow to create them in a reasonable time.
|
||||
if ((i & ((1 << pow2_threshold) - 1)) == 0)
|
||||
{
|
||||
subdirectory_name = std::to_wstring(i >> pow2_threshold);
|
||||
}
|
||||
|
||||
file_path /= subdirectory_name;
|
||||
|
||||
if constexpr (recreate_files)
|
||||
fs::remove_all(ROOT_PATH, _);
|
||||
|
||||
g_files.push_back(fs::path{ ROOT_PATH });
|
||||
constexpr int pow2_threshold = 10;
|
||||
constexpr int num_files = 100'000;
|
||||
for (int i = 0; i < num_files; ++i)
|
||||
{
|
||||
fs::create_directories(file_path, _);
|
||||
file_path /= std::to_wstring(i) + L".txt";
|
||||
HANDLE hFile = CreateFileW(
|
||||
file_path.c_str(),
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
nullptr,
|
||||
CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
CloseHandle(hFile);
|
||||
fs::path file_path{ ROOT_PATH };
|
||||
// Create a subdirectory for each subsequent 2^pow2_threshold files, o/w filesystem becomes too slow to create them in a reasonable time.
|
||||
if ((i & ((1 << pow2_threshold) - 1)) == 0)
|
||||
{
|
||||
subdirectory_name = std::to_wstring(i >> pow2_threshold);
|
||||
}
|
||||
|
||||
file_path /= subdirectory_name;
|
||||
|
||||
if constexpr (recreate_files)
|
||||
{
|
||||
fs::create_directories(file_path, _);
|
||||
file_path /= std::to_wstring(i) + L".txt";
|
||||
HANDLE hFile = CreateFileW(
|
||||
file_path.c_str(),
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
nullptr,
|
||||
CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define BUFSIZE 4096 * 4
|
||||
|
||||
BOOL bSuccess;
|
||||
WCHAR chBuf[BUFSIZE];
|
||||
DWORD dwRead;
|
||||
for (;;)
|
||||
{
|
||||
// Read from standard input and stop on error or no data.
|
||||
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE * sizeof(wchar_t), &dwRead, NULL);
|
||||
|
||||
if (!bSuccess || dwRead == 0)
|
||||
break;
|
||||
|
||||
std::wstring inputBatch{ chBuf, dwRead / sizeof(wchar_t) };
|
||||
|
||||
std::wstringstream ss(inputBatch);
|
||||
std::wstring item;
|
||||
wchar_t delimiter = '?';
|
||||
while (std::getline(ss, item, delimiter))
|
||||
BOOL bSuccess;
|
||||
WCHAR chBuf[BUFSIZE];
|
||||
DWORD dwRead;
|
||||
for (;;)
|
||||
{
|
||||
g_files.push_back(item);
|
||||
// Read from standard input and stop on error or no data.
|
||||
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE * sizeof(wchar_t), &dwRead, NULL);
|
||||
|
||||
if (!bSuccess || dwRead == 0)
|
||||
break;
|
||||
|
||||
std::wstring inputBatch{ chBuf, dwRead / sizeof(wchar_t) };
|
||||
|
||||
std::wstringstream ss(inputBatch);
|
||||
std::wstring item;
|
||||
wchar_t delimiter = '?';
|
||||
while (std::getline(ss, item, delimiter))
|
||||
{
|
||||
g_files.push_back(item);
|
||||
}
|
||||
|
||||
if (!bSuccess)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bSuccess)
|
||||
break;
|
||||
}
|
||||
CloseHandle(hStdin);
|
||||
CloseHandle(hStdin);
|
||||
#endif
|
||||
|
||||
Logger::debug(L"Starting PowerRename with {} files selected", g_files.size());
|
||||
Logger::debug(L"Starting PowerRename with {} files from pipe/stdin", g_files.size());
|
||||
}
|
||||
|
||||
window = make<MainWindow>();
|
||||
window.Activate();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Text;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerRename.UITests;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BasicRenameTests"/> class.
|
||||
/// Initialize PowerRename UITest with custom test file paths
|
||||
/// </summary>
|
||||
/// <param name="testFilePaths">Array of file/folder paths to test with</param>
|
||||
[TestClass]
|
||||
public class BasicRenameTests : PowerRenameUITestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BasicRenameTests"/> class.
|
||||
/// Initialize PowerRename UITest with default test files
|
||||
/// </summary>
|
||||
public BasicRenameTests()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BasicInput()
|
||||
{
|
||||
this.SetSearchBoxText("search");
|
||||
this.SetReplaceBoxText("replace");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BasicMatchFileName()
|
||||
{
|
||||
this.SetSearchBoxText("testCase1");
|
||||
this.SetReplaceBoxText("replaced");
|
||||
|
||||
Assert.IsTrue(this.Find<TextBlock>("replaced.txt").Text == "replaced.txt");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BasicRegularMatch()
|
||||
{
|
||||
this.SetSearchBoxText("^test.*\\.txt$");
|
||||
this.SetReplaceBoxText("matched.txt");
|
||||
|
||||
CheckOriginalOrRenamedCount(0);
|
||||
|
||||
this.SetRegularExpressionCheckbox(true);
|
||||
|
||||
CheckOriginalOrRenamedCount(2);
|
||||
|
||||
Assert.IsTrue(this.Find<TextBlock>("matched.txt").Text == "matched.txt");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BasicMatchAllOccurrences()
|
||||
{
|
||||
this.SetSearchBoxText("t");
|
||||
this.SetReplaceBoxText("f");
|
||||
|
||||
this.SetMatchAllOccurrencesCheckbox(true);
|
||||
|
||||
Assert.IsTrue(this.Find<TextBlock>("fesfCase2.fxf").Text == "fesfCase2.fxf");
|
||||
Assert.IsTrue(this.Find<TextBlock>("fesfCase1.fxf").Text == "fesfCase1.fxf");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BasicCaseSensitive()
|
||||
{
|
||||
this.SetSearchBoxText("testcase1");
|
||||
this.SetReplaceBoxText("match1");
|
||||
|
||||
CheckOriginalOrRenamedCount(1);
|
||||
Assert.IsTrue(this.Find<TextBlock>("match1.txt").Text == "match1.txt");
|
||||
|
||||
this.SetCaseSensitiveCheckbox(true);
|
||||
CheckOriginalOrRenamedCount(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>PowerRename.UITests</RootNamespace>
|
||||
<AssemblyName>PowerRename.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\PowerRename.UITests\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\codeAnalysis\GlobalSuppressions.cs" Link="GlobalSuppressions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="BasicRenameTests.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<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>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Copy testItems folder and all contents to output directory -->
|
||||
<Content Include="testItems\**\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.PowerToys.UITest;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace PowerRename.UITests;
|
||||
|
||||
[TestClass]
|
||||
public class PowerRenameUITestBase : UITestBase
|
||||
{
|
||||
private static readonly string[] OriginalTestFilePaths = new string[]
|
||||
{
|
||||
Path.Combine("testItems", "folder1"), // Test folder
|
||||
Path.Combine("testItems", "folder2"), // Test folder
|
||||
Path.Combine("testItems", "testCase1.txt"), // Test file
|
||||
};
|
||||
|
||||
private static readonly string BaseTestFileFolderPath = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", "test", typeof(BasicRenameTests).Name);
|
||||
|
||||
private static List<string> TestFilesAndFoldersArray { get; } = InitCleanTestEnvironment();
|
||||
|
||||
private static List<string> InitCleanTestEnvironment()
|
||||
{
|
||||
var testFilesAndFolders = new List<string>
|
||||
{
|
||||
};
|
||||
|
||||
foreach (var files in OriginalTestFilePaths)
|
||||
{
|
||||
var targetFolder = Path.Combine(BaseTestFileFolderPath, files);
|
||||
testFilesAndFolders.Add(targetFolder);
|
||||
}
|
||||
|
||||
return testFilesAndFolders;
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void InitTestCase()
|
||||
{
|
||||
// Clean up any existing test directories for this test class
|
||||
CleanupTestDirectories();
|
||||
|
||||
// copy files and folders from OriginalTestFilePaths to testFilesAndFoldersArray
|
||||
CopyTestFilesToDestination();
|
||||
|
||||
RestartScopeExe();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PowerRenameUITestBase"/> class.
|
||||
/// Initialize PowerRename UITest with default test files
|
||||
/// </summary>
|
||||
public PowerRenameUITestBase()
|
||||
: base(PowerToysModule.PowerRename, WindowSize.UnSpecified, TestFilesAndFoldersArray.ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any existing test directories for the specified test class
|
||||
/// </summary>
|
||||
private static void CleanupTestDirectories()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(BaseTestFileFolderPath))
|
||||
{
|
||||
Directory.Delete(BaseTestFileFolderPath, true);
|
||||
Console.WriteLine($"Cleaned up old test directory: {BaseTestFileFolderPath}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Warning: Error during cleanup: {ex.Message}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(BaseTestFileFolderPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Warning: Error during cleanup create folder: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy test files and folders from source paths to destination paths
|
||||
/// </summary>
|
||||
private static void CopyTestFilesToDestination()
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < OriginalTestFilePaths.Length && i < TestFilesAndFoldersArray.Count; i++)
|
||||
{
|
||||
var sourcePath = Path.GetFullPath(OriginalTestFilePaths[i]);
|
||||
var destinationPath = TestFilesAndFoldersArray[i];
|
||||
|
||||
var destinationDir = Path.GetDirectoryName(destinationPath);
|
||||
if (destinationDir != null && !Directory.Exists(destinationDir))
|
||||
{
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
}
|
||||
|
||||
if (Directory.Exists(sourcePath))
|
||||
{
|
||||
CopyDirectory(sourcePath, destinationPath);
|
||||
Console.WriteLine($"Copied directory from {sourcePath} to {destinationPath}");
|
||||
}
|
||||
else if (File.Exists(sourcePath))
|
||||
{
|
||||
File.Copy(sourcePath, destinationPath, true);
|
||||
Console.WriteLine($"Copied file from {sourcePath} to {destinationPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Warning: Source path does not exist: {sourcePath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error during file copy operation: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively copy a directory and its contents
|
||||
/// </summary>
|
||||
/// <param name="sourceDir">Source directory path</param>
|
||||
/// <param name="destDir">Destination directory path</param>
|
||||
private static void CopyDirectory(string sourceDir, string destDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create target directory
|
||||
if (!Directory.Exists(destDir))
|
||||
{
|
||||
Directory.CreateDirectory(destDir);
|
||||
}
|
||||
|
||||
// Copy all files
|
||||
foreach (var file in Directory.GetFiles(sourceDir))
|
||||
{
|
||||
var fileName = Path.GetFileName(file);
|
||||
var destFile = Path.Combine(destDir, fileName);
|
||||
File.Copy(file, destFile, true);
|
||||
}
|
||||
|
||||
// Recursively copy all subdirectories
|
||||
foreach (var dir in Directory.GetDirectories(sourceDir))
|
||||
{
|
||||
var dirName = Path.GetFileName(dir);
|
||||
var destSubDir = Path.Combine(destDir, dirName);
|
||||
CopyDirectory(dir, destSubDir);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error copying directory from {sourceDir} to {destDir}: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetSearchBoxText(string text)
|
||||
{
|
||||
Assert.IsTrue(this.Find<TextBox>("Search for").SetText(text, true).Text == text);
|
||||
}
|
||||
|
||||
protected void SetReplaceBoxText(string text)
|
||||
{
|
||||
Assert.IsTrue(this.Find<TextBox>("Replace with").SetText(text, true).Text == text);
|
||||
}
|
||||
|
||||
protected void SetRegularExpressionCheckbox(bool flag)
|
||||
{
|
||||
Assert.IsTrue(this.Find<CheckBox>("Use regular expressions").SetCheck(flag).IsChecked == flag);
|
||||
}
|
||||
|
||||
protected void SetMatchAllOccurrencesCheckbox(bool flag)
|
||||
{
|
||||
Assert.IsTrue(this.Find<CheckBox>("Match all occurrences").SetCheck(flag).IsChecked == flag);
|
||||
}
|
||||
|
||||
protected void SetCaseSensitiveCheckbox(bool flag)
|
||||
{
|
||||
Assert.IsTrue(this.Find<CheckBox>("Case sensitive").SetCheck(flag).IsChecked == flag);
|
||||
}
|
||||
|
||||
protected void CheckOriginalOrRenamedCount(int count)
|
||||
{
|
||||
Assert.IsTrue(this.Find<TextBlock>($"({count})").Text == $"({count})");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user