Skip to content

Commit

Permalink
Limited window feature
Browse files Browse the repository at this point in the history
Added getting element rect, getting window rect, setting window rect,
getting title.
  • Loading branch information
aristotelos committed Nov 24, 2023
1 parent 7dbe9ae commit b1f61c1
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 50 deletions.
1 change: 1 addition & 0 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ Task("Run-UI-Tests")
if (AppVeyor.IsRunningOnAppVeyor) {
AppVeyor.UploadTestResults(resultFile, AppVeyorTestResultsType.NUnit3);
}
Information("Finished Publishing WebDriver Results");
});

Task("Run-Tests")
Expand Down
17 changes: 17 additions & 0 deletions src/FlaUI.WebDriver.UITests/ElementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,23 @@ public void Click_Default_IsSupported()
Assert.That(element.Text, Is.EqualTo("Invoked!"));
}

[Test]
public void GetElementRect_Default_IsSupported()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);
var element = driver.FindElement(ExtendedBy.AccessibilityId("InvokableButton"));

var location = element.Location;
var size = element.Size;

var windowLocation = driver.Manage().Window.Position;
Assert.That(location.X, Is.EqualTo(windowLocation.X + 11));
Assert.That(location.Y, Is.EqualTo(windowLocation.Y + 324));
Assert.That(size.Width, Is.EqualTo(607));
Assert.That(size.Height, Is.EqualTo(20));
}

[Test]
public void ActiveElement_Default_IsSupported()
{
Expand Down
21 changes: 21 additions & 0 deletions src/FlaUI.WebDriver.UITests/SessionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using FlaUI.WebDriver.UITests.TestUtil;
using NUnit.Framework;
using OpenQA.Selenium.Remote;

namespace FlaUI.WebDriver.UITests
{
[TestFixture]
public class SessionTests
{
[Test]
public void GetTitle_Default_IsSupported()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);

var title = driver.Title;

Assert.That(title, Is.EqualTo("FlaUI WPF Test App"));
}
}
}
5 changes: 0 additions & 5 deletions src/FlaUI.WebDriver.UITests/TestUtil/ExtendedBy.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using OpenQA.Selenium;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FlaUI.WebDriver.UITests.TestUtil
{
Expand Down
88 changes: 83 additions & 5 deletions src/FlaUI.WebDriver.UITests/WindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,73 @@ namespace FlaUI.WebDriver.UITests
public class WindowTests
{
[Test]
public void GetWindowHandle_Default_ReturnsStableValue()
public void GetWindowRect_Default_IsSupported()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);

var position = driver.Manage().Window.Position;
var size = driver.Manage().Window.Size;

Assert.That(position.X, Is.GreaterThanOrEqualTo(0));
Assert.That(position.Y, Is.GreaterThanOrEqualTo(0));
Assert.That(size.Width, Is.EqualTo(629));
Assert.That(size.Height, Is.EqualTo(516));
}

[Test]
public void SetWindowRect_Position_IsSupported()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);

driver.Manage().Window.Position = new System.Drawing.Point(100, 100);

var newPosition = driver.Manage().Window.Position;
Assert.That(newPosition.X, Is.EqualTo(100));
Assert.That(newPosition.Y, Is.EqualTo(100));
}

[Test]
public void SetWindowRect_Size_IsSupported()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);

driver.Manage().Window.Size = new System.Drawing.Size(650, 650);

var newSize = driver.Manage().Window.Size;
Assert.That(newSize.Width, Is.EqualTo(650));
Assert.That(newSize.Height, Is.EqualTo(650));
}

[Test]
public void GetWindowHandle_AppOpensNewWindow_DoesNotSwitchToNewWindow()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);
var initialWindowHandle = driver.CurrentWindowHandle;
OpenAnotherWindow(driver);
driver.Close();

var windowHandleAfterOpenCloseOtherWindow = driver.CurrentWindowHandle;

Assert.That(windowHandleAfterOpenCloseOtherWindow, Is.EqualTo(initialWindowHandle));
}

[Test]
[Test, Ignore("https://github.com/FlaUI/FlaUI/issues/596")]
public void GetWindowHandle_WindowClosed_ReturnsNoSuchWindow()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);
OpenAndSwitchToNewWindow(driver);
driver.Close();

var getWindowHandle = () => driver.CurrentWindowHandle;

Assert.That(getWindowHandle, Throws.TypeOf<NoSuchWindowException>());
}

[Test, Ignore("https://github.com/FlaUI/FlaUI/issues/596")]
public void GetWindowHandles_Default_ReturnsUniqueHandlePerWindow()
{
var driverOptions = FlaUIDriverOptions.TestApp();
Expand All @@ -38,7 +91,7 @@ public void GetWindowHandles_Default_ReturnsUniqueHandlePerWindow()
}

[Test]
public void Close_Default_ClosesTopMostWindow()
public void Close_Default_DoesNotChangeWindowHandle()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);
Expand All @@ -60,7 +113,8 @@ public void Close_LastWindow_EndsSession()
driver.Close();

