Skip to content

Commit

Permalink
Implement window decoration tests on macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkatz6 committed May 1, 2024
1 parent 1ffe210 commit 14e8376
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 95 deletions.
1 change: 1 addition & 0 deletions samples/IntegrationTestApp/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
<TextBox Name="WindowTitleBarHeightHint" Text="-1" Watermark="In dips" />
<Button Name="ApplyWindowDecorations" Content="Apply decorations on this Window" />
<Button Name="ShowNewWindowDecorations" Content="Show new Window with decorations" />
<TextBox Name="WindowDecorationProperties" AcceptsReturn="True" />
</StackPanel>
</TabItem>

Expand Down
7 changes: 5 additions & 2 deletions samples/IntegrationTestApp/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ private void OnApplyWindowDecorations(Window window)
window.Background = Brushes.Transparent;
window.PropertyChanged += WindowOnPropertyChanged;

static void WindowOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
void WindowOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
var window = (Window)sender!;
if (e.Property == OffScreenMarginProperty || e.Property == WindowDecorationMarginProperty)
Expand All @@ -311,10 +311,13 @@ static void WindowOnPropertyChanged(object? sender, AvaloniaPropertyChangedEvent
}
}

static void AdjustOffsets(Window window)
void AdjustOffsets(Window window)
{
window.Padding = window.OffScreenMargin;
((Control)window.Content!).Margin = window.WindowDecorationMargin;

WindowDecorationProperties.Text =
$"{window.OffScreenMargin.Top * DesktopScaling} {window.WindowDecorationMargin.Top * DesktopScaling} {window.IsExtendedIntoWindowDecorations}";
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Chrome;

Expand All @@ -16,6 +17,8 @@ protected override string GetClassNameCore()
return "TitleBar";
}

protected override string? GetAutomationIdCore() => base.GetAutomationIdCore() ?? "AvaloniaTitleBar";

protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.TitleBar;
Expand Down
6 changes: 6 additions & 0 deletions tests/Avalonia.IntegrationTests.Appium/AppiumDriverEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ public static AppiumWebElement FindElementByClassName(this IFindsElement session
public static IReadOnlyList<AppiumWebElement> FindElementsByClassName(this IFindsElement session, string criteria) =>
session.FindElements(By.ClassName(criteria));

public static AppiumWebElement FindElementByTagName(this IFindsElement session, string criteria) =>
session.FindElement(By.TagName(criteria));

public static IReadOnlyList<AppiumWebElement> FindElementsByTagName(this IFindsElement session, string criteria) =>
session.FindElements(By.TagName(criteria));

public static void AddAdditionalCapability(this AppiumOptions options, string name, object value)
{
if (name == MobileCapabilityType.AutomationName)
Expand Down
82 changes: 76 additions & 6 deletions tests/Avalonia.IntegrationTests.Appium/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,76 @@
using System.Runtime.InteropServices;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions;
using Xunit;

namespace Avalonia.IntegrationTests.Appium
{
public record class WindowChrome(
public record WindowChrome(
AppiumWebElement? Close,
AppiumWebElement? Minimize,
AppiumWebElement? Maximize,
AppiumWebElement? FullScreen);
AppiumWebElement? FullScreen,
AppiumWebElement? TitleBar)
{
public bool IsAnyButtonEnabled => (TitleBar is null || TitleBar.Enabled) &&
(Close?.Enabled == true
|| Minimize?.Enabled == true
|| Maximize?.Enabled == true
|| FullScreen?.Enabled == true);

public int TitleBarHeight => TitleBar?.Size.Height ?? -1;

public int MaxButtonHeight =>
Math.Max(
Math.Max(Close?.Size.Height ?? -1, Minimize?.Size.Height ?? -1),
Math.Max(Maximize?.Size.Height ?? -1, FullScreen?.Size.Height ?? -1));
}

internal static class ElementExtensions
{
public static IReadOnlyList<AppiumWebElement> GetChildren(this AppiumWebElement element) =>
element.FindElementsByXPath("*/*");

public static WindowChrome GetChromeButtons(this AppiumWebElement window)
public static WindowChrome GetSystemChromeButtons(this AppiumWebElement window)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
if (OperatingSystem.IsMacOS())
{
var closeButton = window.FindElementsByAccessibilityId("_XCUI:CloseWindow").FirstOrDefault();
var fullscreenButton = window.FindElementsByAccessibilityId("_XCUI:FullScreenWindow").FirstOrDefault();
var minimizeButton = window.FindElementsByAccessibilityId("_XCUI:MinimizeWindow").FirstOrDefault();
var zoomButton = window.FindElementsByAccessibilityId("_XCUI:ZoomWindow").FirstOrDefault();
return new(closeButton, minimizeButton, zoomButton, fullscreenButton);
return new(closeButton, minimizeButton, zoomButton, fullscreenButton, null);
}

if (OperatingSystem.IsWindows())
{
var titlebar = window.FindElementByTagName("TitleBar");
var closeButton = titlebar.FindElementByName("Close");
var minimizeButton = titlebar.FindElementByName("Minimize");
var maximizeButton = titlebar.FindElementByName("Maximize");
return new(closeButton, minimizeButton, maximizeButton, null, titlebar);
}

throw new NotSupportedException("GetChromeButtons not supported on this platform.");
}

public static WindowChrome GetClientChromeButtons(this AppiumWebElement window)
{
try
{
var titlebar = window.FindElementByAccessibilityId("AvaloniaTitleBar");
var closeButton = titlebar.FindElementByName("Close");
var minimizeButton = titlebar.FindElementByName("Minimize");
var maximizeButton = titlebar.FindElementByName("Maximize");
return new(closeButton, minimizeButton, maximizeButton, null, titlebar);
}
catch (NoSuchElementException)
{
return new(null, null, null, null, null);
}
}

public static string GetComboBoxValue(this AppiumWebElement element)
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
Expand Down Expand Up @@ -68,6 +107,37 @@ public static bool GetIsFocused(this AppiumWebElement element)
}
}

public static AppiumWebElement GetCurrentSingleWindow(this AppiumDriver session)
{
if (OperatingSystem.IsMacOS())
{
// The Avalonia a11y tree currently exposes two nested Window elements, this is a bug and should be fixed
// but in the meantime use the `parent::' selector to return the parent "real" window.
return session.FindElementByXPath(
$"XCUIElementTypeWindow//*/parent::XCUIElementTypeWindow");
}
else
{
return session.FindElementByXPath($"//Window");
}
}

public static AppiumWebElement GetWindowById(this AppiumDriver session, string identifier)
{
if (OperatingSystem.IsMacOS())
{
// The Avalonia a11y tree currently exposes two nested Window elements, this is a bug and should be fixed
// but in the meantime use the `parent::' selector to return the parent "real" window.
return session.FindElementByXPath(
$"XCUIElementTypeWindow//*[@identifier='{identifier}']/parent::XCUIElementTypeWindow");
}
else
{
return session.FindElementByXPath($"//Window[@AutomationId='{identifier}']");
}
}


/// <summary>
/// Clicks a button which is expected to open a new window.
/// </summary>
Expand Down
Loading

0 comments on commit 14e8376

Please sign in to comment.