Skip to content

Commit

Permalink
Add functionality to show and hide windows basing on their process +s…
Browse files Browse the repository at this point in the history
…emver: feature

* Add functionality to show and hide windows basing on their process:
- Implemented ShowWindow process extension
- Added Process property to IForm
- Added localized logging and a test for this functionality
- Hide WinAppDriver window if it was started programmatically
  • Loading branch information
mialeska authored Oct 2, 2020
1 parent 9e14623 commit 85e7af0
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Aquality.WinAppDriver.Applications;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Aquality.WinAppDriver.Extensions
{
public static class ProcessExtensions
{
/// <summary>
/// Sends ShowCommand to the window associated with the specified proccess.
/// </summary>
/// <param name="process">Proccess to send the command.</param>
/// <param name="showCommand">A command that controls how the window is to be shown.</param>
public static void ShowWindow(this Process process, ShowCommand showCommand)
{
AqualityServices.LocalizedLogger.Info("loc.process.runcommand", process.ProcessName, $"{nameof(ShowWindow)}: {showCommand}");
var intPtr = process.MainWindowHandle.ToInt32();
ShowWindow(intPtr, Convert.ToInt32(showCommand));
}

[DllImport("User32")]
private static extern int ShowWindow(int hwnd, int nCmdShow);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
namespace Aquality.WinAppDriver.Extensions
{
/// <summary>
/// Controls how the window is to be shown.
/// This parameter is ignored the first time an application calls ShowWindow,
/// if the program that launched the application provides a STARTUPINFO structure.
/// Otherwise, the first time ShowWindow is called,
/// the value should be the value obtained by the WinMain function in its nCmdShow parameter.
/// For more information, please visit https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow.
/// </summary>
public enum ShowCommand
{
/// <summary>
/// Hides the window and activates another window.
/// </summary>
Hide = 0,
/// <summary>
/// Activates and displays a window.
/// If the window is minimized or maximized, the system restores it to its original size and position.
/// An application should specify this flag when displaying the window for the first time.
/// </summary>
ShowNormal = 1,
/// <summary>
/// Activates the window and displays it as a minimized window.
/// </summary>
ShowMinimized = 2,
/// <summary>
/// Activates the window and displays it as a maximized window.
/// </summary>
ShowMaximized = 3,
/// <summary>
/// Displays a window in its most recent size and position.
/// This value is similar to <see cref="ShowNormal"/>, except that the window is not activated.
/// </summary>
ShowNoActivate = 4,
/// <summary>
/// Activates the window and displays it in its current size and position.
/// </summary>
Show = 5,
/// <summary>
/// Minimizes the specified window and activates the next top-level window in the Z order.
/// </summary>
Minimize = 6,
/// <summary>
/// Displays the window as a minimized window.
/// This value is similar to <see cref="ShowMinimized"/>, except the window is not activated.
/// </summary>
ShowMinimizedNoActive = 7,
/// <summary>
/// Displays the window in its current size and position.
/// This value is similar to <see cref="Show"/>, except that the window is not activated.
/// </summary>
ShowNoActive = 8,
/// <summary>
/// Activates and displays the window.
/// If the window is minimized or maximized, the system restores it to its original size and position.
/// An application should specify this flag when restoring a minimized window.
/// </summary>
Restore = 9,
/// <summary>
/// Sets the show state based on the SW_ value specified in the STARTUPINFO structure
/// passed to the CreateProcess function by the program that started the application.
/// </summary>
ShowDefault = 10,
/// <summary>
/// Minimizes a window, even if the thread that owns the window is not responding.
/// This flag should only be used when minimizing windows from a different thread.
/// </summary>
ForceMinimize = 11
}
}
3 changes: 3 additions & 0 deletions Aquality.WinAppDriver/src/Aquality.WinAppDriver/Forms/Form.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using IElement = Aquality.Selenium.Core.Elements.Interfaces.IElement;
using ElementFactory = Aquality.WinAppDriver.Elements.ElementFactory;
using OpenQA.Selenium.Appium.Windows;
using System.Diagnostics;

namespace Aquality.WinAppDriver.Forms
{
Expand All @@ -32,6 +33,8 @@ protected Form(By locator, string name, IForm parentForm = null, Func<WindowsDri
RelativeElementFactory = new ElementFactory(ConditionalWait, relativeFinderFromForm, LocalizationManager);
}

public Process Process => Process.GetProcessById(Convert.ToInt32(GetAttribute("ProcessId")));

private static Func<ISearchContext> ResolveSearchContextSupplier(IForm parentForm)
{
return parentForm == null
Expand Down
10 changes: 9 additions & 1 deletion Aquality.WinAppDriver/src/Aquality.WinAppDriver/Forms/IForm.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Drawing;
using System.Diagnostics;
using System.Drawing;
using Aquality.WinAppDriver.Elements.Interfaces;

namespace Aquality.WinAppDriver.Forms
Expand All @@ -12,5 +13,12 @@ public interface IForm : IElement
/// Gets size of the form element defined by its locator.
/// </summary>
Size Size { get; }

/// <summary>
/// Returns the process defined by ProcessId of the current form.
/// <seealso cref="Extensions.ProcessExtensions"/>
/// <seealso cref="Applications.AqualityServices.ProcessManager"/>
/// </summary>
Process Process { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected Window(By locator, string name, WindowsDriverSupplier customSessionSup
: base(locator, name, customSessionSupplier: ResolveWindowsSessionSupplier(customSessionSupplier))
{
}

private static WindowsDriverSupplier ResolveWindowsSessionSupplier(WindowsDriverSupplier customSessionSupplier)
{
return customSessionSupplier ?? (() => AqualityServices.Application.RootSession);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"loc.mouse.movetoelement.byoffset": "Наводзім курсор мышы на элемент са змяшчэннем на '{0}' пунктаў па вертыкалі і '{1}' пунктаў па гарызанталі",
"loc.mouse.movetoelement.byoffset.withorigin": "Наводзім курсор мышы на элемент са змяшчэннем на '{0}' пунктаў па вертыкалі і '{1}' пунктаў па гарызанталі, пачынаючы ад '{2}'",
"loc.mouse.scrollbyoffset": "Пракручваем кольца мышы на '{0}' пунктаў па вертыкалі і '{1}' пунктаў па гарызанталі",
"loc.processes.stop": "Спрабуем спыніць працэсы з іменем '{0}'",
"loc.processes.notfound": "Не знайшлі працэсаў з іменем '{0}'",
"loc.processes.start": "Запускаем працэс '{0}'"
"loc.processes.stop": "Спрабуем спыніць працэсы з імем '{0}'",
"loc.processes.notfound": "Не знайшлі працэсаў з імем '{0}'",
"loc.processes.start": "Запускаем працэс '{0}'",
"loc.process.runcommand": "Працэс з імем '{0}' :: Выконваем каманду '{1}'"
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
"loc.mouse.scrollbyoffset": "Scroll mouse wheel by '{0}' vertical and '{1}' horizontal offset",
"loc.processes.stop": "Attempting to stop all processes by name '{0}'",
"loc.processes.notfound": "No process was found by name '{0}'",
"loc.processes.start": "Starting a process '{0}'"
"loc.processes.start": "Starting a process '{0}'",
"loc.process.runcommand": "Process with the name '{0}' :: Running command '{1}'"
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
"loc.mouse.scrollbyoffset": "Прокручиваем колёсико мыши на '{0}' пунктов по вертикали и '{1}' пунктов по горизонтали",
"loc.processes.stop": "Делаем попытку остановить все процессы с именем '{0}'",
"loc.processes.notfound": "Не нашли процессов с именем '{0}'",
"loc.processes.start": "Запускаем процесс '{0}'"
"loc.processes.start": "Запускаем процесс '{0}'",
"loc.process.runcommand": "Процесс с именем '{0}' :: Исполняем команду '{1}'"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Aquality.WinAppDriver.Extensions;
using System;
using System.Diagnostics;
using System.IO;

Expand All @@ -23,6 +24,7 @@ public Process StartWinAppDriverIfRequired()
{
var exePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), FolderName, ExecutableName);
result = processManager.Start(exePath);
result.ShowWindow(ShowCommand.Minimize);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,71 @@
using Aquality.WinAppDriver.Applications;
using Aquality.WinAppDriver.Extensions;
using NUnit.Framework;
using System;
using System.IO;

namespace Aquality.WinAppDriver.Tests.Forms.Chrome
{
public class MultipleWindowsTest : TestWithCustomApplication
{
protected override string ApplicationPath => @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
private static readonly string ProgramFiles = Environment.ExpandEnvironmentVariables("%ProgramW6432%");
private static readonly string ProgramFilesX86 = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%");
private const string AppPathRelativeFromProgramFiles = @"Google\Chrome\Application\chrome.exe";
protected override string ApplicationPath => File.Exists(Path.Combine(ProgramFilesX86, AppPathRelativeFromProgramFiles))
? Path.Combine(ProgramFilesX86, AppPathRelativeFromProgramFiles)
: Path.Combine(ProgramFiles, AppPathRelativeFromProgramFiles);

private string NewTabName => $"New Tab{TabNamePostfix}";
private string DownloadsTabName => $"Downloads{TabNamePostfix}";

private const string TabNamePostfix = " - Google Chrome";

[Test]
public void Should_BePossibleTo_WorkWithTwoWindows()
public void Should_BePossibleTo_CloseOneOfWindows()
{
ProcessManager.Start(ApplicationPath);
OpenTwoWindows(out var firstWindow, out var secondWindow);

secondWindow.Click();
secondWindow.Close();
secondWindow.State.WaitForNotDisplayed();
Assert.IsFalse(secondWindow.State.IsDisplayed, "Second window is not closed");
Assert.IsTrue(firstWindow.State.IsDisplayed, "First window is closed but should not");
}

[Test]
public void Should_BePossibleTo_ShowAndHideWindow_ViaProcess()
{
OpenTwoWindows(out var firstWindow, out var secondWindow);

var secondWindowProcess = secondWindow.Process;
secondWindowProcess.ShowWindow(ShowCommand.Minimize);
secondWindow.State.WaitForNotDisplayed();
Assert.IsFalse(secondWindow.State.IsDisplayed, "Second window is not minimized");
Assert.IsTrue(firstWindow.State.IsDisplayed, "First window is not displayed");
secondWindowProcess.ShowWindow(ShowCommand.ShowNormal);
secondWindow.State.WaitForDisplayed();
Assert.IsTrue(secondWindow.State.IsDisplayed, "Second window is not shown");
}

private void OpenTwoWindows(out ChromeWindow firstWindow, out ChromeWindow secondWindow)
{
var appProcess = ProcessManager.Start(ApplicationPath);

AqualityServices.SetWindowHandleApplicationFactory(rootSession => new CoreChromeWindow(rootSession).NativeWindowHandle);
var navigationPanel = new ChromeNavigationPanel();
Assert.IsTrue(navigationPanel.State.WaitForDisplayed());
var firstWindowName = AqualityServices.Application.Driver.Title;
var firstWindow = new ChromeWindow(firstWindowName);
firstWindow = new ChromeWindow(firstWindowName);
Assert.IsTrue(firstWindow.State.WaitForDisplayed(), $"{firstWindow.Name} window is not displayed");

firstWindow.Click();
navigationPanel.OpenDownloads();
firstWindow = new ChromeWindow(DownloadsTabName);
Assert.IsTrue(firstWindow.State.WaitForDisplayed(), $"First window is not displayed with the new name {firstWindow.Name}");

navigationPanel.OpenNewWindow();
var secondWindow = new ChromeWindow(NewTabName);
secondWindow = new ChromeWindow(NewTabName);
Assert.IsTrue(secondWindow.State.WaitForDisplayed(), $"Second window with the name {secondWindow.Name} is not displayed");

secondWindow.Click();
secondWindow.Close();
secondWindow.State.WaitForNotDisplayed();
Assert.IsFalse(secondWindow.State.IsDisplayed, "Second window is not closed");
Assert.IsTrue(firstWindow.State.IsDisplayed, "First window is closed but should not");
}

[TearDown]
Expand Down

0 comments on commit 85e7af0

Please sign in to comment.