var currentWindowHandle = () => driver.CurrentWindowHandle;
Assert.That(currentWindowHandle, Throws.Exception.TypeOf<WebDriverException>()); }
Assert.That(currentWindowHandle, Throws.Exception.TypeOf<WebDriverException>());
}

[Test]
public void SwitchWindow_Default_SwitchesToWindow()
Expand All @@ -76,6 +130,30 @@ public void SwitchWindow_Default_SwitchesToWindow()
Assert.That(driver.CurrentWindowHandle, Is.EqualTo(newWindowHandle));
}

[Test]
public void SwitchWindow_Default_MovesWindowToForeground()
{
var driverOptions = FlaUIDriverOptions.TestApp();
using var driver = new RemoteWebDriver(WebDriverFixture.WebDriverUrl, driverOptions);
var initialWindowHandle = driver.CurrentWindowHandle;
OpenAnotherWindow(driver);

driver.SwitchTo().Window(initialWindowHandle);

// We assert that it is in the foreground by checking if a button can be clicked without an error
var element = driver.FindElement(ExtendedBy.AccessibilityId("InvokableButton"));
element.Click();
Assert.That(element.Text, Is.EqualTo("Invoked!"));
}

private static void OpenAndSwitchToNewWindow(RemoteWebDriver driver)
{
var initialWindowHandle = driver.CurrentWindowHandle;
OpenAnotherWindow(driver);
var newWindowHandle = driver.WindowHandles.Except(new[] { initialWindowHandle }).Single();
driver.SwitchTo().Window(newWindowHandle);
}

private static void OpenAnotherWindow(RemoteWebDriver driver)
{
driver.FindElement(ExtendedBy.NonCssName("_File")).Click();
Expand Down
16 changes: 16 additions & 0 deletions src/FlaUI.WebDriver/Controllers/ElementController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,22 @@ public async Task<ActionResult> ElementSendKeys([FromRoute] string sessionId, [F
return WebDriverResult.Success();
}

[HttpGet("{elementId}/rect")]
public async Task<ActionResult> GetElementRect([FromRoute] string sessionId, [FromRoute] string elementId)
{
var session = GetSession(sessionId);
var element = GetElement(session, elementId);
var elementBoundingRect = element.BoundingRectangle;
var elementRect = new ElementRect
{
X = elementBoundingRect.X,
Y = elementBoundingRect.Y,
Width = elementBoundingRect.Width,
Height = elementBoundingRect.Height
};
return await Task.FromResult(WebDriverResult.Success(elementRect));
}

private static void ScrollElementContainerIntoView(AutomationElement element)
{
element.Patterns.ScrollItem.PatternOrDefault?.ScrollIntoView();
Expand Down
8 changes: 8 additions & 0 deletions src/FlaUI.WebDriver/Controllers/SessionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ public async Task<ActionResult> DeleteSession([FromRoute] string sessionId)
return await Task.FromResult(WebDriverResult.Success());
}

[HttpGet("{sessionId}/title")]
public async Task<ActionResult> GetTitle([FromRoute] string sessionId)
{
var session = GetSession(sessionId);
var title = session.CurrentWindow.Title;
return await Task.FromResult(WebDriverResult.Success(title));
}

private Session GetSession(string sessionId)
{
var session = _sessionRepository.FindById(sessionId);
Expand Down
90 changes: 74 additions & 16 deletions src/FlaUI.WebDriver/Controllers/WindowController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FlaUI.WebDriver.Models;
using FlaUI.Core.AutomationElements;
using FlaUI.WebDriver.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
Expand Down Expand Up @@ -30,27 +31,22 @@ public async Task<ActionResult> CloseWindow([FromRoute] string sessionId)
throw WebDriverResponseException.NoWindowsOpenForSession();
}

// When closing the last window of the application, the `GetAllTopLevelWindows` function times out with an exception
// Therefore retrieve windows before closing the current one
// https://github.com/FlaUI/FlaUI/issues/596
var windowHandlesBeforeClose = GetWindowHandles(session).ToArray();

var currentWindow = session.CurrentWindow;
session.RemoveKnownWindow(currentWindow);
currentWindow.Close();

// When closing the last window of the application, the `GetAllTopLevelWindows` function times out with an exception
// Therefore wait for some time
await Wait.Until(() => session.App.HasExited, TimeSpan.FromMilliseconds(2000));

var remainingWindowHandles = GetWindowHandles(session).ToArray();
var remainingWindowHandles = windowHandlesBeforeClose.Except(new[] { session.CurrentWindowHandle } );
if (!remainingWindowHandles.Any())
{
_sessionRepository.Delete(session);
session.Dispose();
_logger.LogInformation("Closed last window of session and therefore deleted session with ID {SessionId}", sessionId);
}
else
{
var mainWindow = session.App.GetMainWindow(session.Automation);
session.CurrentWindow = mainWindow;
_logger.LogInformation("Switching back to window with title {WindowTitle} (handle {WindowHandle})", mainWindow.Title, session.CurrentWindowHandle);
}
return await Task.FromResult(WebDriverResult.Success(remainingWindowHandles));
}

Expand All @@ -66,11 +62,17 @@ public async Task<ActionResult> GetWindowHandles([FromRoute] string sessionId)
public async Task<ActionResult> GetWindowHandle([FromRoute] string sessionId)
{
var session = GetSession(sessionId);

if(session.FindKnownWindowByWindowHandle(session.CurrentWindowHandle) == null)
{
throw WebDriverResponseException.WindowNotFoundByHandle(session.CurrentWindowHandle);
}

return await Task.FromResult(WebDriverResult.Success(session.CurrentWindowHandle));
}

[HttpPost]
public async Task<ActionResult> SwitchWindow([FromRoute] string sessionId, [FromBody] SwitchWindowRequest switchWindowRequest)
public async Task<ActionResult> SwitchToWindow([FromRoute] string sessionId, [FromBody] SwitchWindowRequest switchWindowRequest)
{
var session = GetSession(sessionId);
if (session.App == null)
Expand All @@ -82,11 +84,52 @@ public async Task<ActionResult> SwitchWindow([FromRoute] string sessionId, [From
{
throw WebDriverResponseException.WindowNotFoundByHandle(switchWindowRequest.Handle);
}

session.CurrentWindow = window;
window.SetForeground();

_logger.LogInformation("Session {SessionId}: Switched to window with title {WindowTitle} (handle {WindowHandle})", sessionId, window.Title, switchWindowRequest.Handle);
return await Task.FromResult(WebDriverResult.Success());
}

[HttpGet("rect")]
public async Task<ActionResult> GetWindowRect([FromRoute] string sessionId)
{
var session = GetSession(sessionId);
return await Task.FromResult(WebDriverResult.Success(GetWindowRect(session.CurrentWindow)));
}

[HttpPost("rect")]
public async Task<ActionResult> SetWindowRect([FromRoute] string sessionId, [FromBody] WindowRect windowRect)
{
var session = GetSession(sessionId);

if(!session.CurrentWindow.Patterns.Transform.IsSupported)
{
throw WebDriverResponseException.UnsupportedOperation("Cannot transform the current window");
}

if (windowRect.Width != null && windowRect.Height != null)
{
if (!session.CurrentWindow.Patterns.Transform.Pattern.CanResize)
{
throw WebDriverResponseException.UnsupportedOperation("Cannot resize the current window");
}
session.CurrentWindow.Patterns.Transform.Pattern.Resize(windowRect.Width.Value, windowRect.Height.Value);
}

if (windowRect.X != null && windowRect.Y != null)
{
if (!session.CurrentWindow.Patterns.Transform.Pattern.CanMove)
{
throw WebDriverResponseException.UnsupportedOperation("Cannot move the current window");
}
session.CurrentWindow.Move(windowRect.X.Value, windowRect.Y.Value);
}

return await Task.FromResult(WebDriverResult.Success(GetWindowRect(session.CurrentWindow)));
}

private IEnumerable<string> GetWindowHandles(Session session)
{
if (session.App == null)
Expand All @@ -97,16 +140,31 @@ private IEnumerable<string> GetWindowHandles(Session session)
{
return Enumerable.Empty<string>();
}
if (session.App.GetMainWindow(session.Automation, TimeSpan.Zero) == null)
var mainWindow = session.App.GetMainWindow(session.Automation, TimeSpan.Zero);
if (mainWindow == null)
{
return Enumerable.Empty<string>();
}
var knownWindows = session.App.GetAllTopLevelWindows(session.Automation)
.SelectMany(topLevelWindow => topLevelWindow.ModalWindows.Prepend(topLevelWindow))

// GetAllTopLevelWindows sometimes times out, so we return only the main window and modal windows
// https://github.com/FlaUI/FlaUI/issues/596
var knownWindows = mainWindow.ModalWindows.Prepend(mainWindow)
.Select(session.GetOrAddKnownWindow);
return knownWindows.Select(knownWindows => knownWindows.WindowHandle);
}

private WindowRect GetWindowRect(Window window)
{
var boundingRectangle = window.BoundingRectangle;
return new WindowRect
{
X = boundingRectangle.X,
Y = boundingRectangle.Y,
Width = boundingRectangle.Width,
Height = boundingRectangle.Height
};
}

private Session GetSession(string sessionId)
{
var session = _sessionRepository.FindById(sessionId);
Expand Down
10 changes: 10 additions & 0 deletions src/FlaUI.WebDriver/Models/ElementRect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FlaUI.WebDriver.Models
{
public class ElementRect
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
}
10 changes: 10 additions & 0 deletions src/FlaUI.WebDriver/Models/WindowRect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FlaUI.WebDriver.Models
{
public class WindowRect
{
public int? X { get; set; }
public int? Y { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
}
}

0 comments on commit b1f61c1

Please sign in to comment.